@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.
Files changed (50) hide show
  1. package/bizar-dash/dist/assets/{MobileChat-BVojK0n5.js → MobileChat-TCputYzr.js} +1 -1
  2. package/bizar-dash/dist/assets/{MobileChat-BVojK0n5.js.map → MobileChat-TCputYzr.js.map} +1 -1
  3. package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js → MobileSettings-BxcbL5XT.js} +1 -1
  4. package/bizar-dash/dist/assets/{MobileSettings-C85ApWKZ.js.map → MobileSettings-BxcbL5XT.js.map} +1 -1
  5. package/bizar-dash/dist/assets/{icons-Clz0NR6Y.js → icons-DRDXfbBP.js} +154 -134
  6. package/bizar-dash/dist/assets/icons-DRDXfbBP.js.map +1 -0
  7. package/bizar-dash/dist/assets/main-D5Ditnrd.js +19 -0
  8. package/bizar-dash/dist/assets/main-D5Ditnrd.js.map +1 -0
  9. package/bizar-dash/dist/assets/{main-DTkNlLrw.css → main-xFpWMd32.css} +1 -1
  10. package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js → mobile--17fkfrl.js} +1 -1
  11. package/bizar-dash/dist/assets/{mobile-D2pc-iNh.js.map → mobile--17fkfrl.js.map} +1 -1
  12. package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js → mobile-j3rOZK6v.js} +2 -2
  13. package/bizar-dash/dist/assets/{mobile-IaZ47uKC.js.map → mobile-j3rOZK6v.js.map} +1 -1
  14. package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js → useSlashCommands-BG-DhEck.js} +2 -2
  15. package/bizar-dash/dist/assets/{useSlashCommands-DjEwHl4n.js.map → useSlashCommands-BG-DhEck.js.map} +1 -1
  16. package/bizar-dash/dist/index.html +5 -5
  17. package/bizar-dash/dist/mobile.html +3 -3
  18. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  19. package/bizar-dash/src/server/api.mjs +4 -0
  20. package/bizar-dash/src/server/routes/plugins.mjs +11 -3
  21. package/bizar-dash/src/server/routes/tailscale.mjs +46 -0
  22. package/bizar-dash/src/server/routes/voice.mjs +42 -6
  23. package/bizar-dash/src/server/server.mjs +13 -0
  24. package/bizar-dash/src/server/voice-store.mjs +27 -0
  25. package/bizar-dash/src/server/workers/transcription-worker.mjs +213 -0
  26. package/bizar-dash/src/web/App.tsx +9 -0
  27. package/bizar-dash/src/web/components/EvalDiff.tsx +157 -0
  28. package/bizar-dash/src/web/components/EvalRunCard.tsx +78 -0
  29. package/bizar-dash/src/web/components/PluginCard.tsx +64 -0
  30. package/bizar-dash/src/web/components/PluginPermissions.tsx +36 -0
  31. package/bizar-dash/src/web/components/TailscaleSettings.tsx +161 -0
  32. package/bizar-dash/src/web/components/Toggle.tsx +31 -0
  33. package/bizar-dash/src/web/components/Topbar.tsx +6 -0
  34. package/bizar-dash/src/web/styles/main.css +545 -0
  35. package/bizar-dash/src/web/views/Eval.tsx +172 -0
  36. package/bizar-dash/src/web/views/EvalReport.tsx +349 -0
  37. package/bizar-dash/src/web/views/Plugins.tsx +128 -0
  38. package/bizar-dash/src/web/views/Settings.tsx +2 -0
  39. package/bizar-dash/tests/cli-tailscale.test.mjs +113 -0
  40. package/bizar-dash/tests/components/plugin-permissions.test.tsx +95 -0
  41. package/bizar-dash/tests/eval-web-ui.test.tsx +220 -0
  42. package/bizar-dash/tests/voice-transcribe-worker.test.mjs +343 -0
  43. package/cli/bin.mjs +14 -0
  44. package/cli/commands/dash.mjs +19 -1
  45. package/cli/commands/service.mjs +7 -0
  46. package/cli/commands/tailscale.mjs +251 -0
  47. package/package.json +1 -1
  48. package/bizar-dash/dist/assets/icons-Clz0NR6Y.js.map +0 -1
  49. package/bizar-dash/dist/assets/main-8feQWXiF.js +0 -19
  50. package/bizar-dash/dist/assets/main-8feQWXiF.js.map +0 -1
@@ -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;
@@ -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.1.0",
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": {