@polderlabs/bizar 5.1.0 → 5.2.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/dist/assets/{MobileChat-BVojK0n5.js → MobileChat-TCputYzr.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-BVojK0n5.js.map → MobileChat-TCputYzr.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js → MobileSettings-BxcbL5XT.js} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js.map → MobileSettings-BxcbL5XT.js.map} +1 -1
- package/bizar-dash/dist/assets/{icons-Clz0NR6Y.js → icons-DRDXfbBP.js} +154 -134
- package/bizar-dash/dist/assets/icons-DRDXfbBP.js.map +1 -0
- package/bizar-dash/dist/assets/main-D5Ditnrd.js +19 -0
- package/bizar-dash/dist/assets/main-D5Ditnrd.js.map +1 -0
- package/bizar-dash/dist/assets/{main-DTkNlLrw.css → main-xFpWMd32.css} +1 -1
- package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js → mobile--17fkfrl.js} +1 -1
- package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js.map → mobile--17fkfrl.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js → mobile-j3rOZK6v.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js.map → mobile-j3rOZK6v.js.map} +1 -1
- package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js → useSlashCommands-BG-DhEck.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js.map → useSlashCommands-BG-DhEck.js.map} +1 -1
- package/bizar-dash/dist/index.html +5 -5
- package/bizar-dash/dist/mobile.html +3 -3
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +4 -0
- package/bizar-dash/src/server/routes/plugins.mjs +11 -3
- package/bizar-dash/src/server/routes/tailscale.mjs +46 -0
- package/bizar-dash/src/server/routes/voice.mjs +42 -6
- package/bizar-dash/src/server/server.mjs +13 -0
- package/bizar-dash/src/server/voice-store.mjs +27 -0
- package/bizar-dash/src/server/workers/transcription-worker.mjs +213 -0
- package/bizar-dash/src/web/App.tsx +9 -0
- package/bizar-dash/src/web/components/EvalDiff.tsx +157 -0
- package/bizar-dash/src/web/components/EvalRunCard.tsx +78 -0
- package/bizar-dash/src/web/components/PluginCard.tsx +64 -0
- package/bizar-dash/src/web/components/PluginPermissions.tsx +36 -0
- package/bizar-dash/src/web/components/TailscaleSettings.tsx +161 -0
- package/bizar-dash/src/web/components/Toggle.tsx +31 -0
- package/bizar-dash/src/web/components/Topbar.tsx +6 -0
- package/bizar-dash/src/web/styles/main.css +545 -0
- package/bizar-dash/src/web/views/Eval.tsx +172 -0
- package/bizar-dash/src/web/views/EvalReport.tsx +349 -0
- package/bizar-dash/src/web/views/Plugins.tsx +128 -0
- package/bizar-dash/src/web/views/Settings.tsx +2 -0
- package/bizar-dash/tests/cli-tailscale.test.mjs +113 -0
- package/bizar-dash/tests/components/plugin-permissions.test.tsx +95 -0
- package/bizar-dash/tests/eval-web-ui.test.tsx +220 -0
- package/bizar-dash/tests/voice-transcribe-worker.test.mjs +343 -0
- package/cli/bin.mjs +14 -0
- package/cli/commands/dash.mjs +19 -1
- package/cli/commands/service.mjs +7 -0
- package/cli/commands/tailscale.mjs +251 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/icons-Clz0NR6Y.js.map +0 -1
- package/bizar-dash/dist/assets/main-8feQWXiF.js +0 -19
- package/bizar-dash/dist/assets/main-8feQWXiF.js.map +0 -1
package/cli/commands/dash.mjs
CHANGED
|
@@ -127,7 +127,24 @@ export async function runDash(dashArgs) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
switch (sub) {
|
|
130
|
-
case 'start':
|
|
130
|
+
case 'start': {
|
|
131
|
+
// v5.2 — Auto-setup Tailscale serve if auth key is available
|
|
132
|
+
const usePort = subOpts.port || 4321;
|
|
133
|
+
if (process.env.TAILSCALE_AUTHKEY || process.env.BIZAR_TAILSCALE_AUTOSETUP === '1') {
|
|
134
|
+
try {
|
|
135
|
+
const { ensureTailscaleAuth, setupTailscaleServe } = await import('./tailscale.mjs');
|
|
136
|
+
const authResult = await ensureTailscaleAuth();
|
|
137
|
+
if (authResult.authenticated) {
|
|
138
|
+
const serveResult = await setupTailscaleServe({ dashboardPort: usePort });
|
|
139
|
+
if (serveResult.ok) {
|
|
140
|
+
console.log(chalk.green(` ✓ Tailscale serve: ${serveResult.url}`));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// Non-fatal: Tailscale setup errors don't block dashboard start
|
|
145
|
+
console.log(chalk.dim(` ⚠ Tailscale auto-setup skipped: ${err.message}`));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
131
148
|
if (subOpts.bg) {
|
|
132
149
|
// Pass --port, --host, --bind through to the child process
|
|
133
150
|
const bgArgs = ['start', ...(subOpts.subArgs || [])];
|
|
@@ -138,6 +155,7 @@ export async function runDash(dashArgs) {
|
|
|
138
155
|
await dashModule.start(subOpts);
|
|
139
156
|
}
|
|
140
157
|
break;
|
|
158
|
+
}
|
|
141
159
|
case 'stop':
|
|
142
160
|
await dashModule.stop(subOpts);
|
|
143
161
|
break;
|
package/cli/commands/service.mjs
CHANGED
|
@@ -22,6 +22,8 @@ function getBizarConfigDir() {
|
|
|
22
22
|
|
|
23
23
|
export function showServiceHelp() {
|
|
24
24
|
const bizarConfigDir = getBizarConfigDir();
|
|
25
|
+
const tsAuthkey = process.env.TAILSCALE_AUTHKEY ? '(set)' : '(not set)';
|
|
26
|
+
const tsAuto = process.env.BIZAR_TAILSCALE_AUTOSETUP === '1' ? 'enabled' : 'disabled';
|
|
25
27
|
console.log(`
|
|
26
28
|
bizar service — Manage the background service daemon
|
|
27
29
|
|
|
@@ -47,6 +49,11 @@ export function showServiceHelp() {
|
|
|
47
49
|
("BizarDashboardService", ONSTART, HIGHEST) on Windows. After
|
|
48
50
|
install, a normal user does not need to run \`bizar service start\`
|
|
49
51
|
for the dashboard background process — the OS does it at login.
|
|
52
|
+
|
|
53
|
+
Tailscale Integration (v5.2):
|
|
54
|
+
TAILSCALE_AUTHKEY=${tsAuthkey}
|
|
55
|
+
BIZAR_TAILSCALE_AUTOSETUP=${tsAuto}
|
|
56
|
+
Run \`bizar tailscale --help\` for Tailscale management.
|
|
50
57
|
`);
|
|
51
58
|
}
|
|
52
59
|
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/tailscale.mjs
|
|
3
|
+
*
|
|
4
|
+
* v5.2 — Tailscale auth key integration for the dashboard.
|
|
5
|
+
*
|
|
6
|
+
* When TAILSCALE_AUTHKEY is set (or BIZAR_TAILSCALE_AUTOSETUP=1), the
|
|
7
|
+
* dashboard auto-configures `tailscale serve` on startup so the user
|
|
8
|
+
* doesn't need to run `sudo tailscale serve ...` manually.
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
11
|
+
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { join, dirname } from 'node:path';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
|
|
16
|
+
const TS_AUTHKEY = process.env.TAILSCALE_AUTHKEY;
|
|
17
|
+
const TS_STATE_DIR = process.env.TAILSCALE_STATE_DIR || join(homedir(), '.local', 'share', 'bizar', 'tailscale');
|
|
18
|
+
const TS_SERVE_CONFIG = join(TS_STATE_DIR, 'serve.json');
|
|
19
|
+
|
|
20
|
+
function ensureStateDir() {
|
|
21
|
+
const { mkdirSync } = require('node:fs');
|
|
22
|
+
try {
|
|
23
|
+
mkdirSync(TS_STATE_DIR, { recursive: true });
|
|
24
|
+
} catch { /* ignore */ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if tailscale binary exists and is executable.
|
|
29
|
+
*/
|
|
30
|
+
export function isTailscaleInstalled() {
|
|
31
|
+
try {
|
|
32
|
+
execFileSync('tailscale', ['version'], { encoding: 'utf8', timeout: 5000 });
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if already authenticated with Tailscale.
|
|
41
|
+
* Returns { authenticated, hostname, backendState }.
|
|
42
|
+
*/
|
|
43
|
+
export async function ensureTailscaleAuth() {
|
|
44
|
+
if (!isTailscaleInstalled()) {
|
|
45
|
+
return { authenticated: false, message: 'Tailscale not installed' };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const result = execFileSync('tailscale', ['status', '--json'], { encoding: 'utf8', timeout: 5000 });
|
|
49
|
+
const status = JSON.parse(result);
|
|
50
|
+
return {
|
|
51
|
+
authenticated: !!status.Self?.Online,
|
|
52
|
+
hostname: (status.Self?.DNSName || '').split('.')[0],
|
|
53
|
+
backendState: status.BackendState || 'unknown',
|
|
54
|
+
};
|
|
55
|
+
} catch {
|
|
56
|
+
if (!TS_AUTHKEY) {
|
|
57
|
+
return { authenticated: false, message: 'No TAILSCALE_AUTHKEY env var set' };
|
|
58
|
+
}
|
|
59
|
+
// Authenticate with auth key
|
|
60
|
+
console.log(chalk.dim(' Authenticating with Tailscale...'));
|
|
61
|
+
try {
|
|
62
|
+
execFileSync('tailscale', ['up', '--authkey=' + TS_AUTHKEY, '--hostname=bizar-dash'], { encoding: 'utf8', timeout: 60000 });
|
|
63
|
+
return { authenticated: true, hostname: 'bizar-dash' };
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return { authenticated: false, message: 'Auth failed: ' + (err.message || String(err)) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set up tailscale serve for the dashboard.
|
|
72
|
+
*/
|
|
73
|
+
export async function setupTailscaleServe({ dashboardPort = 4097, path = '/', https = 443 }) {
|
|
74
|
+
const authResult = await ensureTailscaleAuth();
|
|
75
|
+
if (!authResult.authenticated) {
|
|
76
|
+
console.error(chalk.red(' ✗ Tailscale not authenticated'));
|
|
77
|
+
return { ok: false, reason: 'not_authenticated' };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Remove existing serve config
|
|
81
|
+
try {
|
|
82
|
+
execFileSync('tailscale', ['serve', '--https=443', 'off'], { encoding: 'utf8', timeout: 5000 });
|
|
83
|
+
} catch { /* ignore — might not have existing config */ }
|
|
84
|
+
|
|
85
|
+
// Set up new serve config
|
|
86
|
+
try {
|
|
87
|
+
execFileSync('tailscale', [
|
|
88
|
+
'serve', '--bg', `--https=${https}`, `--set-path=${path}`,
|
|
89
|
+
`http://localhost:${dashboardPort}`,
|
|
90
|
+
], { encoding: 'utf8', timeout: 30000 });
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return { ok: false, reason: 'serve_setup_failed', error: err.message };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Get the public URL
|
|
96
|
+
try {
|
|
97
|
+
const statusResult = execFileSync('tailscale', ['status', '--json'], { encoding: 'utf8', timeout: 5000 });
|
|
98
|
+
const status = JSON.parse(statusResult);
|
|
99
|
+
const url = `https://${(status.Self?.DNSName || '').split('.')[0]}`;
|
|
100
|
+
|
|
101
|
+
// Save config
|
|
102
|
+
ensureStateDir();
|
|
103
|
+
writeFileSync(TS_SERVE_CONFIG, JSON.stringify({
|
|
104
|
+
url,
|
|
105
|
+
port: dashboardPort,
|
|
106
|
+
https,
|
|
107
|
+
path,
|
|
108
|
+
setAt: new Date().toISOString(),
|
|
109
|
+
}, null, 2));
|
|
110
|
+
|
|
111
|
+
return { ok: true, url };
|
|
112
|
+
} catch (err) {
|
|
113
|
+
return { ok: false, reason: 'status_check_failed', error: err.message };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Remove tailscale serve configuration.
|
|
119
|
+
*/
|
|
120
|
+
export function unsetupTailscaleServe() {
|
|
121
|
+
try {
|
|
122
|
+
execFileSync('tailscale', ['serve', 'reset'], { encoding: 'utf8', timeout: 10000 });
|
|
123
|
+
// Remove saved config
|
|
124
|
+
if (existsSync(TS_SERVE_CONFIG)) {
|
|
125
|
+
const { unlinkSync } = require('node:fs');
|
|
126
|
+
try { unlinkSync(TS_SERVE_CONFIG); } catch { /* ignore */ }
|
|
127
|
+
}
|
|
128
|
+
return { ok: true };
|
|
129
|
+
} catch (err) {
|
|
130
|
+
return { ok: false, error: err.message };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Print the saved serve URL (if any).
|
|
136
|
+
*/
|
|
137
|
+
export function getServeUrl() {
|
|
138
|
+
try {
|
|
139
|
+
if (existsSync(TS_SERVE_CONFIG)) {
|
|
140
|
+
const cfg = JSON.parse(readFileSync(TS_SERVE_CONFIG, 'utf8'));
|
|
141
|
+
return cfg.url || null;
|
|
142
|
+
}
|
|
143
|
+
} catch { /* ignore */ }
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function showTailscaleStatus() {
|
|
148
|
+
if (!isTailscaleInstalled()) {
|
|
149
|
+
console.error(chalk.red(' ✗ tailscale not found or not installed'));
|
|
150
|
+
console.error(' Install: https://tailscale.com/download');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const result = execFileSync('tailscale', ['status'], { encoding: 'utf8', timeout: 5000 });
|
|
155
|
+
console.log(result);
|
|
156
|
+
|
|
157
|
+
// Also show serve URL if configured
|
|
158
|
+
const url = getServeUrl();
|
|
159
|
+
if (url) {
|
|
160
|
+
console.log(chalk.dim(` Serve URL: ${url}`));
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error(chalk.red(' ✗ Failed to get tailscale status'));
|
|
164
|
+
console.error(' ', err.message);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function showTailscaleHelp() {
|
|
169
|
+
console.log(`
|
|
170
|
+
bizar tailscale — Manage Tailscale integration for the dashboard
|
|
171
|
+
|
|
172
|
+
Usage:
|
|
173
|
+
bizar tailscale status Show Tailscale auth + serve status
|
|
174
|
+
bizar tailscale auth Authenticate with auth key (from env TAILSCALE_AUTHKEY)
|
|
175
|
+
bizar tailscale serve Set up tailscale serve for the dashboard
|
|
176
|
+
bizar tailscale unserve Remove tailscale serve
|
|
177
|
+
bizar tailscale url Print the public dashboard URL
|
|
178
|
+
|
|
179
|
+
Env vars:
|
|
180
|
+
TAILSCALE_AUTHKEY Auth key for non-interactive auth
|
|
181
|
+
TAILSCALE_STATE_DIR Where to store Tailscale state (default ~/.local/share/bizar/tailscale)
|
|
182
|
+
BIZAR_TAILSCALE_AUTOSETUP=1 Auto-setup tailscale serve when running 'bizar dash start'
|
|
183
|
+
|
|
184
|
+
Examples:
|
|
185
|
+
TAILSCALE_AUTHKEY=tskey-... bizar tailscale auth
|
|
186
|
+
TAILSCALE_AUTHKEY=tskey-... bizar tailscale serve
|
|
187
|
+
bizar tailscale unserve
|
|
188
|
+
`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Main entry point for the `bizar tailscale` CLI command.
|
|
193
|
+
*/
|
|
194
|
+
export async function run(name, args, isHelpRequest) {
|
|
195
|
+
if (isHelpRequest || args.length === 0 || args[0] === 'help') {
|
|
196
|
+
showTailscaleHelp();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const sub = args[0];
|
|
201
|
+
|
|
202
|
+
switch (sub) {
|
|
203
|
+
case 'status': {
|
|
204
|
+
showTailscaleStatus();
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 'auth': {
|
|
208
|
+
const result = await ensureTailscaleAuth();
|
|
209
|
+
if (result.authenticated) {
|
|
210
|
+
console.log(chalk.green(` ✓ Authenticated as ${result.hostname}`));
|
|
211
|
+
} else {
|
|
212
|
+
console.error(chalk.red(` ✗ Not authenticated: ${result.message}`));
|
|
213
|
+
if (!TS_AUTHKEY) {
|
|
214
|
+
console.error(chalk.dim(' Set TAILSCALE_AUTHKEY env var and try again'));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 'serve': {
|
|
220
|
+
const result = await setupTailscaleServe({ dashboardPort: 4321 });
|
|
221
|
+
if (result.ok) {
|
|
222
|
+
console.log(chalk.green(` ✓ Tailscale serve: ${result.url}`));
|
|
223
|
+
} else {
|
|
224
|
+
console.error(chalk.red(` ✗ Serve setup failed: ${result.reason}`));
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case 'unset': {
|
|
229
|
+
const result = unsetupTailscaleServe();
|
|
230
|
+
if (result.ok) {
|
|
231
|
+
console.log(chalk.green(' ✓ Tailscale serve removed'));
|
|
232
|
+
} else {
|
|
233
|
+
console.error(chalk.red(` ✗ ${result.error}`));
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case 'url': {
|
|
238
|
+
const url = getServeUrl();
|
|
239
|
+
if (url) {
|
|
240
|
+
console.log(url);
|
|
241
|
+
} else {
|
|
242
|
+
console.error(chalk.red(' ✗ No serve URL configured'));
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
default: {
|
|
247
|
+
console.error(chalk.red(` ✗ Unknown subcommand: ${sub}`));
|
|
248
|
+
showTailscaleHelp();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.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": {
|