@polderlabs/bizar 5.6.0-beta.8 → 5.6.0-beta.9

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.
@@ -132,5 +132,21 @@ case "${1:-start}" in
132
132
  status) ensure_binary; print_status ;;
133
133
  doctor) ensure_binary; $AB_BIN doctor ;;
134
134
  install) ensure_binary; ensure_chrome; $AB_BIN install ;;
135
+ help|--help|-h)
136
+ cat <<'AGENT_BROWSER_UP_HELP'
137
+ agent-browser-up -- start/stop the agent-browser daemon
138
+
139
+ Usage: cli/agent-browser-up.sh <command>
140
+
141
+ Commands:
142
+ start Start the daemon (idempotent)
143
+ stop Stop the daemon
144
+ restart Stop + start
145
+ status Print daemon status
146
+ doctor Run agent-browser doctor
147
+ install Install agent-browser + download Chrome
148
+ AGENT_BROWSER_UP_HELP
149
+ exit 0
150
+ ;;
135
151
  *) fail "unknown command: $1 (use start|stop|restart|status|doctor|install)" ;;
136
152
  esac
package/cli/bin.mjs CHANGED
@@ -13,7 +13,8 @@
13
13
  * Commands:
14
14
  * install, audit, init, export, artifact, update, test-gate, service, dash,
15
15
  * memory, headroom, minimax, usage, mod, doctor, repair, dev-link, dev-unlink,
16
- * heads-up, bg, agent-browser, agent-browser-up, providers, deploy, plugin, marketplace
16
+ * heads-up, bg, agent-browser, agent-browser-up, providers, deploy, plugin,
17
+ * marketplace, plan, digest, backup, restore, clip, ocr, voice, workspace, eval
17
18
  */
18
19
  import chalk from 'chalk';
19
20
  import { existsSync, readFileSync } from 'node:fs';
@@ -120,6 +121,13 @@ function showHelp() {
120
121
  providers detect Auto-detect provider API keys from env + cline.json
121
122
  clip <subcommand> Manage web clipper saved clips (list/delete/configure)
122
123
  ocr <subcommand> OCR operations on images (list/process/configure)
124
+ digest Manage weekly digests (list/view/generate)
125
+ backup Create / list / verify / delete backups of BizarHarness state
126
+ restore Restore BizarHarness from a backup
127
+ voice Manage voice notes (via the dashboard's HTTP API)
128
+ workspace Manage workspaces (via the dashboard's HTTP API)
129
+ eval Evaluate AI agent outputs against golden fixtures
130
+ plan [v6.0.0+] Reserved for future plan management
123
131
 
124
132
  Examples:
125
133
  bizar install
@@ -189,8 +197,30 @@ async function main() {
189
197
  }
190
198
 
191
199
  if (isHelpRequest && !cmd.startsWith('-')) {
192
- // Pass --help to the command
193
- const mod = await importCommand(cmd);
200
+ // Pass --help to the command. Commands dispatched through util.mjs
201
+ // (audit, init, export, doctor, backup, restore, etc.) don't have
202
+ // their own cli/commands/<name>.mjs — they all live in util.mjs.
203
+ const UTIL_COMMANDS = new Set([
204
+ 'audit', 'init', 'export', 'test-gate', 'dev-link', 'dev-unlink',
205
+ 'doctor', 'repair', 'heads-up', 'bg', 'digest', 'backup', 'restore',
206
+ 'agent-browser', 'update', 'providers', 'plan',
207
+ ]);
208
+ const UTIL_ALIASES = new Set(['dashboard', 'agent-browser-up']);
209
+ let mod;
210
+ if (UTIL_COMMANDS.has(cmd)) {
211
+ // util-based commands: audit, init, etc.
212
+ mod = await importCommand('util');
213
+ } else if (UTIL_ALIASES.has(cmd)) {
214
+ // util aliases: dashboard → dash, agent-browser-up → bash script
215
+ if (cmd === 'dashboard') {
216
+ mod = await importCommand('dash');
217
+ } else if (cmd === 'agent-browser-up') {
218
+ // Run via util.mjs's `agent-browser-up` case
219
+ mod = await importCommand('util');
220
+ }
221
+ } else {
222
+ mod = await importCommand(cmd);
223
+ }
194
224
  if (mod && typeof mod.run === 'function') {
195
225
  await mod.run(cmd, cmdArgs, true);
196
226
  return;
@@ -430,6 +460,8 @@ async function main() {
430
460
  case 'heads-up':
431
461
  case 'bg':
432
462
  case 'digest':
463
+ case 'backup':
464
+ case 'restore':
433
465
  case 'agent-browser':
434
466
  case 'agent-browser-up':
435
467
  case 'providers':
@@ -451,6 +483,20 @@ async function main() {
451
483
  break;
452
484
  }
453
485
 
486
+ case 'voice':
487
+ case 'workspace':
488
+ case 'eval': {
489
+ // Each of these has its own module file in cli/commands/.
490
+ const mod = await importCommand(cmd);
491
+ if (!mod) {
492
+ console.error(chalk.red(` ✗ Could not load ${cmd} command module`));
493
+ process.exit(EXIT_ERROR);
494
+ return;
495
+ }
496
+ if (mod.run) await mod.run(cmd, cmdArgs, isHelpRequest);
497
+ break;
498
+ }
499
+
454
500
  default: {
455
501
  console.error(chalk.red(` ✗ Unknown command: ${cmd}`));
456
502
  showHelp();
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * /tmp/test-cli-commands.mjs
4
+ * Comprehensive validation of every `bizar` CLI command.
5
+ *
6
+ * Tests:
7
+ * - All case branches in cli/bin.mjs respond to --help
8
+ * - All case branches in cli/commands/util.mjs respond to --help
9
+ * - Module exports are loadable
10
+ *
11
+ * Usage: node /tmp/test-cli-commands.mjs
12
+ */
13
+
14
+ import { spawnSync } from 'node:child_process';
15
+
16
+ const REPO = process.cwd();
17
+ const BIN = `${REPO}/cli/bin.mjs`;
18
+
19
+ const ALL_COMMANDS = [
20
+ // Util-based (live in cli/commands/util.mjs)
21
+ 'audit', 'init', 'export', 'test-gate', 'dev-link', 'dev-unlink',
22
+ 'doctor', 'repair', 'heads-up', 'bg', 'digest', 'backup', 'restore',
23
+ 'agent-browser', 'agent-browser-up', 'providers',
24
+ // Own module
25
+ 'install', 'update',
26
+ 'service',
27
+ 'dash', 'dashboard',
28
+ 'minimax', 'tailscale', 'headroom', 'lightrag',
29
+ 'mod', 'usage',
30
+ 'deploy', 'plugin', 'marketplace',
31
+ 'artifact',
32
+ 'memory',
33
+ 'memory',
34
+ 'clip', 'ocr',
35
+ 'voice', 'workspace', 'eval',
36
+ ];
37
+
38
+ const NON_OWN_MODULE = new Set([
39
+ 'audit', 'init', 'export', 'test-gate', 'dev-link', 'dev-unlink',
40
+ 'doctor', 'repair', 'heads-up', 'bg', 'digest', 'backup', 'restore',
41
+ 'agent-browser', 'agent-browser-up', 'providers', 'install', 'update',
42
+ ]);
43
+
44
+ const seen = new Set();
45
+ const passed = [];
46
+ const failed = [];
47
+
48
+ for (const cmd of ALL_COMMANDS) {
49
+ if (seen.has(cmd)) continue;
50
+ seen.add(cmd);
51
+
52
+ // Run `node cli/bin.mjs <cmd> --help` and check exit code + that it prints help
53
+ const r = spawnSync('node', [BIN, cmd, '--help'], {
54
+ timeout: 15_000,
55
+ encoding: 'utf8',
56
+ stdio: ['ignore', 'pipe', 'pipe'],
57
+ });
58
+ const stdout = r.stdout || '';
59
+ const stderr = r.stderr || '';
60
+ const exitCode = r.status;
61
+
62
+ // Pass criteria:
63
+ // - Exit code 0 OR 2 (CLINE exit code for usage/help is 2 sometimes)
64
+ // - stdout mentions the command name or contains a recognizable help string
65
+ // - stderr is empty OR mentions the command name (not "module not found")
66
+ const hasModuleError = stderr.includes('Cannot find module') ||
67
+ stderr.includes('Failed to load command module');
68
+ const hasHelp = stdout.toLowerCase().includes(cmd) ||
69
+ stdout.includes('Usage:') ||
70
+ stdout.includes('Subcommands:') ||
71
+ stdout.includes('Description:');
72
+
73
+ if ((exitCode === 0 || exitCode === 2) && !hasModuleError && (hasHelp || stdout.length > 50)) {
74
+ passed.push({ cmd, exitCode, stdoutLines: stdout.split('\n').length });
75
+ } else {
76
+ failed.push({ cmd, exitCode, hasModuleError, hasHelp, stdout: stdout.slice(0, 200), stderr: stderr.slice(0, 200) });
77
+ }
78
+ }
79
+
80
+ console.log(`\n=== CLI command validation ===\n`);
81
+ console.log(`Passed: ${passed.length}/${seen.size}`);
82
+ console.log(`Failed: ${failed.length}\n`);
83
+
84
+ if (passed.length > 0) {
85
+ console.log('=== Passing commands ===');
86
+ for (const p of passed) {
87
+ console.log(` ✓ ${p.cmd.padEnd(20)} (exit ${p.exitCode}, ${p.stdoutLines} lines)`);
88
+ }
89
+ console.log();
90
+ }
91
+
92
+ if (failed.length > 0) {
93
+ console.log('=== FAILING commands ===');
94
+ for (const f of failed) {
95
+ console.log(` ✗ ${f.cmd.padEnd(20)} exitCode=${f.exitCode} moduleErr=${f.hasModuleError} hasHelp=${f.hasHelp}`);
96
+ if (f.hasModuleError) {
97
+ console.log(` stderr: ${f.stderr.trim()}`);
98
+ } else if (f.exitCode !== 0 && f.exitCode !== 2) {
99
+ console.log(` stderr: ${f.stderr.trim().slice(0, 200)}`);
100
+ console.log(` stdout: ${f.stdout.trim().slice(0, 200)}`);
101
+ }
102
+ }
103
+ console.log();
104
+ }
105
+
106
+ process.exit(failed.length > 0 ? 1 : 0);
@@ -10,7 +10,24 @@
10
10
  */
11
11
 
12
12
  import chalk from 'chalk';
13
- import { readDashboardConn } from './headroom.mjs';
13
+ // Mirror `readDashboardConn` from clip.mjs / minimax.mjs / usage.mjs.
14
+ function readDashboardConn() {
15
+ const cfgDir = process.env.XDG_CONFIG_HOME
16
+ ? require_('node:path').join(process.env.XDG_CONFIG_HOME, 'bizar')
17
+ : require_('node:path').join(require_('node:os').homedir(), '.config', 'bizar');
18
+ const portPath = require_('node:path').join(cfgDir, 'dashboard.port');
19
+ const secretPath = require_('node:path').join(cfgDir, 'dashboard.secret');
20
+ const port = require_('node:fs').existsSync(portPath)
21
+ ? parseInt(require_('node:fs').readFileSync(portPath, 'utf8').trim(), 10)
22
+ : 4321;
23
+ const secret = require_('node:fs').existsSync(secretPath)
24
+ ? require_('node:fs').readFileSync(secretPath, 'utf8').trim()
25
+ : '';
26
+ return {
27
+ port: Number.isFinite(port) && port > 0 ? port : 4321,
28
+ secret,
29
+ };
30
+ }
14
31
 
15
32
  // ── API helpers ────────────────────────────────────────────────────────────────
16
33
 
@@ -15,7 +15,25 @@ import chalk from 'chalk';
15
15
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
16
16
  import { join, dirname } from 'node:path';
17
17
  import { homedir } from 'node:os';
18
- import { readDashboardConn } from './headroom.mjs';
18
+
19
+ // Mirror `readDashboardConn` from clip.mjs / minimax.mjs / usage.mjs.
20
+ function readDashboardConn() {
21
+ const cfgDir = process.env.XDG_CONFIG_HOME
22
+ ? join(process.env.XDG_CONFIG_HOME, 'bizar')
23
+ : join(homedir(), '.config', 'bizar');
24
+ const portPath = join(cfgDir, 'dashboard.port');
25
+ const secretPath = join(cfgDir, 'dashboard.secret');
26
+ const port = existsSync(portPath)
27
+ ? parseInt(readFileSync(portPath, 'utf8').trim(), 10)
28
+ : 4321;
29
+ const secret = existsSync(secretPath)
30
+ ? readFileSync(secretPath, 'utf8').trim()
31
+ : '';
32
+ return {
33
+ port: Number.isFinite(port) && port > 0 ? port : 4321,
34
+ secret,
35
+ };
36
+ }
19
37
 
20
38
  const HOME = homedir();
21
39
  const TEMPLATES_DIR = join(HOME, '.config', 'bizar', 'templates', 'eval-fixtures');
@@ -18,7 +18,6 @@
18
18
  import chalk from 'chalk';
19
19
  import { existsSync, readFileSync } from 'node:fs';
20
20
  import { join } from 'node:path';
21
- import { process } from 'node:process';
22
21
 
23
22
  export function showLightragHelp() {
24
23
  console.log(`
@@ -11,7 +11,24 @@
11
11
 
12
12
  import chalk from 'chalk';
13
13
  import { readFileSync, existsSync } from 'node:fs';
14
- import { readDashboardConn } from './headroom.mjs';
14
+ // Mirror `readDashboardConn` from clip.mjs / minimax.mjs / usage.mjs.
15
+ function readDashboardConn() {
16
+ const cfgDir = process.env.XDG_CONFIG_HOME
17
+ ? require_('node:path').join(process.env.XDG_CONFIG_HOME, 'bizar')
18
+ : require_('node:path').join(require_('node:os').homedir(), '.config', 'bizar');
19
+ const portPath = require_('node:path').join(cfgDir, 'dashboard.port');
20
+ const secretPath = require_('node:path').join(cfgDir, 'dashboard.secret');
21
+ const port = require_('node:fs').existsSync(portPath)
22
+ ? parseInt(require_('node:fs').readFileSync(portPath, 'utf8').trim(), 10)
23
+ : 4321;
24
+ const secret = require_('node:fs').existsSync(secretPath)
25
+ ? require_('node:fs').readFileSync(secretPath, 'utf8').trim()
26
+ : '';
27
+ return {
28
+ port: Number.isFinite(port) && port > 0 ? port : 4321,
29
+ secret,
30
+ };
31
+ }
15
32
 
16
33
  // ── API helpers ────────────────────────────────────────────────────────────────
17
34
 
@@ -105,5 +105,9 @@ async function runUsageCommand(args, wantJson = false) {
105
105
  }
106
106
 
107
107
  export async function run(name, args, isHelpRequest) {
108
+ if (isHelpRequest || args.length === 0) {
109
+ showUsageHelp();
110
+ return;
111
+ }
108
112
  await runUsageCommand(args, false);
109
113
  }
@@ -154,6 +154,24 @@ export function showBackupHelp() {
154
154
  `);
155
155
  }
156
156
 
157
+ export function showProvidersHelp() {
158
+ console.log(`
159
+ bizar providers - Auto-detect provider API keys
160
+
161
+ Usage:
162
+ bizar providers detect Auto-detect provider API keys from env + cline.json
163
+ bizar providers --help Show this help
164
+
165
+ Description:
166
+ Scans process.env + ~/.config/cline/cline.json for known provider
167
+ API keys (OpenAI, Anthropic, OpenRouter, MiniMax, etc.) and reports
168
+ what's available. Useful before the first \`bizar install\` to verify
169
+ credentials are picked up.
170
+
171
+ No API keys are sent over the network - detection is local.
172
+ `);
173
+ }
174
+
157
175
  export function showRestoreHelp() {
158
176
  console.log(`
159
177
  bizar restore — Restore BizarHarness from a backup
@@ -209,6 +227,18 @@ export async function runTestGate() {
209
227
 
210
228
  export async function run(name, args, isHelpRequest) {
211
229
  switch (name) {
230
+ case 'update':
231
+ // `bizar update` lives in commands/install.mjs (the install/update
232
+ // pair share a code path). Proxy to it.
233
+ if (isHelpRequest) {
234
+ const { showUpdateHelp } = await import('./install.mjs');
235
+ showUpdateHelp();
236
+ } else {
237
+ const { runUpdate } = await import('./install.mjs');
238
+ await runUpdate(args, {});
239
+ }
240
+ break;
241
+
212
242
  case 'audit':
213
243
  if (isHelpRequest) showAuditHelp();
214
244
  else {
@@ -506,6 +536,10 @@ export async function run(name, args, isHelpRequest) {
506
536
  }
507
537
 
508
538
  case 'providers':
539
+ if (isHelpRequest || args.length === 0) {
540
+ showProvidersHelp();
541
+ break;
542
+ }
509
543
  if (args[0] === 'detect') {
510
544
  const { runProvidersDetect } = await import('../providers-detect.mjs');
511
545
  await runProvidersDetect(args.slice(1));
package/cli/digest.mjs CHANGED
@@ -13,7 +13,7 @@
13
13
  import chalk from 'chalk';
14
14
  import { readFileSync, existsSync } from 'node:fs';
15
15
 
16
- function showDigestHelp() {
16
+ export function showDigestHelp() {
17
17
  console.log(`
18
18
  bizar digest — Manage weekly digests
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "5.6.0-beta.8",
3
+ "version": "5.6.0-beta.9",
4
4
  "description": "Norse-pantheon multi-agent system for cline — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, cline plugin, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar-sdk",
3
- "version": "0.2.0-beta.8",
3
+ "version": "0.2.0-beta.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",