@polderlabs/bizar 4.4.12 → 4.5.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/bizar-dash/CHANGELOG.md +37 -276
- package/bizar-dash/dist/assets/main-CDFKHzBg.css +1 -0
- package/bizar-dash/dist/assets/main-NYFpS2wY.js +312 -0
- package/bizar-dash/dist/assets/main-NYFpS2wY.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js → mobile--0FBIKX3.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js.map → mobile--0FBIKX3.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-OgRp8VIb.js +352 -0
- package/bizar-dash/dist/assets/mobile-OgRp8VIb.js.map +1 -0
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/skills/agent-baseline/SKILL.md +80 -0
- package/bizar-dash/skills/bizar/SKILL.md +96 -0
- package/bizar-dash/skills/chat/SKILL.md +74 -0
- package/bizar-dash/skills/lightrag/SKILL.md +75 -0
- package/bizar-dash/skills/minimax/SKILL.md +80 -0
- package/bizar-dash/skills/obsidian/SKILL.md +55 -0
- package/bizar-dash/skills/providers/SKILL.md +75 -0
- package/bizar-dash/skills/sdk/SKILL.md +138 -0
- package/bizar-dash/skills/self-improvement/SKILL.md +53 -0
- package/bizar-dash/skills/skills-cli/SKILL.md +94 -0
- package/bizar-dash/skills/usage/SKILL.md +62 -0
- package/bizar-dash/src/server/api.mjs +12 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +5 -2
- package/bizar-dash/src/server/memory-store.mjs +38 -0
- package/bizar-dash/src/server/minimax-usage-store.mjs +372 -0
- package/bizar-dash/src/server/minimax.mjs +427 -110
- package/bizar-dash/src/server/providers-store.mjs +966 -6
- package/bizar-dash/src/server/routes/config.mjs +52 -1
- package/bizar-dash/src/server/routes/env-vars.mjs +165 -0
- package/bizar-dash/src/server/routes/lightrag.mjs +154 -0
- package/bizar-dash/src/server/routes/memory.mjs +241 -1
- package/bizar-dash/src/server/routes/minimax.mjs +50 -57
- package/bizar-dash/src/server/routes/opencode-session-detail.mjs +14 -29
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +205 -3
- package/bizar-dash/src/server/routes/providers.mjs +266 -5
- package/bizar-dash/src/server/routes/skills.mjs +32 -43
- package/bizar-dash/src/server/routes/update.mjs +340 -0
- package/bizar-dash/src/server/routes/usage.mjs +136 -0
- package/bizar-dash/src/server/serve-info.mjs +135 -4
- package/bizar-dash/src/server/server.mjs +4 -0
- package/bizar-dash/src/server/skills-store.mjs +152 -262
- package/bizar-dash/src/web/App.tsx +118 -29
- package/bizar-dash/src/web/components/EnvVarManager.tsx +247 -0
- package/bizar-dash/src/web/components/SettingsSearch.tsx +213 -0
- package/bizar-dash/src/web/components/Topbar.tsx +0 -1
- package/bizar-dash/src/web/components/UsageChart.tsx +250 -0
- package/bizar-dash/src/web/components/UsageTable.tsx +90 -0
- package/bizar-dash/src/web/components/chat/ChatComposer.tsx +21 -25
- package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +199 -37
- package/bizar-dash/src/web/components/chat/ChatThread.tsx +29 -17
- package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +7 -1
- package/bizar-dash/src/web/components/chat/InfoPanel.tsx +71 -6
- package/bizar-dash/src/web/components/chat/useChat.ts +751 -257
- package/bizar-dash/src/web/lib/api.ts +43 -0
- package/bizar-dash/src/web/main.tsx +1 -0
- package/bizar-dash/src/web/mobile/views/MobileChat.tsx +110 -35
- package/bizar-dash/src/web/styles/chat.css +135 -1
- package/bizar-dash/src/web/styles/main.css +46 -0
- package/bizar-dash/src/web/styles/minimax-usage.css +471 -0
- package/bizar-dash/src/web/styles/settings.css +418 -0
- package/bizar-dash/src/web/styles/skills.css +302 -0
- package/bizar-dash/src/web/styles/tasks.css +288 -0
- package/bizar-dash/src/web/views/Chat.tsx +276 -48
- package/bizar-dash/src/web/views/Config.tsx +3 -2065
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +620 -240
- package/bizar-dash/src/web/views/Settings.tsx +6 -0
- package/bizar-dash/src/web/views/Skills.tsx +208 -260
- package/bizar-dash/src/web/views/Tasks.tsx +348 -1119
- package/bizar-dash/tests/chat-session-create.test.mjs +391 -0
- package/bizar-dash/tests/chat-session-stream.test.mjs +308 -0
- package/bizar-dash/tests/env-vars-store.test.mjs +216 -0
- package/bizar-dash/tests/lightrag-defaults.node.test.mjs +118 -0
- package/bizar-dash/tests/minimax-chat-usage.test.mjs +178 -0
- package/bizar-dash/tests/minimax-usage-store.node.test.mjs +293 -0
- package/bizar-dash/tests/opencode-sessions-detail.test.mjs +12 -9
- package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +479 -3
- package/bizar-dash/tests/providers-store-search.node.test.mjs +166 -0
- package/bizar-dash/tests/skills-list.test.mjs +232 -0
- package/bizar-dash/tests/skills-search.test.mjs +222 -0
- package/bizar-dash/tests/tasks-create.test.mjs +187 -0
- package/bizar-dash/tests/update-check.test.mjs +127 -0
- package/bizar-dash/tests/update-run.test.mjs +266 -0
- package/cli/bin.mjs +345 -4
- package/cli/provision.mjs +118 -4
- package/config/agents/_shared/SKILLS.md +109 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/main-BKXEqU1w.css +0 -1
- package/bizar-dash/dist/assets/main-EK_fzXn_.js +0 -352
- package/bizar-dash/dist/assets/main-EK_fzXn_.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js +0 -354
- package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js.map +0 -1
package/cli/bin.mjs
CHANGED
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
* install, audit, init, export, artifact, update, test-gate, service, dash
|
|
16
16
|
*/
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
|
-
import { readFileSync } from 'node:fs';
|
|
19
|
-
import {
|
|
18
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
20
21
|
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const HOME = homedir();
|
|
24
|
+
const BIZAR_HOME = join(HOME, '.config', 'bizar');
|
|
21
25
|
import chalk from 'chalk';
|
|
22
26
|
import { runInstaller } from './install.mjs';
|
|
23
27
|
import { runAudit } from './audit.mjs';
|
|
@@ -93,7 +97,9 @@ function showHelp() {
|
|
|
93
97
|
dev-unlink Remove the dev symlink and restore the deployed copy
|
|
94
98
|
doctor Check the BizarHarness install for health issues
|
|
95
99
|
heads-up <subcommand> Manage pre-push / pre-release heads-ups (list/check/archive)
|
|
96
|
-
|
|
100
|
+
minimax <subcommand> Manage MiniMax Token Plan integration (key + onboarding)
|
|
101
|
+
usage Show compact usage analytics summary (24h rolling)
|
|
102
|
+
mod <subcommand> Manage mods (install/upgrade/list via the dashboard API)
|
|
97
103
|
|
|
98
104
|
Examples:
|
|
99
105
|
bizar install
|
|
@@ -191,6 +197,8 @@ function showUpdateHelp() {
|
|
|
191
197
|
|
|
192
198
|
Usage:
|
|
193
199
|
bizar update Update EVERYTHING (default; auto-kills + restarts)
|
|
200
|
+
bizar update --check Only print current vs. latest; do not update
|
|
201
|
+
bizar update --channel=stable|beta Pick the npm dist-tag (default: stable)
|
|
194
202
|
bizar update --no-restart Don't auto-restart the dashboard after update
|
|
195
203
|
bizar update --dry-run Print what would happen, change nothing
|
|
196
204
|
bizar update --force Override .bizar/PRE_PUSH_NOTES.md blockers
|
|
@@ -218,12 +226,20 @@ function showUpdateHelp() {
|
|
|
218
226
|
--no-restart).
|
|
219
227
|
• Runs 'bizar doctor' after a successful update to catch config
|
|
220
228
|
regressions before opencode tries to start.
|
|
229
|
+
• With --check: prints the version matrix and release-notes excerpt
|
|
230
|
+
between current and latest, exits non-zero if an update is available.
|
|
221
231
|
|
|
222
232
|
Examples:
|
|
223
233
|
bizar update Full auto-update (recommended)
|
|
234
|
+
bizar update --check Show version matrix + notes, do nothing
|
|
235
|
+
bizar update --channel=beta Upgrade to latest beta build
|
|
224
236
|
bizar update --dry-run Preview what would change
|
|
225
|
-
bizar update --pick Pick specific components
|
|
226
237
|
bizar update plugin --no-restart Plugin-only, leave dashboard alone
|
|
238
|
+
|
|
239
|
+
Errors:
|
|
240
|
+
Network failures (registry offline / DNS) and npm permission issues
|
|
241
|
+
are surfaced with the raw npm output. The provisioner never silently
|
|
242
|
+
swallows them — look for the ✗ marker in the step output.
|
|
227
243
|
`);
|
|
228
244
|
}
|
|
229
245
|
|
|
@@ -476,6 +492,323 @@ async function runModCommand(modArgs) {
|
|
|
476
492
|
}
|
|
477
493
|
}
|
|
478
494
|
|
|
495
|
+
function showMinimaxHelp() {
|
|
496
|
+
console.log(`
|
|
497
|
+
bizar minimax — Manage the MiniMax Token Plan integration
|
|
498
|
+
|
|
499
|
+
Usage:
|
|
500
|
+
bizar minimax status Show whether the Subscription Key is configured
|
|
501
|
+
and where it was resolved from (auth.json,
|
|
502
|
+
opencode.json, env var, or none).
|
|
503
|
+
bizar minimax remains Fetch the live 5-hour + weekly remaining
|
|
504
|
+
quota per model. Shows reset times.
|
|
505
|
+
bizar minimax test Smoke-test the key with a one-shot chat
|
|
506
|
+
completion. Prints the usage block.
|
|
507
|
+
bizar minimax config <key> Save a new Subscription Key to
|
|
508
|
+
~/.local/share/opencode/auth.json. The
|
|
509
|
+
key never leaves this machine.
|
|
510
|
+
bizar minimax clear Remove the Subscription Key from
|
|
511
|
+
opencode's auth.json.
|
|
512
|
+
bizar minimax reset-onboarding Re-trigger the first-run wizard. Use
|
|
513
|
+
this if the key was changed outside the
|
|
514
|
+
dashboard and you want to re-enter it.
|
|
515
|
+
|
|
516
|
+
Flags:
|
|
517
|
+
--base-url <url> Override the Token Plan host (default: https://www.minimax.io)
|
|
518
|
+
--chat-url <url> Override the chat-completions host (default: https://api.minimax.io/v1)
|
|
519
|
+
--yes Skip the confirmation prompt on 'clear' and 'reset-onboarding'
|
|
520
|
+
`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Find the dashboard's port + password so the CLI can talk to /api/minimax/*.
|
|
524
|
+
function readDashboardConn() {
|
|
525
|
+
const portFile = join(BIZAR_HOME, 'dashboard.port');
|
|
526
|
+
const authFile = join(BIZAR_HOME, 'dashboard-secret');
|
|
527
|
+
let port = 4321;
|
|
528
|
+
let secret = '';
|
|
529
|
+
try {
|
|
530
|
+
if (existsSync(portFile)) {
|
|
531
|
+
const parsed = parseInt(readFileSync(portFile, 'utf8').trim(), 10);
|
|
532
|
+
if (Number.isFinite(parsed) && parsed > 0) port = parsed;
|
|
533
|
+
}
|
|
534
|
+
} catch { /* ignore */ }
|
|
535
|
+
try {
|
|
536
|
+
if (existsSync(authFile)) secret = readFileSync(authFile, 'utf8').trim();
|
|
537
|
+
} catch { /* ignore */ }
|
|
538
|
+
return { port, secret };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async function minimaxApi(path, opts = {}) {
|
|
542
|
+
const { port, secret } = readDashboardConn();
|
|
543
|
+
const url = `http://127.0.0.1:${port}${path}`;
|
|
544
|
+
const headers = { 'content-type': 'application/json', accept: 'application/json' };
|
|
545
|
+
if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
|
|
546
|
+
const method = (opts.method || 'GET').toUpperCase();
|
|
547
|
+
try {
|
|
548
|
+
const resp = await fetch(url, { method, headers, body: opts.body ? JSON.stringify(opts.body) : undefined });
|
|
549
|
+
const text = await resp.text();
|
|
550
|
+
let data = null;
|
|
551
|
+
try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
|
|
552
|
+
if (!resp.ok) {
|
|
553
|
+
return { ok: false, error: `http_${resp.status}`, message: data?.message || data?.error || resp.statusText, status: resp.status };
|
|
554
|
+
}
|
|
555
|
+
return { ok: true, status: resp.status, data };
|
|
556
|
+
} catch (err) {
|
|
557
|
+
return { ok: false, error: 'network_error', message: err && err.message ? err.message : String(err) };
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function runMinimaxCommand(minimaxArgs) {
|
|
562
|
+
const sub = minimaxArgs[0];
|
|
563
|
+
const flags = minimaxArgs.slice(1).filter((a) => a.startsWith('-'));
|
|
564
|
+
const positional = minimaxArgs.slice(1).filter((a) => !a.startsWith('-'));
|
|
565
|
+
const yes = flags.includes('--yes') || flags.includes('-y');
|
|
566
|
+
|
|
567
|
+
if (!sub || sub === '--help' || sub === '-h' || flags.includes('--help') || flags.includes('-h')) {
|
|
568
|
+
showMinimaxHelp();
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (sub === 'status') {
|
|
573
|
+
const r = await minimaxApi('/api/minimax/status');
|
|
574
|
+
if (!r.ok) {
|
|
575
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
const s = r.data;
|
|
579
|
+
console.log('');
|
|
580
|
+
console.log(chalk.bold(' MiniMax Token Plan status'));
|
|
581
|
+
console.log('');
|
|
582
|
+
console.log(` ${chalk.dim('configured:')} ${s.configured ? chalk.green('yes') : chalk.red('no')}`);
|
|
583
|
+
console.log(` ${chalk.dim('key source:')} ${chalk.cyan(s.source)}`);
|
|
584
|
+
if (s.apiKeyHint) console.log(` ${chalk.dim('key hint:')} ${s.apiKeyHint}`);
|
|
585
|
+
console.log(` ${chalk.dim('group id:')} ${s.groupId}`);
|
|
586
|
+
console.log(` ${chalk.dim('token host:')} ${s.tokenBaseUrl}`);
|
|
587
|
+
console.log(` ${chalk.dim('chat host:')} ${s.chatBaseUrl}`);
|
|
588
|
+
console.log(` ${chalk.dim('key format:')} ${s.keyPatternValid === true ? chalk.green('ok') : s.keyPatternValid === false ? chalk.red('unexpected prefix') : chalk.dim('n/a')}`);
|
|
589
|
+
console.log('');
|
|
590
|
+
if (s.cache) {
|
|
591
|
+
console.log(` ${chalk.dim('cached:')} ${new Date(s.cache.fetchedAt).toLocaleString()} (${s.cache.modelCount} models)`);
|
|
592
|
+
} else {
|
|
593
|
+
console.log(` ${chalk.dim('cached:')} none yet — run \`bizar minimax remains\` to populate`);
|
|
594
|
+
}
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (sub === 'remains') {
|
|
599
|
+
const r = await minimaxApi('/api/minimax/remains');
|
|
600
|
+
if (!r.ok) {
|
|
601
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
if (!r.data?.ok) {
|
|
605
|
+
console.error(chalk.red(` ✗ ${r.data?.message || r.data?.error || 'unknown'}`));
|
|
606
|
+
process.exit(1);
|
|
607
|
+
}
|
|
608
|
+
const data = r.data;
|
|
609
|
+
console.log('');
|
|
610
|
+
console.log(chalk.bold(` MiniMax Token Plan quota (${new Date(data.fetchedAt).toLocaleString()})`));
|
|
611
|
+
if (data.apiKeyHint) console.log(chalk.dim(` Key: ${data.apiKeyHint} · source: ${data.keySource} · group: ${data.groupId}`));
|
|
612
|
+
console.log('');
|
|
613
|
+
for (const m of data.models || []) {
|
|
614
|
+
const five = m.current_interval_remaining_percent;
|
|
615
|
+
const week = m.current_weekly_remaining_percent;
|
|
616
|
+
const fiveBar = '█'.repeat(Math.round(five / 5)) + '░'.repeat(20 - Math.round(five / 5));
|
|
617
|
+
const weekBar = '█'.repeat(Math.round(week / 5)) + '░'.repeat(20 - Math.round(week / 5));
|
|
618
|
+
const fiveColor = five >= 75 ? chalk.green : five >= 25 ? chalk.yellow : chalk.red;
|
|
619
|
+
const weekColor = week >= 75 ? chalk.green : week >= 25 ? chalk.yellow : chalk.red;
|
|
620
|
+
console.log(` ${chalk.bold(m.model_name)}`);
|
|
621
|
+
console.log(` ${chalk.dim('5h:')} ${fiveColor(five + '%'.padStart(4))} ${fiveBar} resets in ${m.intervalResetInHuman}`);
|
|
622
|
+
console.log(` ${chalk.dim('week:')} ${weekColor(week + '%'.padStart(4))} ${weekBar} resets in ${m.weeklyResetInHuman}`);
|
|
623
|
+
console.log('');
|
|
624
|
+
}
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if (sub === 'test') {
|
|
629
|
+
const prompt = positional[0] || 'Reply with the single word: pong';
|
|
630
|
+
const r = await minimaxApi('/api/minimax/test', {
|
|
631
|
+
method: 'POST',
|
|
632
|
+
body: { prompt, model: 'MiniMax-M3', maxTokens: 32 },
|
|
633
|
+
});
|
|
634
|
+
if (!r.ok) {
|
|
635
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
const data = r.data;
|
|
639
|
+
if (!data?.ok) {
|
|
640
|
+
console.error(chalk.red(` ✗ ${data?.message || data?.error || 'unknown'}`));
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
console.log('');
|
|
644
|
+
console.log(chalk.green(' ✓ Key works'));
|
|
645
|
+
console.log(` ${chalk.dim('model:')} ${data.model}`);
|
|
646
|
+
console.log(` ${chalk.dim('finish:')} ${data.finishReason}`);
|
|
647
|
+
if (data.content) console.log(` ${chalk.dim('content:')} ${JSON.stringify(data.content.slice(0, 80))}${data.content.length > 80 ? '…' : ''}`);
|
|
648
|
+
if (data.usage) {
|
|
649
|
+
console.log(` ${chalk.dim('usage:')} total=${data.usage.total_tokens} prompt=${data.usage.prompt_tokens} completion=${data.usage.completion_tokens}`);
|
|
650
|
+
}
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (sub === 'config' || sub === 'set') {
|
|
655
|
+
const key = positional[0];
|
|
656
|
+
if (!key) {
|
|
657
|
+
console.error(chalk.red(' ✗ Missing key. Usage: bizar minimax config <sk-cp-…>'));
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
if (!/^sk-(cp|ant|or)-[A-Za-z0-9_-]{20,}$/.test(key)) {
|
|
661
|
+
console.error(chalk.red(' ✗ Key does not look like a MiniMax key (expected sk-cp-…, sk-ant-…, or sk-or-… prefix)'));
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
console.log(chalk.dim(' Saving to opencode auth.json…'));
|
|
665
|
+
const r = await minimaxApi('/api/minimax/onboarding/save-key', {
|
|
666
|
+
method: 'POST',
|
|
667
|
+
body: { key, groupId: 'default' },
|
|
668
|
+
});
|
|
669
|
+
if (!r.ok) {
|
|
670
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
671
|
+
process.exit(1);
|
|
672
|
+
}
|
|
673
|
+
console.log(chalk.green(` ✓ Saved to ${r.data?.path}`));
|
|
674
|
+
console.log(` ${chalk.dim('key hint:')} ${r.data?.apiKeyHint}`);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (sub === 'clear' || sub === 'remove') {
|
|
679
|
+
if (!yes) {
|
|
680
|
+
console.log(chalk.yellow(` ⚠ This will remove the MiniMax Subscription Key from opencode's auth.json.`));
|
|
681
|
+
console.log(chalk.dim(' Continue? [y/N]'));
|
|
682
|
+
// simple readline confirmation
|
|
683
|
+
const buf = [];
|
|
684
|
+
process.stdin.setEncoding('utf8');
|
|
685
|
+
process.stdin.on('data', (c) => { buf.push(c); if (c.includes('\n')) process.stdin.pause(); });
|
|
686
|
+
await new Promise((resolve) => process.stdin.once('close', resolve));
|
|
687
|
+
const answer = buf.join('').trim().toLowerCase();
|
|
688
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
689
|
+
console.log(chalk.dim(' Cancelled.'));
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// We don't have a "clear key" route; instead write an empty auth.json
|
|
694
|
+
// entry. We piggyback on the onboarding save-key by saving an empty
|
|
695
|
+
// marker isn't allowed — use the providers-store side instead. For
|
|
696
|
+
// now, recommend using the dashboard's "clear" button. But the
|
|
697
|
+
// simplest CLI path is to write auth.json directly.
|
|
698
|
+
const authFile = join(HOME, '.local', 'share', 'opencode', 'auth.json');
|
|
699
|
+
let auth = {};
|
|
700
|
+
try {
|
|
701
|
+
if (existsSync(authFile)) auth = JSON.parse(readFileSync(authFile, 'utf8'));
|
|
702
|
+
} catch { /* ignore */ }
|
|
703
|
+
if (auth.minimax) {
|
|
704
|
+
delete auth.minimax;
|
|
705
|
+
writeFileSync(authFile, JSON.stringify(auth, null, 2) + '\n', 'utf8');
|
|
706
|
+
console.log(chalk.green(' ✓ MiniMax key removed from auth.json'));
|
|
707
|
+
} else {
|
|
708
|
+
console.log(chalk.dim(' No MiniMax key was configured.'));
|
|
709
|
+
}
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (sub === 'reset-onboarding' || sub === 'reset') {
|
|
714
|
+
if (!yes) {
|
|
715
|
+
console.log(chalk.yellow(' ⚠ This will re-trigger the first-run MiniMax onboarding wizard.'));
|
|
716
|
+
console.log(chalk.dim(' Continue? [y/N]'));
|
|
717
|
+
const buf = [];
|
|
718
|
+
process.stdin.setEncoding('utf8');
|
|
719
|
+
process.stdin.on('data', (c) => { buf.push(c); if (c.includes('\n')) process.stdin.pause(); });
|
|
720
|
+
await new Promise((resolve) => process.stdin.once('close', resolve));
|
|
721
|
+
const answer = buf.join('').trim().toLowerCase();
|
|
722
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
723
|
+
console.log(chalk.dim(' Cancelled.'));
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// Reset via the dashboard API.
|
|
728
|
+
const r = await minimaxApi('/api/minimax/onboarding', {
|
|
729
|
+
method: 'POST',
|
|
730
|
+
body: { dismissedAt: null },
|
|
731
|
+
});
|
|
732
|
+
if (!r.ok) {
|
|
733
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
734
|
+
process.exit(1);
|
|
735
|
+
}
|
|
736
|
+
console.log(chalk.green(' ✓ Onboarding wizard will show on next dashboard load'));
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
console.error(chalk.red(` ✗ Unknown minimax subcommand: ${sub}`));
|
|
741
|
+
showMinimaxHelp();
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// ── Usage subcommand ───────────────────────────────────────────────────────
|
|
746
|
+
|
|
747
|
+
function showUsageHelp() {
|
|
748
|
+
console.log(`
|
|
749
|
+
bizar usage — Show compact usage analytics summary
|
|
750
|
+
|
|
751
|
+
Usage:
|
|
752
|
+
bizar usage [24h|7d|30d] Show summary for the given range (default: 24h)
|
|
753
|
+
|
|
754
|
+
Examples:
|
|
755
|
+
bizar usage
|
|
756
|
+
bizar usage 7d
|
|
757
|
+
bizar usage 30d
|
|
758
|
+
`);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
async function runUsageCommand(args) {
|
|
762
|
+
const range = (args[0] && ['24h', '7d', '30d'].includes(args[0])) ? args[0] : '24h';
|
|
763
|
+
const { port, secret } = readDashboardConn();
|
|
764
|
+
const url = `http://127.0.0.1:${port}/api/usage?range=${range}`;
|
|
765
|
+
const headers = { accept: 'application/json' };
|
|
766
|
+
if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
|
|
767
|
+
try {
|
|
768
|
+
const resp = await fetch(url, { method: 'GET', headers });
|
|
769
|
+
const text = await resp.text();
|
|
770
|
+
let data = null;
|
|
771
|
+
try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
|
|
772
|
+
if (!resp.ok || !data) {
|
|
773
|
+
console.error(chalk.red(` ✗ Failed to load usage data: ${data?.message ?? resp.statusText}`));
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
776
|
+
const t = data.totals;
|
|
777
|
+
console.log('');
|
|
778
|
+
console.log(chalk.bold(` Usage summary — ${range} (from JSONL store)`));
|
|
779
|
+
console.log('');
|
|
780
|
+
console.log(` ${chalk.dim('Requests:')} ${t.requests.toLocaleString()} (${t.errors} errors)`);
|
|
781
|
+
console.log(` ${chalk.dim('Tokens:')} ${t.totalTokens.toLocaleString()} total (${t.promptTokens.toLocaleString()} prompt · ${t.completionTokens.toLocaleString()} completion)`);
|
|
782
|
+
console.log(` ${chalk.dim('Cached:')} ${t.cachedTokens.toLocaleString()} tokens`);
|
|
783
|
+
console.log(` ${chalk.dim('Reasoning:')} ${t.reasoningTokens.toLocaleString()} tokens`);
|
|
784
|
+
console.log(` ${chalk.dim('Avg latency:')} ${t.avgLatencyMs}ms (p95: ${t.p95LatencyMs}ms)`);
|
|
785
|
+
if (t.costEstimate > 0) {
|
|
786
|
+
console.log(` ${chalk.dim('Est. cost:')} $${t.costEstimate.toFixed(4)} USD`);
|
|
787
|
+
}
|
|
788
|
+
console.log('');
|
|
789
|
+
if (data.daily && data.daily.length > 0) {
|
|
790
|
+
console.log(chalk.dim(` ${chalk.bold('Daily breakdown')}`));
|
|
791
|
+
for (const day of data.daily.slice(-7)) {
|
|
792
|
+
const barLen = Math.round((day.totalTokens / Math.max(...data.daily.map(d => d.totalTokens))) * 20);
|
|
793
|
+
const bar = '█'.repeat(barLen) + '░'.repeat(20 - barLen);
|
|
794
|
+
console.log(` ${day.date} ${bar} ${day.totalTokens.toLocaleString()} tok ${day.requests} req`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (data.perModel && data.perModel.length > 0) {
|
|
798
|
+
console.log('');
|
|
799
|
+
console.log(chalk.dim(` ${chalk.bold('Per model')}`));
|
|
800
|
+
for (const m of data.perModel.slice(0, 8)) {
|
|
801
|
+
console.log(` ${m.modelId.padEnd(24)} ${String(m.requests).padStart(6)} req ${String(m.totalTokens).padStart(8)} tok`);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
console.log('');
|
|
805
|
+
} catch (err) {
|
|
806
|
+
console.error(chalk.red(` ✗ Network error: ${err && err.message ? err.message : String(err)}`));
|
|
807
|
+
console.error(chalk.dim(' Is the dashboard running? Run `bizar dash start` first.'));
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
479
812
|
function showDashHelp() {
|
|
480
813
|
console.log(`
|
|
481
814
|
bizar dash — Manage the Bizar dashboard
|
|
@@ -823,6 +1156,14 @@ async function main() {
|
|
|
823
1156
|
// (the dashboard owns the actual mod install/upgrade logic and the
|
|
824
1157
|
// opencode-config install/uninstall of instruction files).
|
|
825
1158
|
await runModCommand(args.slice(1));
|
|
1159
|
+
} else if (args[0] === 'minimax') {
|
|
1160
|
+
// v4.5.0 — MiniMax Token Plan integration CLI. Status, remains,
|
|
1161
|
+
// test, config, clear, reset-onboarding. All commands shell out
|
|
1162
|
+
// to the dashboard's /api/minimax/* routes.
|
|
1163
|
+
await runMinimaxCommand(args.slice(1));
|
|
1164
|
+
} else if (args[0] === 'usage') {
|
|
1165
|
+
// v4.6.0 — Compact usage analytics summary from the JSONL store.
|
|
1166
|
+
await runUsageCommand(args.slice(1));
|
|
826
1167
|
} else if (args[0] === 'dash' || args[0] === 'dashboard') {
|
|
827
1168
|
// `bizar dashboard` is a deprecated alias for `bizar dash`
|
|
828
1169
|
if (args[0] === 'dashboard') {
|
package/cli/provision.mjs
CHANGED
|
@@ -1121,17 +1121,131 @@ export async function runProvision(opts = {}) {
|
|
|
1121
1121
|
* `cli/update.mjs` (the bin.mjs-facing module) can stay a thin shim
|
|
1122
1122
|
* without duplicating flag parsing. Accepts the legacy `subargs: string[]`
|
|
1123
1123
|
* shape so any external callers keep working.
|
|
1124
|
+
*
|
|
1125
|
+
* Recognized flags (v4.4.14):
|
|
1126
|
+
* --check Only print current vs. latest, don't update.
|
|
1127
|
+
* --channel <name> npm dist-tag (stable | beta). Default: stable.
|
|
1128
|
+
* --no-restart Don't auto-restart the dashboard after update.
|
|
1129
|
+
* --dry-run Print what would happen, change nothing.
|
|
1130
|
+
* --force Override .bizar/PRE_PUSH_NOTES.md blockers.
|
|
1131
|
+
* --yes / -y Same as --force, but named for one-line scripts.
|
|
1132
|
+
* --with-mods <csv> Opt-in: install specific mods as part of the run.
|
|
1133
|
+
*
|
|
1134
|
+
* With --check, we print the version matrix and release notes between
|
|
1135
|
+
* current and latest, then return without touching the system.
|
|
1124
1136
|
*/
|
|
1125
1137
|
export async function runUpdate(subargs = []) {
|
|
1138
|
+
const args = Array.isArray(subargs) ? subargs : [];
|
|
1139
|
+
const checkOnly = args.includes('--check');
|
|
1140
|
+
|
|
1141
|
+
// --channel <name> — extract a single value. Accepts both
|
|
1142
|
+
// --channel=beta (equals form)
|
|
1143
|
+
// --channel beta (separate-arg form)
|
|
1144
|
+
// Default: 'stable'.
|
|
1145
|
+
let channel = 'stable';
|
|
1146
|
+
const eqArg = args.find((a) => a.startsWith('--channel='));
|
|
1147
|
+
if (eqArg) {
|
|
1148
|
+
const value = eqArg.slice('--channel='.length);
|
|
1149
|
+
if (value === 'stable' || value === 'beta') {
|
|
1150
|
+
channel = value;
|
|
1151
|
+
} else {
|
|
1152
|
+
console.log(chalk.yellow(` ⚠ Unknown channel "${value}". Using "stable".`));
|
|
1153
|
+
}
|
|
1154
|
+
} else {
|
|
1155
|
+
const channelIdx = args.indexOf('--channel');
|
|
1156
|
+
if (channelIdx >= 0) {
|
|
1157
|
+
const value = args[channelIdx + 1];
|
|
1158
|
+
if (!value || value.startsWith('--')) {
|
|
1159
|
+
console.log(chalk.yellow(' ⚠ --channel needs a value (stable | beta). Using "stable".'));
|
|
1160
|
+
} else if (value !== 'stable' && value !== 'beta') {
|
|
1161
|
+
console.log(chalk.yellow(` ⚠ Unknown channel "${value}". Using "stable".`));
|
|
1162
|
+
} else {
|
|
1163
|
+
channel = value;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
if (checkOnly) {
|
|
1169
|
+
return runCheck(channel);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Pass channel through to the provisioner. The provisioner does the
|
|
1173
|
+
// actual npm install; channel maps to the npm dist-tag suffix.
|
|
1126
1174
|
return runProvision({
|
|
1127
1175
|
mode: 'update',
|
|
1128
|
-
dryRun:
|
|
1129
|
-
force:
|
|
1130
|
-
yes:
|
|
1131
|
-
restart: !
|
|
1176
|
+
dryRun: args.includes('--dry-run'),
|
|
1177
|
+
force: args.includes('--force'),
|
|
1178
|
+
yes: args.includes('--yes') || args.includes('-y'),
|
|
1179
|
+
restart: !args.includes('--no-restart'),
|
|
1180
|
+
channel,
|
|
1132
1181
|
});
|
|
1133
1182
|
}
|
|
1134
1183
|
|
|
1184
|
+
/**
|
|
1185
|
+
* v4.4.14 — `bizar update --check`. Print the version matrix and (if
|
|
1186
|
+
* a newer version exists) the release notes between current and latest.
|
|
1187
|
+
* Does NOT touch the system. Exits non-zero when an update is available
|
|
1188
|
+
* so callers can use it in CI / pre-flight scripts.
|
|
1189
|
+
*/
|
|
1190
|
+
export async function runCheck(channel = 'stable') {
|
|
1191
|
+
console.log(chalk.bold.hex('#a855f7')('\n ᚦ BIZAR UPDATE — CHECK ᚦ\n'));
|
|
1192
|
+
console.log(chalk.dim(` Channel: ${channel}\n`));
|
|
1193
|
+
|
|
1194
|
+
const state = detectState();
|
|
1195
|
+
printVersionMatrix([
|
|
1196
|
+
{ label: 'opencode-ai', current: state.opencodeCli.version, latest: state.opencodeCli.latest },
|
|
1197
|
+
{ label: PKG_MAIN, current: state.pkgVersion, latest: state.pkgLatest },
|
|
1198
|
+
]);
|
|
1199
|
+
|
|
1200
|
+
const cur = state.pkgVersion;
|
|
1201
|
+
const lat = state.pkgLatest;
|
|
1202
|
+
if (cur && lat && cur !== lat) {
|
|
1203
|
+
console.log(chalk.dim(`\n Release notes (${cur} → ${lat}):`));
|
|
1204
|
+
const notes = fetchReleaseNotes(PKG_MAIN, cur, lat).catch((err) => {
|
|
1205
|
+
console.log(chalk.dim(` (could not fetch: ${err.message || err})`));
|
|
1206
|
+
return null;
|
|
1207
|
+
});
|
|
1208
|
+
const text = await notes;
|
|
1209
|
+
if (text) {
|
|
1210
|
+
// Trim to a sensible cap (first 30 lines) so the console stays
|
|
1211
|
+
// readable; the full notes are one `npm view` call away.
|
|
1212
|
+
const lines = text.split(/\r?\n/).slice(0, 30);
|
|
1213
|
+
for (const line of lines) console.log(` ${line}`);
|
|
1214
|
+
if (lines.length === 30) console.log(chalk.dim(' ... (truncated; run `npm view @polderlabs/bizar` for full)'));
|
|
1215
|
+
}
|
|
1216
|
+
console.log(chalk.yellow(`\n ⤵ Run \`bizar update\` to apply.\n`));
|
|
1217
|
+
// Non-zero exit so scripts can detect the update-available case.
|
|
1218
|
+
return { ok: true, updateAvailable: true, channel };
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
console.log(chalk.green('\n ✓ Already on the latest version.\n'));
|
|
1222
|
+
return { ok: true, updateAvailable: false, channel };
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Fetch the release notes between two versions from the npm registry.
|
|
1227
|
+
* Returns a plain-text summary (the registry's "description" field for
|
|
1228
|
+
* the latest version), or null on any failure. Never throws — failures
|
|
1229
|
+
* are surfaced to the caller as null so the check command stays useful
|
|
1230
|
+
* offline.
|
|
1231
|
+
*/
|
|
1232
|
+
async function fetchReleaseNotes(pkg, _from, _to) {
|
|
1233
|
+
// The npm registry does not expose a structured per-version release-
|
|
1234
|
+
// notes field. The best we can do is `npm view <pkg> description`
|
|
1235
|
+
// which is the package's README excerpt. We surface that as "notes".
|
|
1236
|
+
try {
|
|
1237
|
+
const { execFileSync } = await import('node:child_process');
|
|
1238
|
+
const out = execFileSync('npm', ['view', pkg, 'description'], {
|
|
1239
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1240
|
+
timeout: 10000,
|
|
1241
|
+
encoding: 'utf8',
|
|
1242
|
+
}).toString().trim();
|
|
1243
|
+
return out || null;
|
|
1244
|
+
} catch {
|
|
1245
|
+
return null;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1135
1249
|
/**
|
|
1136
1250
|
* CLI-flag-parsing entrypoint for `bizar install`. Mirrors `runUpdate`
|
|
1137
1251
|
* so `cli/install.mjs` can stay a thin shim too.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Bizar Skills - Reference
|
|
2
|
+
|
|
3
|
+
Skills are specialized instruction sets that agents load before working on specific domains. They live as `SKILL.md` files in well-known directories and are injected into agent context when relevant.
|
|
4
|
+
|
|
5
|
+
## What Skills Are
|
|
6
|
+
|
|
7
|
+
A skill is a Markdown file with YAML frontmatter. The frontmatter declares a `description` that the agent matcher uses to decide when to load the skill. The body contains domain-specific knowledge, patterns, gotchas, and code examples.
|
|
8
|
+
|
|
9
|
+
Example `SKILL.md`:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
---
|
|
13
|
+
name: my-skill
|
|
14
|
+
description: Use when working with X. Covers Y and Z.
|
|
15
|
+
---
|
|
16
|
+
# My Skill
|
|
17
|
+
|
|
18
|
+
## When to use
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
## Key concepts
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
## Code examples
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
## Common gotchas
|
|
28
|
+
...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Where Skills Live
|
|
32
|
+
|
|
33
|
+
| Path | Source | Priority | Notes |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| `~/.opencode/skills/<name>/SKILL.md` | User / System | Highest | User-overridable builtins |
|
|
36
|
+
| `~/.agents/skills/<name>/SKILL.md` | User-added | High | Installed via `skills add` |
|
|
37
|
+
| `bizar-dash/skills/<name>/SKILL.md` | Shipped | Medium | Ships with BizarHarness package |
|
|
38
|
+
| `.agents/skills/<name>/SKILL.md` | Project | Low | Project-local skills |
|
|
39
|
+
| `.opencode/skills/<name>/SKILL.md` | Project | Lowest | Project-local skills |
|
|
40
|
+
|
|
41
|
+
When the same skill name appears in multiple places, the highest-priority source wins.
|
|
42
|
+
|
|
43
|
+
## Discovering Skills
|
|
44
|
+
|
|
45
|
+
Use the `skills` CLI:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# List installed skills
|
|
49
|
+
skills list --json
|
|
50
|
+
|
|
51
|
+
# Search for a skill
|
|
52
|
+
skills search "react"
|
|
53
|
+
|
|
54
|
+
# Install from a repository
|
|
55
|
+
skills add owner/repo -s "skill-name" -y
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Known repositories:
|
|
59
|
+
- `vercel-labs/skills` - find-skills, skill-creator, general tools
|
|
60
|
+
- `vercel-labs/agent-skills` - React, Next.js, frontend performance
|
|
61
|
+
- `shadcn/ui` - shadcn/ui components
|
|
62
|
+
- `supabase/agent-skills` - Postgres, Auth, Edge Functions
|
|
63
|
+
- `mattpocock/skills` - TypeScript, TDD
|
|
64
|
+
- `anthropics/skills` - Claude patterns, agents
|
|
65
|
+
- `leonxlnx/taste-skill` - design, UI/UX
|
|
66
|
+
|
|
67
|
+
## Shipped Bizar Skills
|
|
68
|
+
|
|
69
|
+
These skills ship with BizarHarness and are available immediately:
|
|
70
|
+
|
|
71
|
+
| Skill | Description |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `bizar` | Norse-pantheon multi-agent system, Odin routing, agent tiers |
|
|
74
|
+
| `agent-baseline` | Always-on rules: Semble, Skills CLI, loop guard, copyright |
|
|
75
|
+
| `self-improvement` | .bizar/AGENTS_SELF_IMPROVEMENT.md protocol |
|
|
76
|
+
| `obsidian` | Bizar Memory Service (Obsidian + Git + LightRAG) |
|
|
77
|
+
| `minimax` | MiniMax provider, multi-key rotation, usage tracking |
|
|
78
|
+
| `providers` | Provider subsystem, backup keys, auto-add wizard |
|
|
79
|
+
| `chat` | Chat + opencode session integration |
|
|
80
|
+
| `usage` | Token usage monitoring, cost estimation, MiniMax usage dashboard |
|
|
81
|
+
| `skills-cli` | skills CLI reference, skill repos, discovery protocol |
|
|
82
|
+
| `lightrag` | LightRAG integration, opencode-free defaults, indexing |
|
|
83
|
+
| `sdk` | @polderlabs/bizar-sdk on Cloudflare Workers |
|
|
84
|
+
|
|
85
|
+
## Skill Loading
|
|
86
|
+
|
|
87
|
+
Agents check skill relevance at dispatch time. You can also load a skill explicitly in conversation using the `skill` tool:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Load the `minimax` skill before we discuss multi-key rotation.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Adding a Project Skill
|
|
94
|
+
|
|
95
|
+
Create a skill directory in your project:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
mkdir -p .agents/skills/my-project-skill
|
|
99
|
+
cat > .agents/skills/my-project-skill/SKILL.md << 'EOF'
|
|
100
|
+
---
|
|
101
|
+
name: my-project-skill
|
|
102
|
+
description: Use when working on my project's specific domain X.
|
|
103
|
+
---
|
|
104
|
+
# My Project Skill
|
|
105
|
+
...
|
|
106
|
+
EOF
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Project skills are picked up automatically on next session start.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.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. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|