@polderlabs/bizar 3.15.0 → 3.16.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/cli/bin.mjs +18 -16
- package/cli/install.mjs +8 -0
- package/cli/providers-detect.mjs +233 -0
- package/cli/update.mjs +45 -29
- package/package.json +1 -1
package/cli/bin.mjs
CHANGED
|
@@ -84,7 +84,7 @@ function showHelp() {
|
|
|
84
84
|
plan <subcommand> Manage visual plans
|
|
85
85
|
graph Per-project knowledge graph (powered by graphify)
|
|
86
86
|
test-gate Detect & run the project's test suite
|
|
87
|
-
update
|
|
87
|
+
update Auto-update everything (opencode + bizar + dash + plugin)
|
|
88
88
|
service Manage the background service daemon
|
|
89
89
|
bg <subcommand> Manage background agents (list/view/kill/logs)
|
|
90
90
|
dash <subcommand> Manage the dashboard (start/stop/status/tui)
|
|
@@ -182,13 +182,12 @@ function showInstallHelp() {
|
|
|
182
182
|
|
|
183
183
|
function showUpdateHelp() {
|
|
184
184
|
console.log(`
|
|
185
|
-
bizar update — Update opencode, bizar, and/or bizar-plugin
|
|
185
|
+
bizar update — Update opencode, bizar, bizar-dash, and/or bizar-plugin
|
|
186
186
|
|
|
187
187
|
Usage:
|
|
188
|
-
bizar update
|
|
189
|
-
bizar update --
|
|
188
|
+
bizar update Update EVERYTHING (default; auto-kills + restarts)
|
|
189
|
+
bizar update --pick Interactive picker (legacy per-component UI)
|
|
190
190
|
bizar update opencode bizar dash Update specific components
|
|
191
|
-
bizar update --all --yes Update everything + auto-kill running instances
|
|
192
191
|
bizar update --no-restart Don't auto-restart the dashboard after update
|
|
193
192
|
bizar update --dry-run Print what would happen, change nothing
|
|
194
193
|
bizar update --help Show this help
|
|
@@ -199,13 +198,15 @@ function showUpdateHelp() {
|
|
|
199
198
|
dash @polderlabs/bizar-dash (web dashboard + TUI)
|
|
200
199
|
plugin @polderlabs/bizar-plugin (opencode plugin)
|
|
201
200
|
|
|
202
|
-
Behavior:
|
|
201
|
+
Behavior (v3.15.0+):
|
|
202
|
+
• Default = automatic. Updates all 4 components without prompting.
|
|
203
|
+
Any missing package is auto-installed so a broken install gets
|
|
204
|
+
repaired in one shot.
|
|
203
205
|
• Detects running Bizar instances (background service daemon, web
|
|
204
206
|
dashboard) by reading ~/.config/bizar/{service,dashboard}.pid and
|
|
205
|
-
|
|
206
|
-
•
|
|
207
|
-
|
|
208
|
-
non-interactive shells).
|
|
207
|
+
cleans up any stale or empty PID files.
|
|
208
|
+
• Auto-kills running instances with a brief notice (use --pick to
|
|
209
|
+
be prompted first instead).
|
|
209
210
|
• Sends SIGTERM, waits up to 5s, escalates to SIGKILL if needed.
|
|
210
211
|
• Re-runs the install script so the deployed plugin source matches
|
|
211
212
|
the just-upgraded npm version (avoids the version-skew trap).
|
|
@@ -214,15 +215,12 @@ function showUpdateHelp() {
|
|
|
214
215
|
(skipped with --no-restart).
|
|
215
216
|
• Runs \`bizar doctor\` after a successful update to catch config
|
|
216
217
|
regressions before opencode tries to start.
|
|
217
|
-
• If ~/.config/opencode/plugins/bizar is a dev symlink (created
|
|
218
|
-
by \`bizar dev-link\`), the copy step is skipped to preserve the
|
|
219
|
-
link. Use \`bizar dev-unlink\` first, or pass --force.
|
|
220
218
|
|
|
221
219
|
Examples:
|
|
222
|
-
bizar update
|
|
223
|
-
bizar update
|
|
220
|
+
bizar update Full auto-update (recommended)
|
|
221
|
+
bizar update --dry-run Preview what would change
|
|
222
|
+
bizar update --pick Pick specific components
|
|
224
223
|
bizar update plugin --no-restart Plugin-only, leave dashboard alone
|
|
225
|
-
bizar update --all --dry-run See what would change without doing it
|
|
226
224
|
`);
|
|
227
225
|
}
|
|
228
226
|
|
|
@@ -537,6 +535,10 @@ async function main() {
|
|
|
537
535
|
// v3.11.1 — Background agent manager (list / view / kill / logs).
|
|
538
536
|
const { runBg } = await import('./bg.mjs');
|
|
539
537
|
await runBg(args[1], args.slice(2));
|
|
538
|
+
} else if (args[0] === 'providers' && args[1] === 'detect') {
|
|
539
|
+
// v3.16.0 — Auto-detect provider API keys from env + opencode.json.
|
|
540
|
+
const { runProvidersDetect } = await import('./providers-detect.mjs');
|
|
541
|
+
await runProvidersDetect(args.slice(2));
|
|
540
542
|
} else if (args[0] === 'dash' || args[0] === 'dashboard') {
|
|
541
543
|
// `bizar dashboard` is a deprecated alias for `bizar dash`
|
|
542
544
|
if (args[0] === 'dashboard') {
|
package/cli/install.mjs
CHANGED
|
@@ -406,6 +406,14 @@ export async function runInstaller() {
|
|
|
406
406
|
));
|
|
407
407
|
console.log();
|
|
408
408
|
|
|
409
|
+
// ── Provider auto-detect (v3.16.0) — best-effort, doesn't block install
|
|
410
|
+
const { runProvidersDetect } = await import('./providers-detect.mjs');
|
|
411
|
+
try {
|
|
412
|
+
await runProvidersDetect(['--no-probe']);
|
|
413
|
+
} catch {
|
|
414
|
+
// best-effort
|
|
415
|
+
}
|
|
416
|
+
|
|
409
417
|
// ── Restart prompt ──
|
|
410
418
|
const shouldRestart = await promptRestartOpenCode();
|
|
411
419
|
if (shouldRestart) {
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/providers-detect.mjs
|
|
3
|
+
*
|
|
4
|
+
* v3.16.0 — Auto-detect providers from environment variables and
|
|
5
|
+
* opencode.json. Surfaces which providers have working API keys,
|
|
6
|
+
* which have keys in unexpected formats, and which need configuration.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bizar providers detect — probe + print
|
|
10
|
+
* bizar providers detect --no-probe — skip the /models probe
|
|
11
|
+
* bizar providers detect --json — machine-readable
|
|
12
|
+
* bizar providers detect --install <id> — auto-add to opencode.json
|
|
13
|
+
*
|
|
14
|
+
* Recognised env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY,
|
|
15
|
+
* GEMINI_API_KEY, GOOGLE_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY,
|
|
16
|
+
* COHERE_API_KEY, OPENROUTER_API_KEY, DEEPSEEK_API_KEY,
|
|
17
|
+
* MINIMAX_API_KEY.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
|
|
24
|
+
const HOME = homedir();
|
|
25
|
+
const OPENCODE_JSON = join(HOME, '.config', 'opencode', 'opencode.json');
|
|
26
|
+
|
|
27
|
+
const KNOWN_PROVIDERS = [
|
|
28
|
+
{ id: 'anthropic', name: 'Anthropic', envKeys: ['ANTHROPIC_API_KEY'], baseURL: 'https://api.anthropic.com/v1', keyPattern: /^sk-ant-[A-Za-z0-9_-]{20,}$/ },
|
|
29
|
+
{ id: 'openai', name: 'OpenAI', envKeys: ['OPENAI_API_KEY'], baseURL: 'https://api.openai.com/v1', keyPattern: /^sk-[A-Za-z0-9]{20,}$/ },
|
|
30
|
+
{ id: 'google', name: 'Google AI', envKeys: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'], baseURL: 'https://generativelanguage.googleapis.com/v1beta', keyPattern: /^AIza[A-Za-z0-9_-]{30,}$/ },
|
|
31
|
+
{ id: 'mistral', name: 'Mistral', envKeys: ['MISTRAL_API_KEY'], baseURL: 'https://api.mistral.ai/v1', keyPattern: /^[A-Za-z0-9]{20,}$/ },
|
|
32
|
+
{ id: 'groq', name: 'Groq', envKeys: ['GROQ_API_KEY'], baseURL: 'https://api.groq.com/openai/v1', keyPattern: /^gsk_[A-Za-z0-9]{20,}$/ },
|
|
33
|
+
{ id: 'cohere', name: 'Cohere', envKeys: ['COHERE_API_KEY'], baseURL: 'https://api.cohere.com/v1', keyPattern: /^[A-Za-z0-9]{20,}$/ },
|
|
34
|
+
{ id: 'openrouter', name: 'OpenRouter', envKeys: ['OPENROUTER_API_KEY'], baseURL: 'https://openrouter.ai/api/v1', keyPattern: /^sk-or-[A-Za-z0-9_-]{20,}$/ },
|
|
35
|
+
{ id: 'deepseek', name: 'DeepSeek', envKeys: ['DEEPSEEK_API_KEY'], baseURL: 'https://api.deepseek.com/v1', keyPattern: /^sk-[A-Za-z0-9]{20,}$/ },
|
|
36
|
+
{ id: 'minimax', name: 'MiniMax', envKeys: ['MINIMAX_API_KEY', 'ANTHROPIC_API_KEY'], baseURL: 'https://api.minimax.chat/v1', keyPattern: /^[A-Za-z0-9]{20,}$/ },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function loadOpencodeConfig() {
|
|
40
|
+
try {
|
|
41
|
+
if (!existsSync(OPENCODE_JSON)) return {};
|
|
42
|
+
return JSON.parse(readFileSync(OPENCODE_JSON, 'utf8')) || {};
|
|
43
|
+
} catch {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function saveOpencodeConfig(cfg) {
|
|
49
|
+
mkdirSync(dirname(OPENCODE_JSON), { recursive: true });
|
|
50
|
+
const tmp = `${OPENCODE_JSON}.tmp.${process.pid}`;
|
|
51
|
+
writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n', 'utf8');
|
|
52
|
+
renameSync(tmp, OPENCODE_JSON);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function probe(spec, apiKey) {
|
|
56
|
+
const ctrl = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
58
|
+
try {
|
|
59
|
+
const resp = await fetch(`${spec.baseURL}/models`, {
|
|
60
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
61
|
+
signal: ctrl.signal,
|
|
62
|
+
});
|
|
63
|
+
if (resp.ok) {
|
|
64
|
+
let modelCount = 0;
|
|
65
|
+
try {
|
|
66
|
+
const body = await resp.json();
|
|
67
|
+
if (Array.isArray(body?.data)) modelCount = body.data.length;
|
|
68
|
+
else if (Array.isArray(body)) modelCount = body.length;
|
|
69
|
+
} catch { /* ignore */ }
|
|
70
|
+
return { ok: true, status: resp.status, modelCount };
|
|
71
|
+
}
|
|
72
|
+
return { ok: false, status: resp.status, reason: `HTTP ${resp.status}` };
|
|
73
|
+
} catch (err) {
|
|
74
|
+
return { ok: false, reason: err?.name === 'AbortError' ? 'timeout' : 'network' };
|
|
75
|
+
} finally {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function detect({ probe: doProbe }) {
|
|
81
|
+
const cfg = loadOpencodeConfig();
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const spec of KNOWN_PROVIDERS) {
|
|
84
|
+
let apiKey = '';
|
|
85
|
+
let keySource = '';
|
|
86
|
+
const cfgProvider = cfg.provider?.[spec.id];
|
|
87
|
+
if (cfgProvider?.apiKey) {
|
|
88
|
+
apiKey = cfgProvider.apiKey;
|
|
89
|
+
keySource = 'config';
|
|
90
|
+
} else if (cfgProvider?.options?.apiKey) {
|
|
91
|
+
apiKey = cfgProvider.options.apiKey;
|
|
92
|
+
keySource = 'config';
|
|
93
|
+
}
|
|
94
|
+
for (const k of spec.envKeys) {
|
|
95
|
+
const v = process.env[k];
|
|
96
|
+
if (typeof v === 'string' && v.length > 0) {
|
|
97
|
+
apiKey = v;
|
|
98
|
+
keySource = 'env';
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let status;
|
|
103
|
+
if (!apiKey) {
|
|
104
|
+
status = 'no-key';
|
|
105
|
+
} else if (spec.keyPattern && !spec.keyPattern.test(apiKey)) {
|
|
106
|
+
status = 'unknown';
|
|
107
|
+
} else {
|
|
108
|
+
status = 'configured';
|
|
109
|
+
}
|
|
110
|
+
let probed = null;
|
|
111
|
+
if (doProbe && status === 'configured') {
|
|
112
|
+
probed = await probe(spec, apiKey);
|
|
113
|
+
if (!probed.ok && probed.status) status = 'unknown';
|
|
114
|
+
}
|
|
115
|
+
out.push({
|
|
116
|
+
id: spec.id,
|
|
117
|
+
name: spec.name,
|
|
118
|
+
baseURL: spec.baseURL,
|
|
119
|
+
status,
|
|
120
|
+
keySource,
|
|
121
|
+
hasKey: !!apiKey,
|
|
122
|
+
probed,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function printTable(results) {
|
|
129
|
+
const pad = (s, n) => String(s).padEnd(n);
|
|
130
|
+
const idW = Math.max(2, ...results.map((r) => r.id.length));
|
|
131
|
+
const nameW = Math.max(4, ...results.map((r) => r.name.length));
|
|
132
|
+
const statusW = Math.max(6, ...results.map((r) => r.status.length));
|
|
133
|
+
const sourceW = Math.max(6, ...results.map((r) => r.keySource.length || 1));
|
|
134
|
+
console.log();
|
|
135
|
+
console.log(chalk.bold(` ${pad('ID', idW)} ${pad('NAME', nameW)} ${pad('STATUS', statusW)} ${pad('SOURCE', sourceW)} PROBE`));
|
|
136
|
+
console.log(chalk.dim(` ${'-'.repeat(idW)} ${'-'.repeat(nameW)} ${'-'.repeat(statusW)} ${'-'.repeat(sourceW)} ${'-'.repeat(20)}`));
|
|
137
|
+
for (const r of results) {
|
|
138
|
+
const id = pad(r.id, idW);
|
|
139
|
+
const name = pad(r.name, nameW);
|
|
140
|
+
const status = pad(r.status, statusW);
|
|
141
|
+
const source = pad(r.keySource || '-', sourceW);
|
|
142
|
+
const statusColored =
|
|
143
|
+
r.status === 'configured' ? chalk.green(status) :
|
|
144
|
+
r.status === 'unknown' ? chalk.yellow(status) :
|
|
145
|
+
chalk.dim(status);
|
|
146
|
+
const probeTxt = r.probed
|
|
147
|
+
? (r.probed.modelCount != null ? chalk.green(`${r.probed.modelCount} models`) : chalk.dim(r.probed.reason || 'ok'))
|
|
148
|
+
: chalk.dim('-');
|
|
149
|
+
console.log(` ${id} ${name} ${statusColored} ${pad(source, sourceW)} ${probeTxt}`);
|
|
150
|
+
}
|
|
151
|
+
console.log();
|
|
152
|
+
const configured = results.filter((r) => r.status === 'configured').length;
|
|
153
|
+
const unknown = results.filter((r) => r.status === 'unknown').length;
|
|
154
|
+
const noKey = results.filter((r) => r.status === 'no-key').length;
|
|
155
|
+
console.log(chalk.bold(` ${configured} configured, ${unknown} unknown, ${noKey} no-key`));
|
|
156
|
+
console.log();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function installProvider(id) {
|
|
160
|
+
const spec = KNOWN_PROVIDERS.find((s) => s.id === id);
|
|
161
|
+
if (!spec) {
|
|
162
|
+
console.error(chalk.red(` ✗ unknown provider: ${id}`));
|
|
163
|
+
console.error(chalk.dim(` Available: ${KNOWN_PROVIDERS.map((s) => s.id).join(', ')}`));
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
// Get the apiKey from env or config
|
|
167
|
+
let apiKey = '';
|
|
168
|
+
for (const k of spec.envKeys) {
|
|
169
|
+
const v = process.env[k];
|
|
170
|
+
if (typeof v === 'string' && v.length > 0) {
|
|
171
|
+
apiKey = v;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!apiKey) {
|
|
176
|
+
console.error(chalk.red(` ✗ no API key found in env (${spec.envKeys.join(', ')})`));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const cfg = loadOpencodeConfig();
|
|
180
|
+
cfg.provider = cfg.provider || {};
|
|
181
|
+
cfg.provider[spec.id] = {
|
|
182
|
+
name: spec.name,
|
|
183
|
+
baseURL: spec.baseURL,
|
|
184
|
+
apiKey,
|
|
185
|
+
enabled: true,
|
|
186
|
+
};
|
|
187
|
+
saveOpencodeConfig(cfg);
|
|
188
|
+
console.log(chalk.green(` ✓ ${spec.name} (${spec.id}) added to opencode.json`));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function showHelp() {
|
|
192
|
+
console.log(`
|
|
193
|
+
${chalk.bold('bizar providers detect')} — auto-detect provider API keys
|
|
194
|
+
|
|
195
|
+
Usage:
|
|
196
|
+
bizar providers detect Probe env + opencode.json, print table
|
|
197
|
+
bizar providers detect --no-probe Skip the /models probe
|
|
198
|
+
bizar providers detect --json Print JSON instead of a table
|
|
199
|
+
bizar providers detect --install <id> Add a configured provider to opencode.json
|
|
200
|
+
|
|
201
|
+
Recognised env vars:
|
|
202
|
+
ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY,
|
|
203
|
+
MISTRAL_API_KEY, GROQ_API_KEY, COHERE_API_KEY, OPENROUTER_API_KEY,
|
|
204
|
+
DEEPSEEK_API_KEY, MINIMAX_API_KEY
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function runProvidersDetect(args) {
|
|
209
|
+
const doProbe = !args.includes('--no-probe');
|
|
210
|
+
const json = args.includes('--json');
|
|
211
|
+
const help = args.includes('--help') || args.includes('-h');
|
|
212
|
+
|
|
213
|
+
if (help) {
|
|
214
|
+
showHelp();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const installIdx = args.indexOf('--install');
|
|
219
|
+
const installId = installIdx >= 0 ? args[installIdx + 1] : null;
|
|
220
|
+
if (installId) {
|
|
221
|
+
installProvider(installId);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const results = await detect({ probe: doProbe });
|
|
226
|
+
if (json) {
|
|
227
|
+
console.log(JSON.stringify(results, null, 2));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
console.log(chalk.bold.cyan('\n Provider auto-detect\n'));
|
|
231
|
+
console.log(chalk.dim(` Probing ${KNOWN_PROVIDERS.length} providers...`));
|
|
232
|
+
printTable(results);
|
|
233
|
+
}
|
package/cli/update.mjs
CHANGED
|
@@ -475,6 +475,22 @@ async function promptForUpdates(forceAll) {
|
|
|
475
475
|
// Main entry point
|
|
476
476
|
// ---------------------------------------------------------------------------
|
|
477
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Default behavior policy for `bizar update`:
|
|
480
|
+
* - Update EVERYTHING (opencode + bizar + dash + plugin) automatically.
|
|
481
|
+
* - Auto-install any missing component without asking.
|
|
482
|
+
* - Kill running instances without confirmation (with a brief notice).
|
|
483
|
+
* - Re-run the install script to refresh the on-disk plugin.
|
|
484
|
+
* - Restart the dashboard if it was running.
|
|
485
|
+
*
|
|
486
|
+
* The `--pick` / `-p` flag opts into the legacy per-component picker
|
|
487
|
+
* (checkbox UI) for users who want to update only some components.
|
|
488
|
+
*
|
|
489
|
+
* The `--dry-run` flag previews what would happen without touching
|
|
490
|
+
* anything.
|
|
491
|
+
*
|
|
492
|
+
* The `--no-restart` flag skips the dashboard restart step.
|
|
493
|
+
*/
|
|
478
494
|
export async function runUpdate(subargs = []) {
|
|
479
495
|
console.log(chalk.bold.hex('#a855f7')('\n ᚦ BIZAR UPDATE ᚦ\n'));
|
|
480
496
|
|
|
@@ -483,6 +499,11 @@ export async function runUpdate(subargs = []) {
|
|
|
483
499
|
const restartAfter = !subargs.includes('--no-restart');
|
|
484
500
|
const forceAll = subargs.includes('--all');
|
|
485
501
|
const dryRun = subargs.includes('--dry-run');
|
|
502
|
+
// Opt-in interactive picker. Without `--pick`, we update everything
|
|
503
|
+
// automatically. This matches what the user actually wants 95% of
|
|
504
|
+
// the time ("update my install") and removes a prompt that
|
|
505
|
+
// interrupted the flow.
|
|
506
|
+
const interactivePick = subargs.includes('--pick') || subargs.includes('-p');
|
|
486
507
|
|
|
487
508
|
if (dryRun) {
|
|
488
509
|
console.log(
|
|
@@ -503,13 +524,16 @@ export async function runUpdate(subargs = []) {
|
|
|
503
524
|
chalk.dim(` [dry-run] would stop running instances: ${labels.join(', ')}`),
|
|
504
525
|
);
|
|
505
526
|
} else {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
console.log(chalk.
|
|
527
|
+
// In automatic mode we still print what we're about to kill
|
|
528
|
+
// but don't prompt — the operator asked for a full update and
|
|
529
|
+
// these processes would block the npm replace anyway.
|
|
530
|
+
const labels = [];
|
|
531
|
+
if (instances.service) labels.push(instances.service.label);
|
|
532
|
+
if (instances.dashboard) labels.push(instances.dashboard.label);
|
|
533
|
+
console.log(chalk.yellow(` ⚠ Stopping running instances: ${labels.join(', ')}`));
|
|
534
|
+
console.log(
|
|
535
|
+
chalk.dim(' (use `--pick` to be prompted before killing in future)'),
|
|
536
|
+
);
|
|
513
537
|
const kills = await killInstances(instances);
|
|
514
538
|
for (const k of kills) {
|
|
515
539
|
const marker = k.ok ? chalk.green('✓') : chalk.red('✗');
|
|
@@ -552,31 +576,23 @@ export async function runUpdate(subargs = []) {
|
|
|
552
576
|
}
|
|
553
577
|
console.log('');
|
|
554
578
|
|
|
555
|
-
// 3. Decide what to update.
|
|
579
|
+
// 3. Decide what to update. Default = everything. Any missing package
|
|
580
|
+
// is automatically included so a partial install gets completed.
|
|
556
581
|
let selected;
|
|
557
|
-
if (forceAll
|
|
558
|
-
// dryRun defaults to showing everything (the whole point is to see
|
|
559
|
-
// what *would* change across the board). Pass a positional list to
|
|
560
|
-
// narrow the preview.
|
|
561
|
-
selected = new Set(COMPONENTS);
|
|
562
|
-
// If the user passed positional component names alongside --dry-run,
|
|
563
|
-
// narrow the selection to those.
|
|
564
|
-
const positional = subargs.filter((a) => !a.startsWith('-'));
|
|
565
|
-
if (positional.length > 0) {
|
|
566
|
-
const valid = new Set(COMPONENTS);
|
|
567
|
-
const narrowed = positional.filter((a) => valid.has(a));
|
|
568
|
-
if (narrowed.length > 0) selected = new Set(narrowed);
|
|
569
|
-
}
|
|
570
|
-
} else if (subargs.length === 0) {
|
|
582
|
+
if (interactivePick && !dryRun && !forceAll && !assumeYes) {
|
|
571
583
|
selected = await promptForUpdates(false);
|
|
572
584
|
} else {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
585
|
+
// Auto-select: all components + any missing one (so a broken install
|
|
586
|
+
// gets repaired automatically).
|
|
587
|
+
selected = new Set(COMPONENTS);
|
|
588
|
+
for (const k of COMPONENTS) {
|
|
589
|
+
if (cur[k] === null && latest[k] !== null) selected.add(k);
|
|
590
|
+
}
|
|
591
|
+
if (interactivePick) {
|
|
592
|
+
// --pick + --dry-run / --all / --yes → still update everything
|
|
593
|
+
// but make the auto-selection visible so the dry-run output is
|
|
594
|
+
// informative.
|
|
595
|
+
console.log(chalk.dim(` Auto-selecting all components (--pick ignored due to flags).`));
|
|
580
596
|
}
|
|
581
597
|
}
|
|
582
598
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|