jbai-cli 1.4.0 → 1.5.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/bin/jbai-opencode.js +0 -15
- package/bin/jbai.js +7 -32
- package/bin/test-models.js +261 -0
- package/lib/config.js +39 -59
- package/package.json +3 -7
- package/bin/jbai-aider.js +0 -94
package/bin/jbai-opencode.js
CHANGED
|
@@ -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',
|
|
@@ -50,7 +44,7 @@ COMMANDS:
|
|
|
50
44
|
jbai test Test all API endpoints
|
|
51
45
|
jbai env [staging|production] Switch environment
|
|
52
46
|
jbai models List available models
|
|
53
|
-
jbai install Install all AI tools (claude, codex,
|
|
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-
|
|
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-
|
|
68
|
+
jbai-gemini # Start Gemini CLI
|
|
76
69
|
|
|
77
70
|
TOKEN:
|
|
78
71
|
Get token: ${config.getEndpoints().tokenUrl}
|
|
@@ -227,7 +220,7 @@ function showModels() {
|
|
|
227
220
|
console.log(` - ${m}${def}`);
|
|
228
221
|
});
|
|
229
222
|
|
|
230
|
-
console.log('\nGPT (OpenAI) - jbai-codex, jbai-
|
|
223
|
+
console.log('\nGPT (OpenAI) - jbai-codex, jbai-opencode:');
|
|
231
224
|
config.MODELS.openai.available.forEach((m) => {
|
|
232
225
|
const def = m === config.MODELS.openai.default ? ' (default)' : '';
|
|
233
226
|
console.log(` - ${m}${def}`);
|
|
@@ -239,31 +232,13 @@ function showModels() {
|
|
|
239
232
|
console.log(` - ${m}${def}`);
|
|
240
233
|
});
|
|
241
234
|
|
|
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
235
|
// Count total
|
|
258
236
|
const total = config.MODELS.claude.available.length +
|
|
259
237
|
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;
|
|
238
|
+
config.MODELS.gemini.available.length;
|
|
266
239
|
console.log(`\nTotal: ${total} models`);
|
|
240
|
+
console.log('\nNote: Other providers (DeepSeek, Mistral, Qwen, XAI, Meta) are available');
|
|
241
|
+
console.log('via Grazie native API but not via OpenAI-compatible CLI tools.');
|
|
267
242
|
}
|
|
268
243
|
|
|
269
244
|
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,76 @@ const ENDPOINTS = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// All models available from JetBrains AI Platform (Grazie)
|
|
29
|
-
// Model names
|
|
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.
|
|
35
|
-
'claude-opus-4-5-
|
|
35
|
+
// Claude 4.5 series (latest)
|
|
36
|
+
'claude-opus-4-5-20251101',
|
|
36
37
|
'claude-sonnet-4-5-20250929',
|
|
37
|
-
'claude-haiku-4-5-
|
|
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-
|
|
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
|
+
default: 'gpt-4o-2024-11-20',
|
|
50
50
|
available: [
|
|
51
|
-
// GPT-5.x series (latest)
|
|
52
|
-
'gpt-5',
|
|
53
|
-
'gpt-5.1',
|
|
51
|
+
// GPT-5.x series (latest) - require date-versioned names
|
|
52
|
+
'gpt-5.2-2025-12-11',
|
|
54
53
|
'gpt-5.2',
|
|
55
|
-
'gpt-5-
|
|
56
|
-
'gpt-5-
|
|
57
|
-
'gpt-5-
|
|
58
|
-
'gpt-5
|
|
59
|
-
'gpt-5.1-codex-max',
|
|
60
|
-
'gpt-5.1-codex-mini',
|
|
61
|
-
'gpt-5.2-codex',
|
|
62
|
-
'gpt-5.2-pro',
|
|
54
|
+
'gpt-5.1-2025-11-13',
|
|
55
|
+
'gpt-5-2025-08-07',
|
|
56
|
+
'gpt-5-mini-2025-08-07',
|
|
57
|
+
'gpt-5-nano-2025-08-07',
|
|
63
58
|
// 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
|
-
|
|
72
|
-
|
|
73
|
-
'
|
|
74
|
-
'o3-
|
|
75
|
-
'
|
|
59
|
+
'gpt-4.1-2025-04-14',
|
|
60
|
+
'gpt-4.1-mini-2025-04-14',
|
|
61
|
+
'gpt-4.1-nano-2025-04-14',
|
|
62
|
+
'gpt-4o-2024-11-20',
|
|
63
|
+
'gpt-4o-mini-2024-07-18',
|
|
64
|
+
'gpt-4-turbo-2024-04-09',
|
|
65
|
+
'gpt-4-0613',
|
|
66
|
+
'gpt-3.5-turbo-0125',
|
|
67
|
+
// O-series (reasoning) - use max_completion_tokens instead of max_tokens
|
|
68
|
+
'o4-mini-2025-04-16',
|
|
69
|
+
'o3-2025-04-16',
|
|
70
|
+
'o3-mini-2025-01-31',
|
|
71
|
+
'o1-2024-12-17'
|
|
76
72
|
]
|
|
77
73
|
},
|
|
78
74
|
gemini: {
|
|
79
75
|
default: 'gemini-2.5-flash',
|
|
80
76
|
available: [
|
|
81
|
-
// Gemini 3.x (
|
|
82
|
-
'gemini-3
|
|
83
|
-
'gemini-3
|
|
84
|
-
// Gemini 2.
|
|
77
|
+
// Gemini 3.x (preview)
|
|
78
|
+
'gemini-3-pro-preview',
|
|
79
|
+
'gemini-3-flash-preview',
|
|
80
|
+
// Gemini 2.5
|
|
85
81
|
'gemini-2.5-pro',
|
|
86
82
|
'gemini-2.5-flash',
|
|
87
83
|
'gemini-2.5-flash-lite',
|
|
88
|
-
|
|
89
|
-
'gemini-2.0-flash-
|
|
84
|
+
// Gemini 2.0
|
|
85
|
+
'gemini-2.0-flash-001',
|
|
86
|
+
'gemini-2.0-flash-lite-001'
|
|
90
87
|
]
|
|
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
88
|
}
|
|
89
|
+
// NOTE: Other providers (DeepSeek, Mistral, Qwen, XAI, Meta) are available
|
|
90
|
+
// via Grazie native Chat API but NOT via the OpenAI-compatible proxy.
|
|
91
|
+
// They are not supported by CLI tools that use OpenAI API format.
|
|
100
92
|
};
|
|
101
93
|
|
|
102
|
-
// All models for tools that support multiple providers (
|
|
94
|
+
// All models for tools that support multiple providers (OpenCode, Codex)
|
|
103
95
|
const ALL_MODELS = {
|
|
104
96
|
openai: MODELS.openai.available,
|
|
105
97
|
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
|
-
]
|
|
98
|
+
gemini: MODELS.gemini.available
|
|
114
99
|
};
|
|
115
100
|
|
|
116
101
|
function ensureConfigDir() {
|
|
@@ -190,11 +175,6 @@ const TOOLS = {
|
|
|
190
175
|
command: 'codex',
|
|
191
176
|
install: 'npm install -g @openai/codex'
|
|
192
177
|
},
|
|
193
|
-
aider: {
|
|
194
|
-
name: 'Aider',
|
|
195
|
-
command: 'aider',
|
|
196
|
-
install: 'brew install aider'
|
|
197
|
-
},
|
|
198
178
|
gemini: {
|
|
199
179
|
name: 'Gemini CLI',
|
|
200
180
|
command: 'gemini',
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jbai-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI wrappers to use AI coding tools (Claude Code, Codex,
|
|
3
|
+
"version": "1.5.0",
|
|
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));
|