@polderlabs/bizar 3.12.1 → 3.12.2

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/update.mjs CHANGED
@@ -233,9 +233,18 @@ function latestVersion(pkg) {
233
233
  /**
234
234
  * Update opencode. Tries `opencode upgrade` first (the upstream installer's
235
235
  * own command); falls back to `npm install -g opencode-ai@latest`.
236
+ * Pass `{ dryRun: true }` to print what would run without executing.
236
237
  * Returns `{ ok: boolean, message: string }`.
237
238
  */
238
- function updateOpencode() {
239
+ function updateOpencode({ dryRun = false } = {}) {
240
+ if (dryRun) {
241
+ console.log(
242
+ chalk.dim(
243
+ ' [dry-run] would run: opencode upgrade (fallback: npm install -g opencode-ai@latest)',
244
+ ),
245
+ );
246
+ return { ok: true, message: '[dry-run] opencode' };
247
+ }
239
248
  const r1 = spawnSync('opencode', ['upgrade'], { stdio: 'inherit' });
240
249
  if (r1.status === 0) {
241
250
  return { ok: true, message: 'opencode updated via `opencode upgrade`' };
@@ -248,7 +257,13 @@ function updateOpencode() {
248
257
  return { ok: false, message: 'opencode update failed — try `opencode upgrade` manually' };
249
258
  }
250
259
 
251
- function updatePackage(pkg) {
260
+ function updatePackage(pkg, { dryRun = false } = {}) {
261
+ if (dryRun) {
262
+ console.log(
263
+ chalk.dim(` [dry-run] would run: npm install -g ${pkg}@latest`),
264
+ );
265
+ return { ok: true, message: `[dry-run] ${pkg}` };
266
+ }
252
267
  const r = spawnSync('npm', ['install', '-g', `${pkg}@latest`], { stdio: 'inherit' });
253
268
  if (r.status !== 0) {
254
269
  return { ok: false, message: `${pkg} update failed` };
@@ -467,27 +482,43 @@ export async function runUpdate(subargs = []) {
467
482
  const assumeYes = subargs.includes('--yes') || subargs.includes('-y') || subargs.includes('--force');
468
483
  const restartAfter = !subargs.includes('--no-restart');
469
484
  const forceAll = subargs.includes('--all');
485
+ const dryRun = subargs.includes('--dry-run');
486
+
487
+ if (dryRun) {
488
+ console.log(
489
+ chalk.dim(' --dry-run set: no installs, kills, or restarts will be performed.\n'),
490
+ );
491
+ }
470
492
 
471
493
  // 1. Detect running instances BEFORE doing anything else.
472
494
  const instances = detectInstances();
473
495
  const runningCount = (instances.service ? 1 : 0) + (instances.dashboard ? 1 : 0);
474
496
 
475
497
  if (runningCount > 0) {
476
- const ok = await confirmKill(instances, { assumeYes });
477
- if (!ok) {
478
- console.log(chalk.yellow('\n Update cancelled — instances still running.'));
479
- console.log(chalk.dim(' Stop them with `bizar service stop` and `bizar dashboard stop`, then retry.'));
480
- process.exit(1);
481
- }
482
- console.log(chalk.cyan('\n Stopping running instances...'));
483
- const kills = await killInstances(instances);
484
- for (const k of kills) {
485
- const marker = k.ok ? chalk.green('✓') : chalk.red('✗');
486
- console.log(` ${marker} ${k.name} stopped`);
498
+ if (dryRun) {
499
+ const labels = [];
500
+ if (instances.service) labels.push(instances.service.label);
501
+ if (instances.dashboard) labels.push(instances.dashboard.label);
502
+ console.log(
503
+ chalk.dim(` [dry-run] would stop running instances: ${labels.join(', ')}`),
504
+ );
505
+ } else {
506
+ const ok = await confirmKill(instances, { assumeYes });
507
+ if (!ok) {
508
+ console.log(chalk.yellow('\n Update cancelled — instances still running.'));
509
+ console.log(chalk.dim(' Stop them with `bizar service stop` and `bizar dashboard stop`, then retry.'));
510
+ process.exit(1);
511
+ }
512
+ console.log(chalk.cyan('\n Stopping running instances...'));
513
+ const kills = await killInstances(instances);
514
+ for (const k of kills) {
515
+ const marker = k.ok ? chalk.green('✓') : chalk.red('✗');
516
+ console.log(` ${marker} ${k.name} stopped`);
517
+ }
518
+ // Give the kernel a moment to release any open file handles on the
519
+ // npm-global directory before npm tries to replace files.
520
+ await new Promise((resolve) => setTimeout(resolve, 500));
487
521
  }
488
- // Give the kernel a moment to release any open file handles on the
489
- // npm-global directory before npm tries to replace files.
490
- await new Promise((resolve) => setTimeout(resolve, 500));
491
522
  } else {
492
523
  console.log(chalk.dim(' No running Bizar instances detected.'));
493
524
  }
@@ -523,8 +554,19 @@ export async function runUpdate(subargs = []) {
523
554
 
524
555
  // 3. Decide what to update.
525
556
  let selected;
526
- if (forceAll || assumeYes) {
557
+ if (forceAll || assumeYes || dryRun) {
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.
527
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
+ }
528
570
  } else if (subargs.length === 0) {
529
571
  selected = await promptForUpdates(false);
530
572
  } else {
@@ -548,44 +590,57 @@ export async function runUpdate(subargs = []) {
548
590
  const results = [];
549
591
  if (selected.has('opencode')) {
550
592
  console.log(chalk.bold(' → opencode'));
551
- results.push(['opencode', updateOpencode()]);
593
+ results.push(['opencode', updateOpencode({ dryRun })]);
552
594
  }
553
595
  if (selected.has('bizar')) {
554
596
  console.log(chalk.bold(` → ${PKG_MAIN}`));
555
- results.push(['bizar', updatePackage(PKG_MAIN)]);
597
+ results.push(['bizar', updatePackage(PKG_MAIN, { dryRun })]);
556
598
  }
557
599
  if (selected.has('dash')) {
558
600
  console.log(chalk.bold(` → ${PKG_DASH}`));
559
- results.push(['dash', updatePackage(PKG_DASH)]);
601
+ results.push(['dash', updatePackage(PKG_DASH, { dryRun })]);
560
602
  }
561
603
  if (selected.has('plugin')) {
562
604
  console.log(chalk.bold(` → ${PKG_PLUGIN}`));
563
- results.push(['plugin', updatePackage(PKG_PLUGIN)]);
605
+ results.push(['plugin', updatePackage(PKG_PLUGIN, { dryRun })]);
564
606
  }
565
607
 
566
608
  // 4. Re-run the install script if anything relevant changed.
567
609
  const anySuccess = results.some(([, r]) => r.ok);
568
610
  if (anySuccess && (selected.has('bizar') || selected.has('plugin'))) {
569
611
  console.log('');
570
- const rerun = rerunInstallScript();
571
- if (rerun.ok) {
572
- console.log(chalk.green(`\n ✓ ${rerun.message}`));
612
+ if (dryRun) {
613
+ console.log(
614
+ chalk.dim(
615
+ ' [dry-run] would re-run install script (bin.mjs --setup or install.sh)',
616
+ ),
617
+ );
573
618
  } else {
574
- console.log(chalk.yellow(`\n ⚠ ${rerun.message}`));
575
- console.log(chalk.dim(' Run `bash install.sh` from the Bizar repo manually.'));
619
+ const rerun = rerunInstallScript();
620
+ if (rerun.ok) {
621
+ console.log(chalk.green(`\n ✓ ${rerun.message}`));
622
+ } else {
623
+ console.log(chalk.yellow(`\n ⚠ ${rerun.message}`));
624
+ console.log(chalk.dim(' Run `bash install.sh` from the Bizar repo manually.'));
625
+ }
576
626
  }
577
627
  }
578
628
 
579
629
  // 5. Restart the dashboard if it was running before the update.
580
630
  if (restartAfter && instances.dashboard && (selected.has('bizar') || selected.has('dash'))) {
581
- console.log('');
582
- console.log(chalk.cyan(' Restarting dashboard with the new code...'));
583
- const res = spawnFreshDashboard({ port: instances.dashboard.port || undefined });
584
- if (res.ok) {
585
- console.log(chalk.green(` ✓ ${res.message}`));
631
+ if (dryRun) {
632
+ console.log('');
633
+ console.log(chalk.dim(' [dry-run] would restart dashboard with the new code'));
586
634
  } else {
587
- console.log(chalk.yellow(` ⚠ ${res.message}`));
588
- console.log(chalk.dim(' Start it manually with `bizar dash start --bg`.'));
635
+ console.log('');
636
+ console.log(chalk.cyan(' Restarting dashboard with the new code...'));
637
+ const res = spawnFreshDashboard({ port: instances.dashboard.port || undefined });
638
+ if (res.ok) {
639
+ console.log(chalk.green(` ✓ ${res.message}`));
640
+ } else {
641
+ console.log(chalk.yellow(` ⚠ ${res.message}`));
642
+ console.log(chalk.dim(' Start it manually with `bizar dash start --bg`.'));
643
+ }
589
644
  }
590
645
  }
591
646
 
@@ -602,5 +657,40 @@ export async function runUpdate(subargs = []) {
602
657
  console.log(chalk.yellow('\n Some updates failed. See messages above.'));
603
658
  process.exit(1);
604
659
  }
660
+ if (dryRun) {
661
+ console.log(
662
+ chalk.green('\n ✓ Dry-run complete (no installs, kills, or restarts performed)\n'),
663
+ );
664
+ return;
665
+ }
605
666
  console.log(chalk.green('\n ✓ Update complete\n'));
667
+
668
+ // 7. Post-update health check (v3.12.2). Catches a bad config merge or
669
+ // missing files before the user discovers it via a broken opencode session.
670
+ try {
671
+ const { runDoctor } = await import('./doctor.mjs');
672
+ const result = await runDoctor({ silent: true });
673
+ if (result.failed > 0) {
674
+ console.log('');
675
+ console.log(
676
+ chalk.yellow(' ⚠ Post-update health check found issues:'),
677
+ );
678
+ for (const r of result.results) {
679
+ if (!r.ok) {
680
+ console.log(chalk.red(` ✗ ${r.name}: ${r.message}`));
681
+ }
682
+ }
683
+ console.log(chalk.dim(' Run `bizar doctor` for details.'));
684
+ process.exit(1);
685
+ }
686
+ } catch (err) {
687
+ // Doctor import or runtime failure shouldn't crash the update —
688
+ // log a hint and let the user run `bizar doctor` themselves.
689
+ console.log(
690
+ chalk.yellow(
691
+ ` ⚠ Post-update health check could not run: ${err.message}`,
692
+ ),
693
+ );
694
+ console.log(chalk.dim(' Run `bizar doctor` manually to verify the install.'));
695
+ }
606
696
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "3.12.1",
3
+ "version": "3.12.2",
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": {