jbai-cli 1.6.0 → 1.8.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/README.md +116 -4
- package/bin/jbai-claude.js +58 -65
- package/bin/jbai-codex.js +97 -91
- package/bin/jbai-gemini.js +53 -60
- package/bin/jbai-opencode.js +147 -151
- package/bin/jbai-proxy.js +857 -0
- package/bin/jbai.js +60 -6
- package/bin/test-models.js +56 -3
- package/lib/config.js +107 -14
- package/lib/ensure-token.js +31 -0
- package/package.json +2 -1
package/bin/jbai-opencode.js
CHANGED
|
@@ -5,159 +5,155 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const config = require('../lib/config');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
args = args.filter(a => !superFlags.includes(a));
|
|
30
|
-
|
|
31
|
-
// Setup OpenCode config with JetBrains provider
|
|
32
|
-
const configDir = path.join(os.homedir(), '.config', 'opencode');
|
|
33
|
-
const configFile = path.join(configDir, 'opencode.json');
|
|
34
|
-
|
|
35
|
-
if (!fs.existsSync(configDir)) {
|
|
36
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Create or update OpenCode config with JetBrains provider
|
|
40
|
-
const providerName = environment === 'staging' ? 'jbai-staging' : 'jbai';
|
|
41
|
-
let opencodeConfig = {};
|
|
42
|
-
|
|
43
|
-
if (fs.existsSync(configFile)) {
|
|
44
|
-
try {
|
|
45
|
-
opencodeConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
|
|
46
|
-
} catch {
|
|
47
|
-
opencodeConfig = {};
|
|
8
|
+
const { ensureToken } = require('../lib/ensure-token');
|
|
9
|
+
|
|
10
|
+
(async () => {
|
|
11
|
+
const token = await ensureToken();
|
|
12
|
+
const endpoints = config.getEndpoints();
|
|
13
|
+
const environment = config.getEnvironment();
|
|
14
|
+
let args = process.argv.slice(2);
|
|
15
|
+
const handoffConfig = stripHandoffFlag(args);
|
|
16
|
+
args = handoffConfig.args;
|
|
17
|
+
|
|
18
|
+
// Check for super mode (--super, --yolo, -s)
|
|
19
|
+
const superFlags = ['--super', '--yolo', '-s'];
|
|
20
|
+
const superMode = args.some(a => superFlags.includes(a));
|
|
21
|
+
args = args.filter(a => !superFlags.includes(a));
|
|
22
|
+
|
|
23
|
+
// Setup OpenCode config with JetBrains provider
|
|
24
|
+
const configDir = path.join(os.homedir(), '.config', 'opencode');
|
|
25
|
+
const configFile = path.join(configDir, 'opencode.json');
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(configDir)) {
|
|
28
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
48
29
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Provider names for OpenAI and Anthropic
|
|
60
|
-
const anthropicProviderName = environment === 'staging' ? 'jbai-anthropic-staging' : 'jbai-anthropic';
|
|
61
|
-
|
|
62
|
-
// Add/update JetBrains OpenAI provider with custom header (using env var reference)
|
|
63
|
-
// Use OpenAI SDK to support max_completion_tokens for GPT-5.x
|
|
64
|
-
opencodeConfig.provider[providerName] = {
|
|
65
|
-
npm: '@ai-sdk/openai',
|
|
66
|
-
name: `JetBrains AI OpenAI (${environment})`,
|
|
67
|
-
options: {
|
|
68
|
-
baseURL: endpoints.openai,
|
|
69
|
-
apiKey: `{env:${envVarName}}`,
|
|
70
|
-
headers: {
|
|
71
|
-
'Grazie-Authenticate-JWT': `{env:${envVarName}}`
|
|
30
|
+
|
|
31
|
+
// Create or update OpenCode config with JetBrains provider
|
|
32
|
+
const providerName = environment === 'staging' ? 'jbai-staging' : 'jbai';
|
|
33
|
+
let opencodeConfig = {};
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(configFile)) {
|
|
36
|
+
try {
|
|
37
|
+
opencodeConfig = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
|
|
38
|
+
} catch {
|
|
39
|
+
opencodeConfig = {};
|
|
72
40
|
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Ensure provider section exists
|
|
44
|
+
if (!opencodeConfig.provider) {
|
|
45
|
+
opencodeConfig.provider = {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Environment variable name for the token
|
|
49
|
+
const envVarName = environment === 'staging' ? 'GRAZIE_STAGING_TOKEN' : 'GRAZIE_API_TOKEN';
|
|
50
|
+
|
|
51
|
+
// Provider names for OpenAI and Anthropic
|
|
52
|
+
const anthropicProviderName = environment === 'staging' ? 'jbai-anthropic-staging' : 'jbai-anthropic';
|
|
53
|
+
|
|
54
|
+
// Add/update JetBrains OpenAI provider with custom header (using env var reference)
|
|
55
|
+
// Use OpenAI SDK to support max_completion_tokens for GPT-5.x
|
|
56
|
+
opencodeConfig.provider[providerName] = {
|
|
57
|
+
npm: '@ai-sdk/openai',
|
|
58
|
+
name: `JetBrains AI OpenAI (${environment})`,
|
|
59
|
+
options: {
|
|
60
|
+
baseURL: endpoints.openai,
|
|
61
|
+
apiKey: `{env:${envVarName}}`,
|
|
62
|
+
headers: {
|
|
63
|
+
'Grazie-Authenticate-JWT': `{env:${envVarName}}`
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
models: {}
|
|
87
67
|
};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
68
|
+
|
|
69
|
+
// Add OpenAI models
|
|
70
|
+
// OpenCode requires an output limit in the config.
|
|
71
|
+
// For O-series models we keep a larger context window but still set output.
|
|
72
|
+
config.MODELS.openai.available.forEach(model => {
|
|
73
|
+
const isOSeries = /^o[1-9]/.test(model);
|
|
74
|
+
opencodeConfig.provider[providerName].models[model] = {
|
|
75
|
+
name: model,
|
|
76
|
+
limit: isOSeries
|
|
77
|
+
? { context: 200000, output: 8192 }
|
|
78
|
+
: { context: 128000, output: 8192 }
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Add JetBrains Anthropic provider for Claude models
|
|
83
|
+
opencodeConfig.provider[anthropicProviderName] = {
|
|
84
|
+
npm: '@ai-sdk/anthropic',
|
|
85
|
+
name: `JetBrains AI Anthropic (${environment})`,
|
|
86
|
+
options: {
|
|
87
|
+
baseURL: endpoints.anthropic,
|
|
88
|
+
apiKey: `{env:${envVarName}}`,
|
|
89
|
+
headers: {
|
|
90
|
+
'Grazie-Authenticate-JWT': `{env:${envVarName}}`
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
models: {}
|
|
109
94
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let finalArgs = [];
|
|
118
|
-
|
|
119
|
-
if (!hasModel) {
|
|
120
|
-
// Use provider/model format for OpenCode
|
|
121
|
-
finalArgs.push('--model', `${providerName}/${config.MODELS.openai.default}`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Note: OpenCode run is already non-interactive, no super mode needed
|
|
125
|
-
if (superMode) {
|
|
126
|
-
console.log('ℹ️ OpenCode run is already non-interactive (no super mode flag needed)');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
finalArgs.push(...args);
|
|
130
|
-
|
|
131
|
-
// Set the token in environment variable for OpenCode config to reference
|
|
132
|
-
const childEnv = {
|
|
133
|
-
...process.env,
|
|
134
|
-
[envVarName]: token
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const child = runWithHandoff({
|
|
138
|
-
command: 'opencode',
|
|
139
|
-
args: finalArgs,
|
|
140
|
-
env: childEnv,
|
|
141
|
-
toolName: 'jbai-opencode',
|
|
142
|
-
handoffDefaults: {
|
|
143
|
-
enabled: !handoffConfig.disabled,
|
|
144
|
-
grazieToken: token,
|
|
145
|
-
grazieEnvironment: environment === 'production' ? 'PRODUCTION' : 'STAGING',
|
|
146
|
-
grazieModel: config.MODELS.claude.default,
|
|
147
|
-
cwd: process.cwd(),
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (child && typeof child.on === 'function') {
|
|
152
|
-
child.on('error', (err) => {
|
|
153
|
-
if (err.code === 'ENOENT') {
|
|
154
|
-
const tool = config.TOOLS.opencode;
|
|
155
|
-
console.error(`❌ ${tool.name} not found.\n`);
|
|
156
|
-
console.error(`Install with: ${tool.install}`);
|
|
157
|
-
console.error(`Or run: jbai install opencode`);
|
|
158
|
-
} else {
|
|
159
|
-
console.error(`Error: ${err.message}`);
|
|
160
|
-
}
|
|
161
|
-
process.exit(1);
|
|
95
|
+
|
|
96
|
+
// Add Claude models to Anthropic provider
|
|
97
|
+
config.MODELS.claude.available.forEach(model => {
|
|
98
|
+
opencodeConfig.provider[anthropicProviderName].models[model] = {
|
|
99
|
+
name: model,
|
|
100
|
+
limit: { context: 200000, output: 8192 }
|
|
101
|
+
};
|
|
162
102
|
});
|
|
163
|
-
|
|
103
|
+
|
|
104
|
+
// NOTE: Gemini models are NOT available via Grazie OpenAI-compatible proxy.
|
|
105
|
+
// The /user/v5/llm/google/v1/vertex endpoint only works with native Google API
|
|
106
|
+
// format (used by jbai-gemini), not OpenAI chat/completions format.
|
|
107
|
+
// Use `jbai gemini` instead for Gemini models.
|
|
108
|
+
|
|
109
|
+
// Enable max permissions (yolo mode) by default
|
|
110
|
+
opencodeConfig.yolo = true;
|
|
111
|
+
|
|
112
|
+
// Write config
|
|
113
|
+
fs.writeFileSync(configFile, JSON.stringify(opencodeConfig, null, 2));
|
|
114
|
+
|
|
115
|
+
// Check if model specified
|
|
116
|
+
const hasModel = args.includes('--model') || args.includes('-m');
|
|
117
|
+
let finalArgs = ['--yolo'];
|
|
118
|
+
|
|
119
|
+
if (!hasModel) {
|
|
120
|
+
// Use provider/model format for OpenCode
|
|
121
|
+
finalArgs.push('--model', `${providerName}/${config.MODELS.openai.default}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
finalArgs.push(...args);
|
|
125
|
+
|
|
126
|
+
// Set the token in environment variable for OpenCode config to reference
|
|
127
|
+
const childEnv = {
|
|
128
|
+
...process.env,
|
|
129
|
+
[envVarName]: token
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const child = runWithHandoff({
|
|
133
|
+
command: 'opencode',
|
|
134
|
+
args: finalArgs,
|
|
135
|
+
env: childEnv,
|
|
136
|
+
toolName: 'jbai-opencode',
|
|
137
|
+
handoffDefaults: {
|
|
138
|
+
enabled: !handoffConfig.disabled,
|
|
139
|
+
grazieToken: token,
|
|
140
|
+
grazieEnvironment: environment === 'production' ? 'PRODUCTION' : 'STAGING',
|
|
141
|
+
grazieModel: config.MODELS.claude.default,
|
|
142
|
+
cwd: process.cwd(),
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (child && typeof child.on === 'function') {
|
|
147
|
+
child.on('error', (err) => {
|
|
148
|
+
if (err.code === 'ENOENT') {
|
|
149
|
+
const tool = config.TOOLS.opencode;
|
|
150
|
+
console.error(`❌ ${tool.name} not found.\n`);
|
|
151
|
+
console.error(`Install with: ${tool.install}`);
|
|
152
|
+
console.error(`Or run: jbai install opencode`);
|
|
153
|
+
} else {
|
|
154
|
+
console.error(`Error: ${err.message}`);
|
|
155
|
+
}
|
|
156
|
+
process.exit(1);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
})();
|