@polderlabs/bizar 4.7.0 → 4.7.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/bizar-dash/dist/assets/main-DHZmbnxQ.js +361 -0
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js.map +1 -0
- package/bizar-dash/dist/assets/main-DX_Jh8Wc.css +1 -0
- package/bizar-dash/dist/assets/{mobile-CWqPoGaT.js → mobile-BK8-ythT.js} +2 -2
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-i4Uv9eW8.js → mobile-Chvf9u_B.js} +1 -1
- package/bizar-dash/dist/assets/{mobile-i4Uv9eW8.js.map → mobile-Chvf9u_B.js.map} +1 -1
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/skills/publishing/SKILL.md +146 -0
- package/bizar-dash/src/server/api.mjs +8 -0
- package/bizar-dash/src/server/backup-store.mjs +525 -0
- package/bizar-dash/src/server/digest-store.mjs +558 -0
- package/bizar-dash/src/server/lib/rate-limit.mjs +122 -0
- package/bizar-dash/src/server/routes/backup.mjs +112 -0
- package/bizar-dash/src/server/routes/chat.mjs +14 -0
- package/bizar-dash/src/server/routes/digests.mjs +82 -0
- package/bizar-dash/src/server/routes-v2/events.mjs +14 -0
- package/bizar-dash/src/server/schedules-runner.mjs +126 -0
- package/bizar-dash/src/server/server.mjs +17 -0
- package/bizar-dash/src/web/App.tsx +8 -1
- package/bizar-dash/src/web/components/BackupRestore.tsx +330 -0
- package/bizar-dash/src/web/components/SearchModal.tsx +3 -1
- package/bizar-dash/src/web/components/chat/Composer.tsx +2 -0
- package/bizar-dash/src/web/styles/main.css +70 -8
- package/bizar-dash/src/web/views/Activity.tsx +11 -1
- package/bizar-dash/src/web/views/Agents.tsx +57 -42
- package/bizar-dash/src/web/views/Artifacts.tsx +38 -25
- package/bizar-dash/src/web/views/Chat.tsx +8 -0
- package/bizar-dash/src/web/views/History.tsx +4 -1
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +21 -4
- package/bizar-dash/src/web/views/Mods.tsx +30 -17
- package/bizar-dash/src/web/views/Overview.tsx +8 -1
- package/bizar-dash/src/web/views/Providers.tsx +16 -16
- package/bizar-dash/src/web/views/Schedules.tsx +33 -15
- package/bizar-dash/src/web/views/Settings.tsx +97 -1751
- package/bizar-dash/src/web/views/Skills.tsx +4 -1
- package/bizar-dash/src/web/views/Tasks.tsx +11 -2
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +8 -2
- package/bizar-dash/src/web/views/memory/LightragPanel.tsx +3 -0
- package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +12 -4
- package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +3 -0
- package/bizar-dash/src/web/views/settings/ActivitySection.tsx +205 -0
- package/bizar-dash/src/web/views/settings/AgentSection.tsx +294 -0
- package/bizar-dash/src/web/views/settings/AuthSection.tsx +159 -0
- package/bizar-dash/src/web/views/settings/BackupSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/GeneralSection.tsx +105 -0
- package/bizar-dash/src/web/views/settings/HeadroomSection.tsx +39 -0
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/NetworkSection.tsx +87 -0
- package/bizar-dash/src/web/views/settings/NotificationsSection.tsx +34 -0
- package/bizar-dash/src/web/views/settings/ProvidersSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/SkillsSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/SystemLlmSection.tsx +81 -0
- package/bizar-dash/src/web/views/settings/ThemeSection.tsx +168 -0
- package/bizar-dash/src/web/views/settings/UpdatesSection.tsx +256 -0
- package/bizar-dash/tests/a11y.test.tsx +206 -0
- package/bizar-dash/tests/backup-restore.test.mjs +217 -0
- package/bizar-dash/tests/backup-restore.test.tsx +123 -0
- package/bizar-dash/tests/backup-store.test.mjs +300 -0
- package/bizar-dash/tests/cli-error-visibility.test.mjs +153 -0
- package/bizar-dash/tests/digest-generation.test.mjs +191 -0
- package/bizar-dash/tests/digest-store.test.mjs +264 -0
- package/bizar-dash/tests/rate-limit.test.mjs +298 -0
- package/cli/bin.mjs +96 -2
- package/cli/commands/minimax.mjs +20 -0
- package/cli/commands/util.mjs +154 -1
- package/cli/digest.mjs +149 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/main-DAlLdW8I.css +0 -1
- package/bizar-dash/dist/assets/main-DGGq-iZI.js +0 -361
- package/bizar-dash/dist/assets/main-DGGq-iZI.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-CWqPoGaT.js.map +0 -1
package/cli/bin.mjs
CHANGED
|
@@ -136,7 +136,12 @@ function showHelp() {
|
|
|
136
136
|
async function importCommand(name) {
|
|
137
137
|
try {
|
|
138
138
|
return await import(`./commands/${name}.mjs`);
|
|
139
|
-
} catch {
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error(chalk.red(` ✗ Failed to load command module '${name}': ${err && err.message ? err.message : String(err)}`));
|
|
141
|
+
if (err && err.stack) {
|
|
142
|
+
const lines = err.stack.split('\n').slice(0, 3);
|
|
143
|
+
console.error(chalk.dim(lines.join('\n')));
|
|
144
|
+
}
|
|
140
145
|
return null;
|
|
141
146
|
}
|
|
142
147
|
}
|
|
@@ -184,61 +189,131 @@ async function main() {
|
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
191
|
|
|
192
|
+
// `bizar help` — explicit help alias
|
|
193
|
+
if (cmd === 'help') {
|
|
194
|
+
showHelp();
|
|
195
|
+
process.exit(EXIT_OK);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
187
199
|
// Dispatch to command modules
|
|
188
200
|
switch (cmd) {
|
|
189
201
|
case 'install':
|
|
190
202
|
case 'update': {
|
|
191
203
|
const mod = await importCommand('install');
|
|
204
|
+
if (!mod) {
|
|
205
|
+
console.error(chalk.red(` ✗ Could not load install command module`));
|
|
206
|
+
process.exit(EXIT_ERROR);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
dbg('loaded command module:', 'install');
|
|
192
210
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
211
|
+
dbg('command returned:', cmd);
|
|
193
212
|
break;
|
|
194
213
|
}
|
|
195
214
|
|
|
196
215
|
case 'service': {
|
|
197
216
|
const mod = await importCommand('service');
|
|
217
|
+
if (!mod) {
|
|
218
|
+
console.error(chalk.red(` ✗ Could not load service command module`));
|
|
219
|
+
process.exit(EXIT_ERROR);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
dbg('loaded command module:', 'service');
|
|
198
223
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
224
|
+
dbg('command returned:', cmd);
|
|
199
225
|
break;
|
|
200
226
|
}
|
|
201
227
|
|
|
202
228
|
case 'dash':
|
|
203
229
|
case 'dashboard': {
|
|
204
230
|
const mod = await importCommand('dash');
|
|
231
|
+
if (!mod) {
|
|
232
|
+
console.error(chalk.red(` ✗ Could not load dash command module`));
|
|
233
|
+
process.exit(EXIT_ERROR);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
dbg('loaded command module:', 'dash');
|
|
205
237
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
238
|
+
dbg('command returned:', cmd);
|
|
206
239
|
break;
|
|
207
240
|
}
|
|
208
241
|
|
|
209
242
|
case 'minimax': {
|
|
210
243
|
const mod = await importCommand('minimax');
|
|
244
|
+
if (!mod) {
|
|
245
|
+
console.error(chalk.red(` ✗ Could not load minimax command module`));
|
|
246
|
+
process.exit(EXIT_ERROR);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
dbg('loaded command module:', 'minimax');
|
|
211
250
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
251
|
+
dbg('command returned:', cmd);
|
|
212
252
|
break;
|
|
213
253
|
}
|
|
214
254
|
|
|
215
255
|
case 'headroom': {
|
|
216
256
|
const mod = await importCommand('headroom');
|
|
257
|
+
if (!mod) {
|
|
258
|
+
console.error(chalk.red(` ✗ Could not load headroom command module`));
|
|
259
|
+
process.exit(EXIT_ERROR);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
dbg('loaded command module:', 'headroom');
|
|
217
263
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
264
|
+
dbg('command returned:', cmd);
|
|
218
265
|
break;
|
|
219
266
|
}
|
|
220
267
|
|
|
221
268
|
case 'mod': {
|
|
222
269
|
const mod = await importCommand('mod');
|
|
270
|
+
if (!mod) {
|
|
271
|
+
console.error(chalk.red(` ✗ Could not load mod command module`));
|
|
272
|
+
process.exit(EXIT_ERROR);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
dbg('loaded command module:', 'mod');
|
|
223
276
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
277
|
+
dbg('command returned:', cmd);
|
|
224
278
|
break;
|
|
225
279
|
}
|
|
226
280
|
|
|
227
281
|
case 'artifact': {
|
|
228
282
|
const mod = await importCommand('artifact');
|
|
283
|
+
if (!mod) {
|
|
284
|
+
console.error(chalk.red(` ✗ Could not load artifact command module`));
|
|
285
|
+
process.exit(EXIT_ERROR);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
dbg('loaded command module:', 'artifact');
|
|
229
289
|
await mod.runArtifact(cmdArgs, { wantJson });
|
|
290
|
+
dbg('command returned:', cmd);
|
|
230
291
|
break;
|
|
231
292
|
}
|
|
232
293
|
|
|
233
294
|
case 'memory': {
|
|
234
295
|
const mod = await importCommand('memory');
|
|
296
|
+
if (!mod) {
|
|
297
|
+
console.error(chalk.red(` ✗ Could not load memory command module`));
|
|
298
|
+
process.exit(EXIT_ERROR);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
dbg('loaded command module:', 'memory');
|
|
235
302
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
303
|
+
dbg('command returned:', cmd);
|
|
236
304
|
break;
|
|
237
305
|
}
|
|
238
306
|
|
|
239
307
|
case 'usage': {
|
|
240
308
|
const mod = await importCommand('usage');
|
|
309
|
+
if (!mod) {
|
|
310
|
+
console.error(chalk.red(` ✗ Could not load usage command module`));
|
|
311
|
+
process.exit(EXIT_ERROR);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
dbg('loaded command module:', 'usage');
|
|
241
315
|
await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
316
|
+
dbg('command returned:', cmd);
|
|
242
317
|
break;
|
|
243
318
|
}
|
|
244
319
|
|
|
@@ -253,11 +328,19 @@ async function main() {
|
|
|
253
328
|
case 'repair':
|
|
254
329
|
case 'heads-up':
|
|
255
330
|
case 'bg':
|
|
331
|
+
case 'digest':
|
|
256
332
|
case 'browser-harness-up':
|
|
257
333
|
case 'providers':
|
|
258
334
|
case 'plan': {
|
|
259
335
|
const mod = await importCommand('util');
|
|
336
|
+
if (!mod) {
|
|
337
|
+
console.error(chalk.red(` ✗ Could not load util command module`));
|
|
338
|
+
process.exit(EXIT_ERROR);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
dbg('loaded command module:', 'util');
|
|
260
342
|
const found = await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
343
|
+
dbg('command returned:', cmd);
|
|
261
344
|
if (!found) {
|
|
262
345
|
console.error(chalk.red(` ✗ Unknown command: ${cmd}`));
|
|
263
346
|
showHelp();
|
|
@@ -277,7 +360,18 @@ async function main() {
|
|
|
277
360
|
// ── Run ───────────────────────────────────────────────────────────────────────
|
|
278
361
|
|
|
279
362
|
const thisFile = fileURLToPath(import.meta.url);
|
|
280
|
-
|
|
363
|
+
// Resolve symlinks: when invoked via a symlink (e.g. /home/drb0rk/.local/bin/bizar),
|
|
364
|
+
// process.argv[1] is the symlink path, but import.meta.url is the resolved target.
|
|
365
|
+
// Compare via realpath so main() runs regardless of how the script is invoked.
|
|
366
|
+
const { realpathSync } = await import('node:fs');
|
|
367
|
+
const resolvedArgv = (() => {
|
|
368
|
+
try {
|
|
369
|
+
return realpathSync(process.argv[1]);
|
|
370
|
+
} catch {
|
|
371
|
+
return process.argv[1];
|
|
372
|
+
}
|
|
373
|
+
})();
|
|
374
|
+
const isMainModule = resolvedArgv === thisFile;
|
|
281
375
|
if (isMainModule) {
|
|
282
376
|
await main().catch((err) => {
|
|
283
377
|
console.error(chalk.red(`bizar: ${err && err.message ? err.message : String(err)}`));
|
package/cli/commands/minimax.mjs
CHANGED
|
@@ -100,6 +100,11 @@ async function runMinimaxCommand(minimaxArgs) {
|
|
|
100
100
|
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
101
101
|
process.exit(1);
|
|
102
102
|
}
|
|
103
|
+
if (!r.data) {
|
|
104
|
+
console.error(chalk.red(' ✗ Dashboard returned no data. Is the dashboard running?'));
|
|
105
|
+
console.error(chalk.dim(` Tried: http://127.0.0.1:4321/api/minimax/status`));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
103
108
|
const s = r.data;
|
|
104
109
|
console.log('');
|
|
105
110
|
console.log(chalk.bold(' MiniMax Token Plan status'));
|
|
@@ -126,6 +131,11 @@ async function runMinimaxCommand(minimaxArgs) {
|
|
|
126
131
|
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
127
132
|
process.exit(1);
|
|
128
133
|
}
|
|
134
|
+
if (!r.data) {
|
|
135
|
+
console.error(chalk.red(' ✗ Dashboard returned no data. Is the dashboard running?'));
|
|
136
|
+
console.error(chalk.dim(` Tried: http://127.0.0.1:4321/api/minimax/remains`));
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
129
139
|
if (!r.data?.ok) {
|
|
130
140
|
console.error(chalk.red(` ✗ ${r.data?.message || r.data?.error || 'unknown'}`));
|
|
131
141
|
process.exit(1);
|
|
@@ -160,6 +170,11 @@ async function runMinimaxCommand(minimaxArgs) {
|
|
|
160
170
|
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
161
171
|
process.exit(1);
|
|
162
172
|
}
|
|
173
|
+
if (!r.data) {
|
|
174
|
+
console.error(chalk.red(' ✗ Dashboard returned no data. Is the dashboard running?'));
|
|
175
|
+
console.error(chalk.dim(` Tried: http://127.0.0.1:4321/api/minimax/test`));
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
163
178
|
const data = r.data;
|
|
164
179
|
if (!data?.ok) {
|
|
165
180
|
console.error(chalk.red(` ✗ ${data?.message || data?.error || 'unknown'}`));
|
|
@@ -195,6 +210,11 @@ async function runMinimaxCommand(minimaxArgs) {
|
|
|
195
210
|
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
196
211
|
process.exit(1);
|
|
197
212
|
}
|
|
213
|
+
if (!r.data) {
|
|
214
|
+
console.error(chalk.red(' ✗ Dashboard returned no data. Is the dashboard running?'));
|
|
215
|
+
console.error(chalk.dim(` Tried: http://127.0.0.1:4321/api/minimax/onboarding/save-key`));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
198
218
|
console.log(chalk.green(` ✓ Saved to ${r.data?.path}`));
|
|
199
219
|
console.log(` ${chalk.dim('key hint:')} ${r.data?.apiKeyHint}`);
|
|
200
220
|
return;
|
package/cli/commands/util.mjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Miscellaneous utility commands:
|
|
5
5
|
* audit, init, export, test-gate, dev-link, dev-unlink,
|
|
6
|
-
* doctor, repair, heads-up, bg, browser-harness-up, providers detect
|
|
6
|
+
* doctor, repair, heads-up, bg, browser-harness-up, providers detect,
|
|
7
|
+
* backup, restore
|
|
7
8
|
*/
|
|
8
9
|
import chalk from 'chalk';
|
|
9
10
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -130,6 +131,51 @@ export function showRepairHelp() {
|
|
|
130
131
|
`);
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
export function showBackupHelp() {
|
|
135
|
+
console.log(`
|
|
136
|
+
bizar backup — Backup BizarHarness state
|
|
137
|
+
|
|
138
|
+
Usage:
|
|
139
|
+
bizar backup [label] Create a new backup (with optional label)
|
|
140
|
+
bizar backup list List available backups
|
|
141
|
+
bizar backup verify <path> Verify a backup's integrity
|
|
142
|
+
bizar backup delete <path> Delete a backup
|
|
143
|
+
|
|
144
|
+
Description:
|
|
145
|
+
Backs up config (~/.config/bizar/, ~/.config/opencode/), memory,
|
|
146
|
+
usage logs, and optionally project-level state (.bizar/, skills/).
|
|
147
|
+
Backups are stored under ~/.local/share/bizar/backups/.
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
bizar backup
|
|
151
|
+
bizar backup "before-upgrade"
|
|
152
|
+
bizar backup list
|
|
153
|
+
bizar backup verify ~/.local/share/bizar/backups/bizar-2025-07-05-120000
|
|
154
|
+
`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function showRestoreHelp() {
|
|
158
|
+
console.log(`
|
|
159
|
+
bizar restore — Restore BizarHarness from a backup
|
|
160
|
+
|
|
161
|
+
Usage:
|
|
162
|
+
bizar restore <path> Restore from a backup
|
|
163
|
+
bizar restore <path> --dry-run Preview restore without modifying files
|
|
164
|
+
bizar restore <path> --overwrite Replace existing files (default: merge)
|
|
165
|
+
bizar restore <path> --skip Keep existing files, don't overwrite
|
|
166
|
+
|
|
167
|
+
Description:
|
|
168
|
+
Restores files from a backup directory. Default strategy is 'merge'
|
|
169
|
+
(newer files from backup overlay existing files). Use --overwrite to
|
|
170
|
+
replace everything, or --skip to leave existing files untouched.
|
|
171
|
+
|
|
172
|
+
Examples:
|
|
173
|
+
bizar restore ~/.local/share/bizar/backups/bizar-2025-07-05-120000
|
|
174
|
+
bizar restore ~/.local/share/bizar/backups/bizar-2025-07-05-120000 --dry-run
|
|
175
|
+
bizar restore ~/.local/share/bizar/backups/bizar-2025-07-05-120000 --overwrite
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
178
|
+
|
|
133
179
|
// ── Test gate ──────────────────────────────────────────────────────────────────
|
|
134
180
|
|
|
135
181
|
export async function runTestGate() {
|
|
@@ -245,6 +291,102 @@ export async function run(name, args, isHelpRequest) {
|
|
|
245
291
|
}
|
|
246
292
|
break;
|
|
247
293
|
|
|
294
|
+
case 'backup': {
|
|
295
|
+
if (isHelpRequest || args.length === 0) {
|
|
296
|
+
showBackupHelp();
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
const sub = args[0];
|
|
300
|
+
if (sub === 'list') {
|
|
301
|
+
const { listBackups } = await import('../../bizar-dash/src/server/backup-store.mjs');
|
|
302
|
+
const backups = await listBackups();
|
|
303
|
+
if (backups.length === 0) {
|
|
304
|
+
console.log(' No backups found.');
|
|
305
|
+
} else {
|
|
306
|
+
for (const b of backups) {
|
|
307
|
+
console.log(` ${b.path}`);
|
|
308
|
+
console.log(` Created: ${b.createdAt}`);
|
|
309
|
+
console.log(` Size: ${b.sizeFormatted}`);
|
|
310
|
+
if (b.manifest?.label) console.log(` Label: ${b.manifest.label}`);
|
|
311
|
+
console.log('');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
if (sub === 'verify') {
|
|
317
|
+
const path = args[1];
|
|
318
|
+
if (!path) { console.error(' Usage: bizar backup verify <path>'); process.exit(1); }
|
|
319
|
+
const { verifyBackup } = await import('../../bizar-dash/src/server/backup-store.mjs');
|
|
320
|
+
const result = await verifyBackup({ backupPath: path });
|
|
321
|
+
if (result.ok) {
|
|
322
|
+
console.log(chalk.green(' ✓ Backup is valid'));
|
|
323
|
+
} else {
|
|
324
|
+
console.error(chalk.red(' ✗ Backup has issues:'));
|
|
325
|
+
for (const issue of result.issues) console.error(` - ${issue}`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
if (sub === 'delete') {
|
|
331
|
+
const path = args[1];
|
|
332
|
+
if (!path) { console.error(' Usage: bizar backup delete <path>'); process.exit(1); }
|
|
333
|
+
const { deleteBackup } = await import('../../bizar-dash/src/server/backup-store.mjs');
|
|
334
|
+
const result = await deleteBackup({ backupPath: path });
|
|
335
|
+
if (result.ok) {
|
|
336
|
+
console.log(chalk.green(' ✓ Backup deleted'));
|
|
337
|
+
} else {
|
|
338
|
+
console.error(chalk.red(` ✗ Delete failed: ${result.error}`));
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
// Default: create backup
|
|
344
|
+
const label = sub || null;
|
|
345
|
+
const { createBackup } = await import('../../bizar-dash/src/server/backup-store.mjs');
|
|
346
|
+
const result = await createBackup({ label });
|
|
347
|
+
if (result.ok) {
|
|
348
|
+
console.log(chalk.green(' ✓ Backup created'));
|
|
349
|
+
console.log(` Path: ${result.path}`);
|
|
350
|
+
console.log(` Size: ${result.sizeBytes > 0 ? `${(result.sizeBytes / 1024).toFixed(1)} KB` : '0 B'}`);
|
|
351
|
+
console.log(` Duration: ${result.durationMs}ms`);
|
|
352
|
+
} else {
|
|
353
|
+
console.error(chalk.red(' ✗ Backup failed'));
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
case 'restore': {
|
|
360
|
+
if (isHelpRequest) {
|
|
361
|
+
showRestoreHelp();
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
const path = args[0];
|
|
365
|
+
if (!path) { showRestoreHelp(); process.exit(1); }
|
|
366
|
+
const dryRun = args.includes('--dry-run');
|
|
367
|
+
const conflictStrategy = args.includes('--overwrite') ? 'overwrite' : args.includes('--skip') ? 'skip' : 'merge';
|
|
368
|
+
const { restoreBackup } = await import('../../bizar-dash/src/server/backup-store.mjs');
|
|
369
|
+
const result = await restoreBackup({ backupPath: path, dryRun, conflictStrategy });
|
|
370
|
+
if (dryRun) {
|
|
371
|
+
console.log(' Dry-run mode — no files were modified.');
|
|
372
|
+
}
|
|
373
|
+
if (result.restored.length > 0) {
|
|
374
|
+
console.log(` Restored: ${result.restored.join(', ')}`);
|
|
375
|
+
}
|
|
376
|
+
if (result.skipped.length > 0) {
|
|
377
|
+
console.log(` Skipped: ${result.skipped.join(', ')}`);
|
|
378
|
+
}
|
|
379
|
+
if (result.errors.length > 0) {
|
|
380
|
+
console.error(chalk.red(' Errors:'));
|
|
381
|
+
for (const e of result.errors) console.error(` - ${e}`);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
if (result.ok) {
|
|
385
|
+
console.log(chalk.green(` ✓ Restore complete`));
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
|
|
248
390
|
case 'heads-up':
|
|
249
391
|
if (isHelpRequest) {
|
|
250
392
|
console.log(' heads-up <subcommand> Manage pre-push / pre-release heads-ups (list/check/archive)');
|
|
@@ -263,6 +405,17 @@ export async function run(name, args, isHelpRequest) {
|
|
|
263
405
|
}
|
|
264
406
|
break;
|
|
265
407
|
|
|
408
|
+
case 'digest':
|
|
409
|
+
// v4.8.0 — Weekly digest management
|
|
410
|
+
if (isHelpRequest) {
|
|
411
|
+
const { showDigestHelp } = await import('../digest.mjs');
|
|
412
|
+
showDigestHelp();
|
|
413
|
+
} else {
|
|
414
|
+
const { runDigest } = await import('../digest.mjs');
|
|
415
|
+
await runDigest(args[0], args.slice(1));
|
|
416
|
+
}
|
|
417
|
+
break;
|
|
418
|
+
|
|
266
419
|
case 'browser-harness-up': {
|
|
267
420
|
const { execFileSync } = await import('node:child_process');
|
|
268
421
|
const sub = args[0] || 'start';
|
package/cli/digest.mjs
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/digest.mjs
|
|
3
|
+
*
|
|
4
|
+
* v4.8.0 — `bizar digest` CLI subcommand.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* bizar digest Show latest digest
|
|
8
|
+
* bizar digest list List all digests
|
|
9
|
+
* bizar digest generate [--days N] Generate now (default 7 days)
|
|
10
|
+
* bizar digest view <path> View a specific digest
|
|
11
|
+
* bizar digest delete <path> Delete a specific digest
|
|
12
|
+
*/
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
function showDigestHelp() {
|
|
17
|
+
console.log(`
|
|
18
|
+
bizar digest — Manage weekly digests
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
bizar digest Show the latest digest
|
|
22
|
+
bizar digest list List all digests
|
|
23
|
+
bizar digest generate [--days N] Generate a digest now (default 7 days)
|
|
24
|
+
bizar digest view <path> View a specific digest
|
|
25
|
+
bizar digest delete <path> Delete a specific digest
|
|
26
|
+
bizar digest --help Show this help
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runDigest(subcommand, args) {
|
|
31
|
+
const { generateAndSave, listDigests, getDigest, deleteDigest } = await import(
|
|
32
|
+
'../bizar-dash/src/server/digest-store.mjs'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
switch (subcommand) {
|
|
36
|
+
case undefined:
|
|
37
|
+
case null:
|
|
38
|
+
case '': {
|
|
39
|
+
// Show latest digest
|
|
40
|
+
const digests = await listDigests({ limit: 1 });
|
|
41
|
+
if (digests.length === 0) {
|
|
42
|
+
console.log(chalk.yellow(' No digests yet. Run `bizar digest generate` to create one.'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const latest = digests[0];
|
|
46
|
+
const digest = await getDigest(latest.path);
|
|
47
|
+
if (!digest) {
|
|
48
|
+
console.log(chalk.red(` ✗ Digest not found at ${latest.path}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(digest.content);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case 'list': {
|
|
56
|
+
const digests = await listDigests({ limit: 50 });
|
|
57
|
+
if (digests.length === 0) {
|
|
58
|
+
console.log(chalk.yellow(' No digests yet.'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.bold(` ${digests.length} digest(s):\n`));
|
|
62
|
+
for (const d of digests) {
|
|
63
|
+
const date = new Date(d.createdAt).toLocaleDateString();
|
|
64
|
+
const size = (d.sizeBytes / 1024).toFixed(1);
|
|
65
|
+
console.log(` ${chalk.cyan(d.filename)}`);
|
|
66
|
+
console.log(` Path: ${d.path}`);
|
|
67
|
+
console.log(` Created: ${date} (${size} KB)`);
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'generate': {
|
|
74
|
+
const daysIdx = args.indexOf('--days');
|
|
75
|
+
const days = daysIdx >= 0 ? parseInt(args[daysIdx + 1], 10) || 7 : 7;
|
|
76
|
+
const dryRun = args.includes('--dry-run');
|
|
77
|
+
const now = new Date();
|
|
78
|
+
const weekEnd = now.toISOString().slice(0, 10);
|
|
79
|
+
const weekStart = new Date(now.getTime() - days * 24 * 60 * 60 * 1000)
|
|
80
|
+
.toISOString()
|
|
81
|
+
.slice(0, 10);
|
|
82
|
+
|
|
83
|
+
console.log(chalk.bold(` Generating digest for ${weekStart} → ${weekEnd}...`));
|
|
84
|
+
if (dryRun) console.log(chalk.yellow(' (dry run — will not save)'));
|
|
85
|
+
|
|
86
|
+
const result = await generateAndSave({
|
|
87
|
+
weekStart,
|
|
88
|
+
weekEnd,
|
|
89
|
+
projectRoot: process.cwd(),
|
|
90
|
+
dryRun,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (result.dryRun) {
|
|
94
|
+
console.log('\n' + result.markdown);
|
|
95
|
+
} else {
|
|
96
|
+
console.log(chalk.green(` ✓ Digest saved:`));
|
|
97
|
+
for (const p of result.saveResult.paths) {
|
|
98
|
+
console.log(` ${p}`);
|
|
99
|
+
}
|
|
100
|
+
console.log(chalk.green(` Sections: ${Object.keys(result.sections).filter(k => Array.isArray(result.sections[k]) ? result.sections[k].length > 0 : result.sections[k] !== null).length}/7 populated`));
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
case 'view': {
|
|
106
|
+
const path = args[0];
|
|
107
|
+
if (!path) {
|
|
108
|
+
console.log(chalk.red(' ✗ Usage: bizar digest view <path>'));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
if (!existsSync(path)) {
|
|
112
|
+
console.log(chalk.red(` ✗ File not found: ${path}`));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const digest = await getDigest(path);
|
|
116
|
+
if (!digest) {
|
|
117
|
+
console.log(chalk.red(` ✗ Could not read digest at ${path}`));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
console.log(digest.content);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case 'delete': {
|
|
125
|
+
const path = args[0];
|
|
126
|
+
if (!path) {
|
|
127
|
+
console.log(chalk.red(' ✗ Usage: bizar digest delete <path>'));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const result = await deleteDigest(path);
|
|
131
|
+
if (result.ok) {
|
|
132
|
+
console.log(chalk.green(` ✓ Deleted ${path}`));
|
|
133
|
+
} else {
|
|
134
|
+
console.log(chalk.red(` ✗ Delete failed: ${result.error || 'unknown'}`));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
default:
|
|
141
|
+
if (subcommand === '--help' || subcommand === '-h') {
|
|
142
|
+
showDigestHelp();
|
|
143
|
+
} else {
|
|
144
|
+
console.log(chalk.red(` ✗ Unknown digest subcommand: ${subcommand}`));
|
|
145
|
+
showDigestHelp();
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.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. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|