@polderlabs/bizar 4.4.12 → 4.4.13
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-BB5mJurD.js +352 -0
- package/bizar-dash/dist/assets/main-BB5mJurD.js.map +1 -0
- package/bizar-dash/dist/assets/{main-BKXEqU1w.css → main-BsnQLXdh.css} +1 -1
- package/bizar-dash/dist/index.html +2 -2
- package/bizar-dash/src/server/minimax.mjs +231 -105
- package/bizar-dash/src/server/providers-store.mjs +10 -6
- package/bizar-dash/src/server/routes/minimax.mjs +50 -57
- package/bizar-dash/src/web/styles/minimax-usage.css +136 -0
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +387 -22
- package/cli/bin.mjs +263 -3
- package/package.json +1 -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/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,8 @@ 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
|
+
mod <subcommand> Manage mods (install/upgrade/list via the dashboard API)
|
|
97
102
|
|
|
98
103
|
Examples:
|
|
99
104
|
bizar install
|
|
@@ -476,6 +481,256 @@ async function runModCommand(modArgs) {
|
|
|
476
481
|
}
|
|
477
482
|
}
|
|
478
483
|
|
|
484
|
+
function showMinimaxHelp() {
|
|
485
|
+
console.log(`
|
|
486
|
+
bizar minimax — Manage the MiniMax Token Plan integration
|
|
487
|
+
|
|
488
|
+
Usage:
|
|
489
|
+
bizar minimax status Show whether the Subscription Key is configured
|
|
490
|
+
and where it was resolved from (auth.json,
|
|
491
|
+
opencode.json, env var, or none).
|
|
492
|
+
bizar minimax remains Fetch the live 5-hour + weekly remaining
|
|
493
|
+
quota per model. Shows reset times.
|
|
494
|
+
bizar minimax test Smoke-test the key with a one-shot chat
|
|
495
|
+
completion. Prints the usage block.
|
|
496
|
+
bizar minimax config <key> Save a new Subscription Key to
|
|
497
|
+
~/.local/share/opencode/auth.json. The
|
|
498
|
+
key never leaves this machine.
|
|
499
|
+
bizar minimax clear Remove the Subscription Key from
|
|
500
|
+
opencode's auth.json.
|
|
501
|
+
bizar minimax reset-onboarding Re-trigger the first-run wizard. Use
|
|
502
|
+
this if the key was changed outside the
|
|
503
|
+
dashboard and you want to re-enter it.
|
|
504
|
+
|
|
505
|
+
Flags:
|
|
506
|
+
--base-url <url> Override the Token Plan host (default: https://www.minimax.io)
|
|
507
|
+
--chat-url <url> Override the chat-completions host (default: https://api.minimax.io/v1)
|
|
508
|
+
--yes Skip the confirmation prompt on 'clear' and 'reset-onboarding'
|
|
509
|
+
`);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Find the dashboard's port + password so the CLI can talk to /api/minimax/*.
|
|
513
|
+
function readDashboardConn() {
|
|
514
|
+
const portFile = join(BIZAR_HOME, 'dashboard.port');
|
|
515
|
+
const authFile = join(BIZAR_HOME, 'dashboard-secret');
|
|
516
|
+
let port = 4321;
|
|
517
|
+
let secret = '';
|
|
518
|
+
try {
|
|
519
|
+
if (existsSync(portFile)) {
|
|
520
|
+
const parsed = parseInt(readFileSync(portFile, 'utf8').trim(), 10);
|
|
521
|
+
if (Number.isFinite(parsed) && parsed > 0) port = parsed;
|
|
522
|
+
}
|
|
523
|
+
} catch { /* ignore */ }
|
|
524
|
+
try {
|
|
525
|
+
if (existsSync(authFile)) secret = readFileSync(authFile, 'utf8').trim();
|
|
526
|
+
} catch { /* ignore */ }
|
|
527
|
+
return { port, secret };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function minimaxApi(path, opts = {}) {
|
|
531
|
+
const { port, secret } = readDashboardConn();
|
|
532
|
+
const url = `http://127.0.0.1:${port}${path}`;
|
|
533
|
+
const headers = { 'content-type': 'application/json', accept: 'application/json' };
|
|
534
|
+
if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
|
|
535
|
+
const method = (opts.method || 'GET').toUpperCase();
|
|
536
|
+
try {
|
|
537
|
+
const resp = await fetch(url, { method, headers, body: opts.body ? JSON.stringify(opts.body) : undefined });
|
|
538
|
+
const text = await resp.text();
|
|
539
|
+
let data = null;
|
|
540
|
+
try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
|
|
541
|
+
if (!resp.ok) {
|
|
542
|
+
return { ok: false, error: `http_${resp.status}`, message: data?.message || data?.error || resp.statusText, status: resp.status };
|
|
543
|
+
}
|
|
544
|
+
return { ok: true, status: resp.status, data };
|
|
545
|
+
} catch (err) {
|
|
546
|
+
return { ok: false, error: 'network_error', message: err && err.message ? err.message : String(err) };
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async function runMinimaxCommand(minimaxArgs) {
|
|
551
|
+
const sub = minimaxArgs[0];
|
|
552
|
+
const flags = minimaxArgs.slice(1).filter((a) => a.startsWith('-'));
|
|
553
|
+
const positional = minimaxArgs.slice(1).filter((a) => !a.startsWith('-'));
|
|
554
|
+
const yes = flags.includes('--yes') || flags.includes('-y');
|
|
555
|
+
|
|
556
|
+
if (!sub || sub === '--help' || sub === '-h' || flags.includes('--help') || flags.includes('-h')) {
|
|
557
|
+
showMinimaxHelp();
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (sub === 'status') {
|
|
562
|
+
const r = await minimaxApi('/api/minimax/status');
|
|
563
|
+
if (!r.ok) {
|
|
564
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
565
|
+
process.exit(1);
|
|
566
|
+
}
|
|
567
|
+
const s = r.data;
|
|
568
|
+
console.log('');
|
|
569
|
+
console.log(chalk.bold(' MiniMax Token Plan status'));
|
|
570
|
+
console.log('');
|
|
571
|
+
console.log(` ${chalk.dim('configured:')} ${s.configured ? chalk.green('yes') : chalk.red('no')}`);
|
|
572
|
+
console.log(` ${chalk.dim('key source:')} ${chalk.cyan(s.source)}`);
|
|
573
|
+
if (s.apiKeyHint) console.log(` ${chalk.dim('key hint:')} ${s.apiKeyHint}`);
|
|
574
|
+
console.log(` ${chalk.dim('group id:')} ${s.groupId}`);
|
|
575
|
+
console.log(` ${chalk.dim('token host:')} ${s.tokenBaseUrl}`);
|
|
576
|
+
console.log(` ${chalk.dim('chat host:')} ${s.chatBaseUrl}`);
|
|
577
|
+
console.log(` ${chalk.dim('key format:')} ${s.keyPatternValid === true ? chalk.green('ok') : s.keyPatternValid === false ? chalk.red('unexpected prefix') : chalk.dim('n/a')}`);
|
|
578
|
+
console.log('');
|
|
579
|
+
if (s.cache) {
|
|
580
|
+
console.log(` ${chalk.dim('cached:')} ${new Date(s.cache.fetchedAt).toLocaleString()} (${s.cache.modelCount} models)`);
|
|
581
|
+
} else {
|
|
582
|
+
console.log(` ${chalk.dim('cached:')} none yet — run \`bizar minimax remains\` to populate`);
|
|
583
|
+
}
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (sub === 'remains') {
|
|
588
|
+
const r = await minimaxApi('/api/minimax/remains');
|
|
589
|
+
if (!r.ok) {
|
|
590
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
591
|
+
process.exit(1);
|
|
592
|
+
}
|
|
593
|
+
if (!r.data?.ok) {
|
|
594
|
+
console.error(chalk.red(` ✗ ${r.data?.message || r.data?.error || 'unknown'}`));
|
|
595
|
+
process.exit(1);
|
|
596
|
+
}
|
|
597
|
+
const data = r.data;
|
|
598
|
+
console.log('');
|
|
599
|
+
console.log(chalk.bold(` MiniMax Token Plan quota (${new Date(data.fetchedAt).toLocaleString()})`));
|
|
600
|
+
if (data.apiKeyHint) console.log(chalk.dim(` Key: ${data.apiKeyHint} · source: ${data.keySource} · group: ${data.groupId}`));
|
|
601
|
+
console.log('');
|
|
602
|
+
for (const m of data.models || []) {
|
|
603
|
+
const five = m.current_interval_remaining_percent;
|
|
604
|
+
const week = m.current_weekly_remaining_percent;
|
|
605
|
+
const fiveBar = '█'.repeat(Math.round(five / 5)) + '░'.repeat(20 - Math.round(five / 5));
|
|
606
|
+
const weekBar = '█'.repeat(Math.round(week / 5)) + '░'.repeat(20 - Math.round(week / 5));
|
|
607
|
+
const fiveColor = five >= 75 ? chalk.green : five >= 25 ? chalk.yellow : chalk.red;
|
|
608
|
+
const weekColor = week >= 75 ? chalk.green : week >= 25 ? chalk.yellow : chalk.red;
|
|
609
|
+
console.log(` ${chalk.bold(m.model_name)}`);
|
|
610
|
+
console.log(` ${chalk.dim('5h:')} ${fiveColor(five + '%'.padStart(4))} ${fiveBar} resets in ${m.intervalResetInHuman}`);
|
|
611
|
+
console.log(` ${chalk.dim('week:')} ${weekColor(week + '%'.padStart(4))} ${weekBar} resets in ${m.weeklyResetInHuman}`);
|
|
612
|
+
console.log('');
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (sub === 'test') {
|
|
618
|
+
const prompt = positional[0] || 'Reply with the single word: pong';
|
|
619
|
+
const r = await minimaxApi('/api/minimax/test', {
|
|
620
|
+
method: 'POST',
|
|
621
|
+
body: { prompt, model: 'MiniMax-M3', maxTokens: 32 },
|
|
622
|
+
});
|
|
623
|
+
if (!r.ok) {
|
|
624
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
const data = r.data;
|
|
628
|
+
if (!data?.ok) {
|
|
629
|
+
console.error(chalk.red(` ✗ ${data?.message || data?.error || 'unknown'}`));
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
console.log('');
|
|
633
|
+
console.log(chalk.green(' ✓ Key works'));
|
|
634
|
+
console.log(` ${chalk.dim('model:')} ${data.model}`);
|
|
635
|
+
console.log(` ${chalk.dim('finish:')} ${data.finishReason}`);
|
|
636
|
+
if (data.content) console.log(` ${chalk.dim('content:')} ${JSON.stringify(data.content.slice(0, 80))}${data.content.length > 80 ? '…' : ''}`);
|
|
637
|
+
if (data.usage) {
|
|
638
|
+
console.log(` ${chalk.dim('usage:')} total=${data.usage.total_tokens} prompt=${data.usage.prompt_tokens} completion=${data.usage.completion_tokens}`);
|
|
639
|
+
}
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (sub === 'config' || sub === 'set') {
|
|
644
|
+
const key = positional[0];
|
|
645
|
+
if (!key) {
|
|
646
|
+
console.error(chalk.red(' ✗ Missing key. Usage: bizar minimax config <sk-cp-…>'));
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
if (!/^sk-(cp|ant|or)-[A-Za-z0-9_-]{20,}$/.test(key)) {
|
|
650
|
+
console.error(chalk.red(' ✗ Key does not look like a MiniMax key (expected sk-cp-…, sk-ant-…, or sk-or-… prefix)'));
|
|
651
|
+
process.exit(1);
|
|
652
|
+
}
|
|
653
|
+
console.log(chalk.dim(' Saving to opencode auth.json…'));
|
|
654
|
+
const r = await minimaxApi('/api/minimax/onboarding/save-key', {
|
|
655
|
+
method: 'POST',
|
|
656
|
+
body: { key, groupId: 'default' },
|
|
657
|
+
});
|
|
658
|
+
if (!r.ok) {
|
|
659
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
console.log(chalk.green(` ✓ Saved to ${r.data?.path}`));
|
|
663
|
+
console.log(` ${chalk.dim('key hint:')} ${r.data?.apiKeyHint}`);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (sub === 'clear' || sub === 'remove') {
|
|
668
|
+
if (!yes) {
|
|
669
|
+
console.log(chalk.yellow(` ⚠ This will remove the MiniMax Subscription Key from opencode's auth.json.`));
|
|
670
|
+
console.log(chalk.dim(' Continue? [y/N]'));
|
|
671
|
+
// simple readline confirmation
|
|
672
|
+
const buf = [];
|
|
673
|
+
process.stdin.setEncoding('utf8');
|
|
674
|
+
process.stdin.on('data', (c) => { buf.push(c); if (c.includes('\n')) process.stdin.pause(); });
|
|
675
|
+
await new Promise((resolve) => process.stdin.once('close', resolve));
|
|
676
|
+
const answer = buf.join('').trim().toLowerCase();
|
|
677
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
678
|
+
console.log(chalk.dim(' Cancelled.'));
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// We don't have a "clear key" route; instead write an empty auth.json
|
|
683
|
+
// entry. We piggyback on the onboarding save-key by saving an empty
|
|
684
|
+
// marker isn't allowed — use the providers-store side instead. For
|
|
685
|
+
// now, recommend using the dashboard's "clear" button. But the
|
|
686
|
+
// simplest CLI path is to write auth.json directly.
|
|
687
|
+
const authFile = join(HOME, '.local', 'share', 'opencode', 'auth.json');
|
|
688
|
+
let auth = {};
|
|
689
|
+
try {
|
|
690
|
+
if (existsSync(authFile)) auth = JSON.parse(readFileSync(authFile, 'utf8'));
|
|
691
|
+
} catch { /* ignore */ }
|
|
692
|
+
if (auth.minimax) {
|
|
693
|
+
delete auth.minimax;
|
|
694
|
+
writeFileSync(authFile, JSON.stringify(auth, null, 2) + '\n', 'utf8');
|
|
695
|
+
console.log(chalk.green(' ✓ MiniMax key removed from auth.json'));
|
|
696
|
+
} else {
|
|
697
|
+
console.log(chalk.dim(' No MiniMax key was configured.'));
|
|
698
|
+
}
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (sub === 'reset-onboarding' || sub === 'reset') {
|
|
703
|
+
if (!yes) {
|
|
704
|
+
console.log(chalk.yellow(' ⚠ This will re-trigger the first-run MiniMax onboarding wizard.'));
|
|
705
|
+
console.log(chalk.dim(' Continue? [y/N]'));
|
|
706
|
+
const buf = [];
|
|
707
|
+
process.stdin.setEncoding('utf8');
|
|
708
|
+
process.stdin.on('data', (c) => { buf.push(c); if (c.includes('\n')) process.stdin.pause(); });
|
|
709
|
+
await new Promise((resolve) => process.stdin.once('close', resolve));
|
|
710
|
+
const answer = buf.join('').trim().toLowerCase();
|
|
711
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
712
|
+
console.log(chalk.dim(' Cancelled.'));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
// Reset via the dashboard API.
|
|
717
|
+
const r = await minimaxApi('/api/minimax/onboarding', {
|
|
718
|
+
method: 'POST',
|
|
719
|
+
body: { dismissedAt: null },
|
|
720
|
+
});
|
|
721
|
+
if (!r.ok) {
|
|
722
|
+
console.error(chalk.red(` ✗ ${r.message || r.error}`));
|
|
723
|
+
process.exit(1);
|
|
724
|
+
}
|
|
725
|
+
console.log(chalk.green(' ✓ Onboarding wizard will show on next dashboard load'));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
console.error(chalk.red(` ✗ Unknown minimax subcommand: ${sub}`));
|
|
730
|
+
showMinimaxHelp();
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
|
|
479
734
|
function showDashHelp() {
|
|
480
735
|
console.log(`
|
|
481
736
|
bizar dash — Manage the Bizar dashboard
|
|
@@ -823,6 +1078,11 @@ async function main() {
|
|
|
823
1078
|
// (the dashboard owns the actual mod install/upgrade logic and the
|
|
824
1079
|
// opencode-config install/uninstall of instruction files).
|
|
825
1080
|
await runModCommand(args.slice(1));
|
|
1081
|
+
} else if (args[0] === 'minimax') {
|
|
1082
|
+
// v4.5.0 — MiniMax Token Plan integration CLI. Status, remains,
|
|
1083
|
+
// test, config, clear, reset-onboarding. All commands shell out
|
|
1084
|
+
// to the dashboard's /api/minimax/* routes.
|
|
1085
|
+
await runMinimaxCommand(args.slice(1));
|
|
826
1086
|
} else if (args[0] === 'dash' || args[0] === 'dashboard') {
|
|
827
1087
|
// `bizar dashboard` is a deprecated alias for `bizar dash`
|
|
828
1088
|
if (args[0] === 'dashboard') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.13",
|
|
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": {
|