@polderlabs/bizar 6.2.2 → 6.2.4
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/cli/bin.mjs +51 -1
- package/cli/commands/cline-cmd.mjs +289 -0
- package/cli/commands/rca.mjs +198 -0
- package/cli/commands/rca.test.mjs +99 -0
- package/cli/commands/setup-provider.mjs +249 -75
- package/cli/commands/setup-provider.test.mjs +198 -98
- package/cli/commands/validate.mjs +92 -0
- package/cli/commands/validate.test.mjs +37 -0
- package/config/agents/_shared/AGENT_BASELINE.md +40 -1
- package/config/agents/_shared/CLINE_TOOLS.md +398 -0
- package/config/agents/agent-browser.md +1 -1
- package/config/agents/baldr.md +1 -1
- package/config/agents/forseti.md +1 -1
- package/config/agents/frigg.md +1 -1
- package/config/agents/heimdall.md +1 -1
- package/config/agents/hermod.md +1 -1
- package/config/agents/mimir.md +1 -1
- package/config/agents/odin.md +1 -1
- package/config/agents/quick.md +1 -1
- package/config/agents/semble-search.md +1 -1
- package/config/agents/thor.md +1 -1
- package/config/agents/tyr.md +1 -1
- package/config/agents/vidarr.md +1 -1
- package/config/agents/vor.md +1 -1
- package/package.json +2 -2
- package/plugins/bizar/package.json +17 -6
- package/plugins/bizar/src/clineruntime.ts +19 -4
- package/plugins/bizar/src/options.ts +14 -7
- package/plugins/bizar/tests/clineruntime-config.test.ts +69 -7
- package/plugins/bizar/tests/options.test.ts +6 -6
- package/scripts/bh-full-e2e.mjs +79 -4
- package/scripts/check-agents.mjs +73 -0
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cli/commands/setup-provider.mjs
|
|
3
3
|
*
|
|
4
|
-
* v6.2.
|
|
4
|
+
* v6.2.3 — `bizar setup-provider` subcommand.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* v6.2.2 wrote to `~/.cline/cline.json` (the harness config). That was
|
|
7
|
+
* the wrong place — the Cline CLI and kanban mode read from
|
|
8
|
+
* `~/.cline/data/settings/providers.json` (the real Cline settings
|
|
9
|
+
* file). v6.2.3 fixes this.
|
|
10
|
+
*
|
|
11
|
+
* The default providerId is `litellm` because:
|
|
12
|
+
* - It's a real entry in Cline's built-in provider catalog (works
|
|
13
|
+
* in both Cline CLI AND kanban mode)
|
|
14
|
+
* - It's `family: "openai-compatible"` and accepts a custom
|
|
15
|
+
* baseUrl — the perfect match for the local 9Router gateway
|
|
16
|
+
* - The previous v6.2.2 default `9router` (and the user's hand-
|
|
17
|
+
* written `openai-compatible`) are NOT in Cline's catalog, so
|
|
18
|
+
* kanban mode fails with "Unknown or disabled provider".
|
|
8
19
|
*
|
|
9
20
|
* Usage:
|
|
10
|
-
* bizar setup-provider #
|
|
11
|
-
* bizar setup-provider --list # print the model catalog
|
|
21
|
+
* bizar setup-provider # default 9Router gateway, litellm provider
|
|
22
|
+
* bizar setup-provider --list # print the live model catalog
|
|
12
23
|
* bizar setup-provider --gateway <url> # override gateway URL
|
|
13
24
|
* bizar setup-provider --key <key> # provider API key
|
|
14
|
-
* bizar setup-provider --provider <name> #
|
|
25
|
+
* bizar setup-provider --provider <name> # built-in providerId (default: litellm)
|
|
26
|
+
* bizar setup-provider --discover # auto-detect a local gateway and configure
|
|
15
27
|
* bizar setup-provider --remove <name> # remove a provider
|
|
16
28
|
*
|
|
17
29
|
* The default gateway is the local 9Router at http://localhost:20128/v1.
|
|
18
|
-
* The catalog is fetched live from `${gateway}/v1/models
|
|
19
|
-
* always gets the up-to-date list.
|
|
30
|
+
* The model catalog is fetched live from `${gateway}/v1/models`.
|
|
20
31
|
*/
|
|
21
32
|
import chalk from 'chalk';
|
|
22
|
-
import { readFileSync, writeFileSync, existsSync, renameSync, copyFileSync } from 'node:fs';
|
|
33
|
+
import { readFileSync, writeFileSync, existsSync, renameSync, copyFileSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
23
34
|
import { join, dirname } from 'node:path';
|
|
24
35
|
import { homedir } from 'node:os';
|
|
25
36
|
|
|
@@ -33,10 +44,38 @@ function clineDir() {
|
|
|
33
44
|
return join(homedir(), '.cline');
|
|
34
45
|
}
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
function dataDir() {
|
|
48
|
+
if (process.env.CLINE_DATA_DIR && process.env.CLINE_DATA_DIR.trim()) {
|
|
49
|
+
return process.env.CLINE_DATA_DIR.trim();
|
|
50
|
+
}
|
|
51
|
+
return join(clineDir(), 'data');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Path to the Cline settings file the CLI + kanban actually read. */
|
|
55
|
+
function settingsPath() {
|
|
56
|
+
if (process.env.CLINE_PROVIDER_SETTINGS_PATH && process.env.CLINE_PROVIDER_SETTINGS_PATH.trim()) {
|
|
57
|
+
return process.env.CLINE_PROVIDER_SETTINGS_PATH.trim();
|
|
58
|
+
}
|
|
59
|
+
return join(dataDir(), 'settings', 'providers.json');
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
const DEFAULT_GATEWAY = 'http://localhost:20128/v1';
|
|
39
|
-
|
|
63
|
+
// v6.2.3 — `litellm` is the right built-in providerId for a custom
|
|
64
|
+
// OpenAI-compatible endpoint. It's in Cline's catalog (so kanban
|
|
65
|
+
// mode recognizes it) and accepts a custom baseUrl.
|
|
66
|
+
const DEFAULT_PROVIDER = 'litellm';
|
|
67
|
+
|
|
68
|
+
// v6.2.3 — Built-in Cline providerIds that support `family: "openai-compatible"`
|
|
69
|
+
// with a custom baseUrl. These are the ones the user can pick for
|
|
70
|
+
// "I have my own OpenAI-compatible endpoint". `9router`, `openai-compatible`
|
|
71
|
+
// and other fake IDs are NOT in this list — they fail in kanban mode.
|
|
72
|
+
export const OPENAI_COMPATIBLE_PROVIDERS = [
|
|
73
|
+
'litellm', // best fit: defaults to localhost:4000, openai-responses
|
|
74
|
+
'cline', // Cline's own API (can be overridden)
|
|
75
|
+
'ollama', // localhost:11434
|
|
76
|
+
'lmstudio', // localhost:1234
|
|
77
|
+
'huggingface', // api-inference.huggingface.co
|
|
78
|
+
];
|
|
40
79
|
|
|
41
80
|
function readJsonSafe(file) {
|
|
42
81
|
try {
|
|
@@ -48,6 +87,7 @@ function readJsonSafe(file) {
|
|
|
48
87
|
}
|
|
49
88
|
|
|
50
89
|
function writeJsonAtomic(file, data) {
|
|
90
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
51
91
|
const tmp = `${file}.tmp`;
|
|
52
92
|
writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n');
|
|
53
93
|
renameSync(tmp, file);
|
|
@@ -77,86 +117,173 @@ function looksLikeEnvVar(s) {
|
|
|
77
117
|
return /^[A-Z][A-Z0-9_]{1,}$/.test(s);
|
|
78
118
|
}
|
|
79
119
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const modelsObj = {};
|
|
88
|
-
for (const m of models) {
|
|
89
|
-
modelsObj[m] = {};
|
|
90
|
-
}
|
|
120
|
+
/**
|
|
121
|
+
* v6.2.3 — Build the Cline settings.json entry for one provider.
|
|
122
|
+
*
|
|
123
|
+
* The shape matches `~/.cline/data/settings/providers.json`:
|
|
124
|
+
* { "version": 1, "lastUsedProvider": "litellm", "providers": { "litellm": { ... } } }
|
|
125
|
+
*/
|
|
126
|
+
export function buildProviderBlock({ name, gateway, apiKey, model, reason = false }) {
|
|
91
127
|
return {
|
|
92
|
-
|
|
93
|
-
|
|
128
|
+
settings: {
|
|
129
|
+
provider: name,
|
|
130
|
+
model,
|
|
94
131
|
apiKey: looksLikeEnvVar(apiKey) ? `\${env:${apiKey}}` : apiKey,
|
|
95
|
-
|
|
132
|
+
baseUrl: gateway,
|
|
133
|
+
...(reason ? { reasoning: { enabled: true, effort: 'medium' } } : { reasoning: { enabled: false, effort: 'medium' } }),
|
|
96
134
|
},
|
|
135
|
+
updatedAt: new Date().toISOString(),
|
|
136
|
+
tokenSource: 'manual',
|
|
97
137
|
};
|
|
98
138
|
}
|
|
99
139
|
|
|
100
|
-
/** Apply
|
|
101
|
-
export function applyProviderBlock(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// Back up the original before mutating.
|
|
108
|
-
const backup = `${clineJsonPath()}.bak`;
|
|
109
|
-
try { copyFileSync(clineJsonPath(), backup); } catch { /* best-effort */ }
|
|
110
|
-
|
|
111
|
-
const cfg = readJsonSafe(clineJsonPath(), {});
|
|
112
|
-
if (!cfg.provider || typeof cfg.provider !== 'object') cfg.provider = {};
|
|
113
|
-
for (const [name, body] of Object.entries(block)) {
|
|
114
|
-
cfg.provider[name] = body;
|
|
140
|
+
/** Apply (create or update) one provider in the settings file. */
|
|
141
|
+
export function applyProviderBlock(name, body) {
|
|
142
|
+
const file = settingsPath();
|
|
143
|
+
if (!existsSync(file)) {
|
|
144
|
+
// Bootstrap with a minimal but valid providers.json
|
|
145
|
+
const bootstrap = { version: 1, providers: {}, lastUsedProvider: name };
|
|
146
|
+
writeJsonAtomic(file, bootstrap);
|
|
115
147
|
}
|
|
116
|
-
|
|
117
|
-
|
|
148
|
+
const backup = `${file}.bak`;
|
|
149
|
+
try { copyFileSync(file, backup); } catch { /* best-effort */ }
|
|
150
|
+
|
|
151
|
+
const cfg = readJsonSafe(file, { version: 1, providers: {} });
|
|
152
|
+
if (!cfg.providers || typeof cfg.providers !== 'object') cfg.providers = {};
|
|
153
|
+
cfg.providers[name] = body;
|
|
154
|
+
// v6.2.3 — Always set lastUsedProvider to the most recently applied
|
|
155
|
+
// provider. The user is actively configuring this one; they want
|
|
156
|
+
// it to be the active one. (Previous v6.2.2 only set it when missing,
|
|
157
|
+
// which left stale `lastUsedProvider: "openai-compatible"` entries
|
|
158
|
+
// in the file when the user upgraded.)
|
|
159
|
+
cfg.lastUsedProvider = name;
|
|
160
|
+
cfg.version = cfg.version || 1;
|
|
161
|
+
writeJsonAtomic(file, cfg);
|
|
162
|
+
return { name, path: file, backup };
|
|
118
163
|
}
|
|
119
164
|
|
|
120
|
-
/** Remove a provider by name. */
|
|
165
|
+
/** Remove a provider by name from the settings file. */
|
|
121
166
|
export function removeProvider(name) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!cfg.provider || !cfg.provider[name]) {
|
|
167
|
+
const file = settingsPath();
|
|
168
|
+
if (!existsSync(file)) return { removed: false, name, reason: 'no settings file' };
|
|
169
|
+
const cfg = readJsonSafe(file, { version: 1, providers: {} });
|
|
170
|
+
if (!cfg.providers || !cfg.providers[name]) {
|
|
127
171
|
return { removed: false, name, reason: 'not present' };
|
|
128
172
|
}
|
|
129
|
-
delete cfg.
|
|
130
|
-
|
|
131
|
-
|
|
173
|
+
delete cfg.providers[name];
|
|
174
|
+
if (cfg.lastUsedProvider === name) {
|
|
175
|
+
cfg.lastUsedProvider = Object.keys(cfg.providers)[0] || undefined;
|
|
176
|
+
}
|
|
177
|
+
writeJsonAtomic(file, cfg);
|
|
178
|
+
return { removed: true, name, path: file };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* v6.2.3 — Detect the broken `openai-compatible` providerId pattern
|
|
183
|
+
* from earlier installs and the Cline v6.0.1 migration shim.
|
|
184
|
+
*
|
|
185
|
+
* Background: v6.0.1's Cline auto-migration created synthetic providers
|
|
186
|
+
* with `provider: "openai-compatible"` (the `family`, not a real
|
|
187
|
+
* `id`). Cline CLI accepts this, but Cline kanban mode looks up the
|
|
188
|
+
* provider in its built-in catalog and fails with:
|
|
189
|
+
* "Unknown or disabled provider \"openai-compatible\"."
|
|
190
|
+
*
|
|
191
|
+
* The fix: if the settings file has a `provider: "openai-compatible"`
|
|
192
|
+
* entry, rename it to a real catalog ID (default `litellm`) so
|
|
193
|
+
* kanban mode recognizes it. We do this IN-PLACE without losing the
|
|
194
|
+
* user's baseUrl, apiKey, or model.
|
|
195
|
+
*/
|
|
196
|
+
export function migrateLegacyOpenaiCompatible(targetName = 'litellm') {
|
|
197
|
+
const file = settingsPath();
|
|
198
|
+
if (!existsSync(file)) return { migrated: false, reason: 'no settings file' };
|
|
199
|
+
const cfg = readJsonSafe(file, { version: 1, providers: {} });
|
|
200
|
+
const broken = cfg.providers && cfg.providers['openai-compatible'];
|
|
201
|
+
if (!broken) return { migrated: false, reason: 'no legacy entry' };
|
|
202
|
+
|
|
203
|
+
// Move the entry to a real providerId.
|
|
204
|
+
const newBody = { ...broken };
|
|
205
|
+
// Strip the `provider: "openai-compatible"` field (replaced by the
|
|
206
|
+
// real providerId at the top level) and keep the rest (baseUrl,
|
|
207
|
+
// apiKey, model, reasoning, etc.).
|
|
208
|
+
if (newBody.settings) {
|
|
209
|
+
newBody.settings.provider = targetName;
|
|
210
|
+
}
|
|
211
|
+
cfg.providers[targetName] = newBody;
|
|
212
|
+
delete cfg.providers['openai-compatible'];
|
|
213
|
+
// If the broken entry was the active one, switch the new one in.
|
|
214
|
+
if (cfg.lastUsedProvider === 'openai-compatible' || !cfg.lastUsedProvider) {
|
|
215
|
+
cfg.lastUsedProvider = targetName;
|
|
216
|
+
}
|
|
217
|
+
writeJsonAtomic(file, cfg);
|
|
218
|
+
return { migrated: true, from: 'openai-compatible', to: targetName, path: file };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* v6.2.3 — `bizar setup-provider --discover`.
|
|
223
|
+
*
|
|
224
|
+
* Probe a list of well-known local gateway URLs and use the first one
|
|
225
|
+
* that responds. Then write the provider block with the discovered URL.
|
|
226
|
+
*/
|
|
227
|
+
export async function discoverLocalGateway() {
|
|
228
|
+
const candidates = [
|
|
229
|
+
'http://localhost:20128/v1', // 9Router (Bizar default)
|
|
230
|
+
'http://127.0.0.1:20128/v1',
|
|
231
|
+
'http://localhost:4000/v1', // LiteLLM
|
|
232
|
+
'http://localhost:11434/v1', // Ollama
|
|
233
|
+
'http://localhost:1234/v1', // LM Studio
|
|
234
|
+
];
|
|
235
|
+
for (const url of candidates) {
|
|
236
|
+
try {
|
|
237
|
+
const models = await listGatewayModels(url);
|
|
238
|
+
if (models.length > 0) {
|
|
239
|
+
return { url, modelCount: models.length, models };
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
// Try the next one
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
132
246
|
}
|
|
133
247
|
|
|
134
248
|
export function showSetupProviderHelp() {
|
|
135
249
|
console.log(`
|
|
136
|
-
bizar setup-provider — Configure a provider in
|
|
250
|
+
bizar setup-provider — Configure a provider in Cline's settings
|
|
137
251
|
|
|
138
252
|
Usage:
|
|
139
|
-
bizar setup-provider Interactive setup (default: 9Router gateway)
|
|
253
|
+
bizar setup-provider Interactive setup (default: litellm + 9Router gateway)
|
|
140
254
|
bizar setup-provider --list Print the live model catalog
|
|
141
255
|
bizar setup-provider --gateway <url> Override the gateway URL (default: http://localhost:20128/v1)
|
|
142
256
|
bizar setup-provider --key <key> Provider API key
|
|
143
257
|
- All-caps-with-underscores → \${env:KEY}
|
|
144
258
|
- Otherwise → literal value
|
|
145
|
-
bizar setup-provider --provider <name>
|
|
259
|
+
bizar setup-provider --provider <name> Built-in providerId (default: litellm)
|
|
260
|
+
See OPENAI_COMPATIBLE_PROVIDERS for the supported list
|
|
261
|
+
bizar setup-provider --model <id> Model ID (default: first model from \${gateway}/v1/models)
|
|
262
|
+
bizar setup-provider --discover Auto-detect a local gateway and configure
|
|
146
263
|
bizar setup-provider --remove <name> Remove a provider by name
|
|
147
264
|
bizar setup-provider --help Show this help
|
|
148
265
|
|
|
149
266
|
Description:
|
|
150
267
|
Since v6.2.2 the Bizar installer no longer touches provider config.
|
|
151
|
-
Use this command to add or update a provider in
|
|
268
|
+
Use this command to add or update a provider in
|
|
269
|
+
\`~/.cline/data/settings/providers.json\` (the file the Cline CLI
|
|
270
|
+
and kanban mode both read).
|
|
152
271
|
|
|
153
|
-
|
|
154
|
-
|
|
272
|
+
v6.2.3 changed the default providerId from \`9router\` to \`litellm\`.
|
|
273
|
+
\`litellm\` is a real entry in Cline's built-in catalog (so it works
|
|
274
|
+
in kanban mode), \`family: "openai-compatible"\`, and accepts a
|
|
275
|
+
custom baseUrl — perfect for the local 9Router gateway.
|
|
276
|
+
|
|
277
|
+
The previous v6.2.2 default \`9router\` and any hand-written
|
|
278
|
+
providerId like \`openai-compatible\` are NOT in Cline's catalog,
|
|
279
|
+
so they fail in kanban mode with:
|
|
280
|
+
"Unknown or disabled provider \\"openai-compatible\\"."
|
|
155
281
|
|
|
156
282
|
Examples:
|
|
157
283
|
bizar setup-provider
|
|
158
284
|
bizar setup-provider --list
|
|
159
|
-
bizar setup-provider --
|
|
285
|
+
bizar setup-provider --discover
|
|
286
|
+
bizar setup-provider --provider ollama --gateway http://localhost:11434/v1
|
|
160
287
|
|
|
161
288
|
Related:
|
|
162
289
|
bizar connect Interactive TUI for provider setup
|
|
@@ -170,12 +297,15 @@ export function parseFlags(args) {
|
|
|
170
297
|
for (let i = 0; i < args.length; i++) {
|
|
171
298
|
const a = args[i];
|
|
172
299
|
if (a === '--list') flags.list = true;
|
|
300
|
+
else if (a === '--discover') flags.discover = true;
|
|
173
301
|
else if (a === '--gateway' && i + 1 < args.length) { flags.gateway = args[++i]; }
|
|
174
302
|
else if (a.startsWith('--gateway=')) flags.gateway = a.slice('--gateway='.length);
|
|
175
303
|
else if (a === '--key' && i + 1 < args.length) { flags.key = args[++i]; }
|
|
176
304
|
else if (a.startsWith('--key=')) flags.key = a.slice('--key='.length);
|
|
177
305
|
else if (a === '--provider' && i + 1 < args.length) { flags.provider = args[++i]; }
|
|
178
306
|
else if (a.startsWith('--provider=')) flags.provider = a.slice('--provider='.length);
|
|
307
|
+
else if (a === '--model' && i + 1 < args.length) { flags.model = args[++i]; }
|
|
308
|
+
else if (a.startsWith('--model=')) flags.model = a.slice('--model='.length);
|
|
179
309
|
else if (a === '--remove' && i + 1 < args.length) { flags.remove = args[++i]; }
|
|
180
310
|
else if (a.startsWith('--remove=')) flags.remove = a.slice('--remove='.length);
|
|
181
311
|
}
|
|
@@ -189,6 +319,19 @@ export async function runSetupProvider(args = []) {
|
|
|
189
319
|
}
|
|
190
320
|
const flags = parseFlags(args);
|
|
191
321
|
|
|
322
|
+
// v6.2.3 — Auto-migrate the legacy `openai-compatible` providerId
|
|
323
|
+
// shim. This runs on every invocation (except --help/--list/--remove)
|
|
324
|
+
// because it's idempotent and fixes a real bug. Users who hit
|
|
325
|
+
// "Unknown or disabled provider openai-compatible" in kanban mode
|
|
326
|
+
// get healed automatically the next time they run setup-provider.
|
|
327
|
+
if (!flags.list && !flags.remove) {
|
|
328
|
+
const mig = migrateLegacyOpenaiCompatible(DEFAULT_PROVIDER);
|
|
329
|
+
if (mig.migrated) {
|
|
330
|
+
console.log(chalk.yellow(`\n ⚠ Migrated legacy '${mig.from}' → '${mig.to}'`));
|
|
331
|
+
console.log(chalk.yellow(' (kanban mode requires a built-in providerId; litellm works for any OpenAI-compatible endpoint.)'));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
192
335
|
// --list — print the catalog and exit
|
|
193
336
|
if (flags.list) {
|
|
194
337
|
const gateway = flags.gateway || DEFAULT_GATEWAY;
|
|
@@ -234,8 +377,27 @@ export async function runSetupProvider(args = []) {
|
|
|
234
377
|
return;
|
|
235
378
|
}
|
|
236
379
|
|
|
237
|
-
//
|
|
238
|
-
|
|
380
|
+
// --discover — auto-detect a local gateway
|
|
381
|
+
let gateway = flags.gateway;
|
|
382
|
+
if (flags.discover || (!gateway && !flags.key)) {
|
|
383
|
+
console.log(chalk.cyan('\n Probing for local gateways...'));
|
|
384
|
+
const found = await discoverLocalGateway();
|
|
385
|
+
if (found) {
|
|
386
|
+
gateway = found.url;
|
|
387
|
+
console.log(chalk.green(` ✓ Found ${found.modelCount} model(s) at ${found.url}`));
|
|
388
|
+
} else if (flags.discover) {
|
|
389
|
+
console.log(chalk.red(' ✗ No local gateway found. Tried:'));
|
|
390
|
+
for (const c of ['20128 (9Router)', '4000 (LiteLLM)', '11434 (Ollama)', '1234 (LM Studio)']) {
|
|
391
|
+
console.log(chalk.red(` - localhost:${c.split(' ')[0]}`));
|
|
392
|
+
}
|
|
393
|
+
process.exit(1);
|
|
394
|
+
} else {
|
|
395
|
+
gateway = DEFAULT_GATEWAY;
|
|
396
|
+
console.log(chalk.dim(` ! No local gateway detected, using default ${gateway}`));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
gateway = gateway || DEFAULT_GATEWAY;
|
|
400
|
+
|
|
239
401
|
const name = flags.provider || DEFAULT_PROVIDER;
|
|
240
402
|
const apiKey = flags.key || process.env.NINEROUTER_KEY || process.env.OPENAI_API_KEY || 'sk-replace-me';
|
|
241
403
|
|
|
@@ -256,27 +418,39 @@ export async function runSetupProvider(args = []) {
|
|
|
256
418
|
|
|
257
419
|
const finalKey = flags.key || apiKey;
|
|
258
420
|
|
|
259
|
-
|
|
260
|
-
let
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
421
|
+
// Pick the model: explicit > first from catalog > sensible default.
|
|
422
|
+
let model = flags.model;
|
|
423
|
+
if (!model) {
|
|
424
|
+
console.log(chalk.cyan(`\n Fetching model catalog from ${gateway}...`));
|
|
425
|
+
try {
|
|
426
|
+
const catalog = await listGatewayModels(gateway);
|
|
427
|
+
if (catalog.length > 0) {
|
|
428
|
+
// Prefer minimaxcustom/MiniMax-M3 if available (the Bizar default model)
|
|
429
|
+
const preferred = catalog.find((m) => m.id === 'minimaxcustom/MiniMax-M3');
|
|
430
|
+
model = preferred ? preferred.id : catalog[0].id;
|
|
431
|
+
console.log(chalk.green(` ✓ Found ${catalog.length} model(s); using '${model}'`));
|
|
432
|
+
} else {
|
|
433
|
+
model = 'minimaxcustom/MiniMax-M3';
|
|
434
|
+
console.log(chalk.yellow(` ! Empty catalog; defaulting to ${model}`));
|
|
435
|
+
}
|
|
436
|
+
} catch (err) {
|
|
437
|
+
model = 'minimaxcustom/MiniMax-M3';
|
|
438
|
+
console.log(chalk.yellow(` ! Could not fetch catalog (${err.message}); defaulting to ${model}`));
|
|
439
|
+
}
|
|
269
440
|
}
|
|
270
441
|
|
|
271
|
-
const
|
|
442
|
+
const body = buildProviderBlock({ name, gateway, apiKey: finalKey, model });
|
|
272
443
|
try {
|
|
273
|
-
const result = applyProviderBlock(
|
|
444
|
+
const result = applyProviderBlock(name, body);
|
|
274
445
|
console.log(chalk.green(`\n ✓ Provider '${result.name}' written to ${result.path}`));
|
|
275
446
|
console.log(chalk.dim(` backup: ${result.backup}`));
|
|
276
447
|
console.log(chalk.dim(` baseUrl: ${gateway}`));
|
|
277
|
-
console.log(chalk.dim(`
|
|
448
|
+
console.log(chalk.dim(` model: ${model}`));
|
|
449
|
+
console.log(chalk.dim(` apiKey: ${looksLikeEnvVar(finalKey) ? `\${env:${finalKey}}` : (finalKey.length > 12 ? finalKey.slice(0, 8) + '...' : finalKey)}`));
|
|
278
450
|
console.log('');
|
|
279
|
-
console.log(chalk.cyan(' Next:
|
|
451
|
+
console.log(chalk.cyan(' Next:'));
|
|
452
|
+
console.log(chalk.cyan(' • Run `bizar validate` to confirm everything is wired up.'));
|
|
453
|
+
console.log(chalk.cyan(' • The same provider now works in both `cline` CLI and `cline --kanban` mode.'));
|
|
280
454
|
} catch (err) {
|
|
281
455
|
console.log(chalk.red(` ✗ ${err.message}`));
|
|
282
456
|
process.exit(1);
|