@polderlabs/bizar 7.0.1 → 7.0.4

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 (62) hide show
  1. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js +114 -0
  2. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js.map +1 -0
  3. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js +1 -0
  4. package/bizar-dash/dist/assets/{MobileChat-BRFjer8p.js.map → MobileChat-DLWdv6tQ.js.map} +1 -1
  5. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js +1 -0
  6. package/bizar-dash/dist/assets/{MobileSettings-Dw5jyJWi.js.map → MobileSettings-Bxv62uPY.js.map} +1 -1
  7. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js +26 -0
  8. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js.map +1 -0
  9. package/bizar-dash/dist/assets/main-CJAtrosO.css +1 -0
  10. package/bizar-dash/dist/assets/main-DKKj9dsq.js +430 -0
  11. package/bizar-dash/dist/assets/main-DKKj9dsq.js.map +1 -0
  12. package/bizar-dash/dist/assets/mobile-C_naUN91.js +1 -0
  13. package/bizar-dash/dist/assets/{mobile-DRZsK3ic.js.map → mobile-C_naUN91.js.map} +1 -1
  14. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js +211 -0
  15. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js.map +1 -0
  16. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js +65 -0
  17. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js.map +1 -0
  18. package/bizar-dash/dist/index.html +6 -12
  19. package/bizar-dash/dist/mobile.html +2 -7
  20. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  21. package/bizar-dash/src/web/components/SettingsNav.tsx +9 -4
  22. package/bizar-dash/src/web/components/Topbar.tsx +45 -0
  23. package/bizar-dash/src/web/styles/main.css +87 -89
  24. package/bizar-dash/src/web/styles/memory.css +30 -35
  25. package/bizar-dash/src/web/styles/settings.css +6 -26
  26. package/bizar-dash/src/web/ui/data/data.css +6 -1
  27. package/bizar-dash/src/web/ui/index.ts +1 -0
  28. package/bizar-dash/src/web/views/Memory.tsx +94 -62
  29. package/bizar-dash/src/web/views/Overview.tsx +2 -23
  30. package/bizar-dash/tests/settings-layout.test.tsx +2 -2
  31. package/bizar-dash/tests/settings-nav.test.tsx +6 -6
  32. package/cli/provision-claude.mjs +108 -5
  33. package/package.json +1 -1
  34. package/packages/sdk/.harness/agents.json +17 -0
  35. package/packages/sdk/.harness/topology.json +4 -0
  36. package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js +0 -4
  37. package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js.map +0 -1
  38. package/bizar-dash/dist/assets/MobileChat-BRFjer8p.js +0 -1
  39. package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js +0 -1
  40. package/bizar-dash/dist/assets/Toast-WTxc3m2g.js +0 -1
  41. package/bizar-dash/dist/assets/Toast-WTxc3m2g.js.map +0 -1
  42. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +0 -1
  43. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +0 -1
  44. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +0 -1
  45. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +0 -1
  46. package/bizar-dash/dist/assets/icons-B0AKnYI3.js +0 -731
  47. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +0 -1
  48. package/bizar-dash/dist/assets/main-BE4Uh9im.css +0 -1
  49. package/bizar-dash/dist/assets/main-DqC58FEZ.js +0 -18
  50. package/bizar-dash/dist/assets/main-DqC58FEZ.js.map +0 -1
  51. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +0 -1
  52. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js.map +0 -1
  53. package/bizar-dash/dist/assets/mobile-DRZsK3ic.js +0 -1
  54. package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js +0 -2
  55. package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js.map +0 -1
  56. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +0 -1
  57. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +0 -46
  58. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +0 -1
  59. package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js +0 -2
  60. package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js.map +0 -1
  61. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +0 -45
  62. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +0 -1
@@ -1,11 +1,12 @@
1
- // src/web/views/Memory.tsx — v4.7.0 dedicated Memory tab.
1
+ // src/web/views/Memory.tsx — v7.0.4 dedicated Memory tab.
2
2
  //
3
- // Three-column layout: left rail (source picker), main panel (per-source UI),
4
- // and a top stat row + a health hero at the top of every panel for context.
5
- //
6
- // The Memory subsystem has 4 sources (LightRAG, Obsidian vault, git sync,
7
- // semantic search) plus a config panel and a high-level overview. Each is a
8
- // standalone component under views/memory/.
3
+ // Two-column layout: source rail on the left (10 sources), main panel on
4
+ // the right (one sub-panel at a time). v7.0.4 swaps the legacy
5
+ // `.view view-memory memory-tab` + `.memory-tab-body` + `.memory-source-rail`
6
+ // shell for the design-system primitives (Box / ViewHeader / Grid /
7
+ // cx-classed nav buttons) so the Memory tab matches the rest of the
8
+ // dashboard theme. Sub-panels keep their own shell until a follow-up
9
+ // sprint per-component migrates them.
9
10
 
10
11
  import React, { useCallback, useState } from 'react';
11
12
  import {
@@ -21,9 +22,9 @@ import {
21
22
  Search as SearchIcon,
22
23
  Sliders,
23
24
  } from 'lucide-react';
24
- import { Card } from '../components/Card';
25
+ import { Box, Grid, IconButton, Inline, Stack, ViewHeader } from '../ui';
26
+ import { cx } from '../ui/utils/cx';
25
27
  import { useToast } from '../components/Toast';
26
- import { cn } from '../lib/utils';
27
28
  import { MemoryOverview } from './memory/MemoryOverview';
28
29
  import { LightragPanel } from './memory/LightragPanel';
29
30
  import { ObsidianPanel } from './memory/ObsidianPanel';
@@ -35,7 +36,17 @@ import { FromScreenshotPanel } from './memory/FromScreenshotPanel';
35
36
  import { VaultFromClipboardPanel } from './memory/VaultFromClipboardPanel';
36
37
  import { VoiceNotesPanel } from '../components/VoiceNotesPanel';
37
38
 
38
- type SubPanel = 'overview' | 'lightrag' | 'obsidian' | 'git' | 'semantic' | 'config' | 'graph' | 'webclip' | 'screenshot' | 'voice';
39
+ type SubPanel =
40
+ | 'overview'
41
+ | 'lightrag'
42
+ | 'obsidian'
43
+ | 'git'
44
+ | 'semantic'
45
+ | 'config'
46
+ | 'graph'
47
+ | 'webclip'
48
+ | 'screenshot'
49
+ | 'voice';
39
50
 
40
51
  type Props = {
41
52
  snapshot: unknown;
@@ -79,7 +90,13 @@ function MemoryInner(_props: Props) {
79
90
  const renderPanel = () => {
80
91
  switch (active) {
81
92
  case 'overview':
82
- return <MemoryOverview refreshKey={refreshKey} onRefresh={refresh} setActiveSubPanel={setActive} />;
93
+ return (
94
+ <MemoryOverview
95
+ refreshKey={refreshKey}
96
+ onRefresh={refresh}
97
+ setActiveSubPanel={(id) => setActive(id as SubPanel)}
98
+ />
99
+ );
83
100
  case 'lightrag':
84
101
  return <LightragPanel refreshKey={refreshKey} />;
85
102
  case 'obsidian':
@@ -99,63 +116,78 @@ function MemoryInner(_props: Props) {
99
116
  case 'voice':
100
117
  return <VoiceNotesPanel refreshKey={refreshKey} />;
101
118
  default:
102
- return <Card>Unknown panel: {active}</Card>;
119
+ return null;
103
120
  }
104
121
  };
105
122
 
106
123
  return (
107
- <div className="view view-memory memory-tab">
108
- {/* ── Header ─────────────────────────────────────────────── */}
109
- <header className="view-header">
110
- <div className="view-header-text">
111
- <h2 className="view-title">
112
- <Brain size={18} /> Memory
113
- </h2>
114
- <p className="view-subtitle">
115
- LightRAG, Obsidian vault, git sync, semantic search, web clips, and screenshot OCR — all in one place.
116
- </p>
117
- </div>
118
- <div className="view-actions">
119
- <button
120
- type="button"
121
- className="icon-btn"
122
- onClick={onRefreshAll}
123
- aria-label="Refresh memory"
124
- title="Refresh"
125
- >
126
- <RefreshCw size={14} />
127
- </button>
128
- </div>
129
- </header>
124
+ <Box as="div" className="view view-memory" bg="0" p={7}>
125
+ <Stack gap={5}>
126
+ <ViewHeader
127
+ title={
128
+ <Inline gap={2} align="center">
129
+ <Brain size={18} />
130
+ <span>Memory</span>
131
+ </Inline>
132
+ }
133
+ subtitle="LightRAG, Obsidian vault, git sync, semantic search, web clips, and screenshot OCR — all in one place."
134
+ actions={
135
+ <IconButton
136
+ variant="ghost"
137
+ size="md"
138
+ onClick={onRefreshAll}
139
+ aria-label="Refresh memory"
140
+ title="Refresh"
141
+ icon={<RefreshCw size={14} />}
142
+ />
143
+ }
144
+ />
130
145
 
131
- <div className="memory-tab-body">
132
- {/* ── Source rail ─────────────────────────────────────── */}
133
- <nav className="memory-source-rail" aria-label="Memory sources">
134
- {SOURCES.map((s) => {
135
- const Icon = s.icon;
136
- const isActive = active === s.id;
137
- return (
138
- <button
139
- key={s.id}
140
- type="button"
141
- role="tab"
142
- aria-selected={isActive}
143
- className={cn('memory-source-button', isActive && 'memory-source-button-active')}
144
- onClick={() => setActive(s.id)}
145
- >
146
- <Icon size={16} aria-hidden />
147
- <span>{s.label}</span>
148
- </button>
149
- );
150
- })}
151
- </nav>
146
+ <Grid
147
+ cols={2}
148
+ gap={4}
149
+ style={{
150
+ gridTemplateColumns: '240px 1fr',
151
+ alignItems: 'start',
152
+ minHeight: 0,
153
+ }}
154
+ data-testid="memory-grid"
155
+ >
156
+ {/* Source rail — Stack doesn't accept `as="nav"`, so wrap
157
+ in <nav> directly and let Stack render its rows. */}
158
+ <nav aria-label="Memory sources" data-testid="memory-source-rail">
159
+ <Stack direction="column" gap={1}>
160
+ {SOURCES.map((s) => {
161
+ const Icon = s.icon;
162
+ const isActive = active === s.id;
163
+ return (
164
+ <button
165
+ key={s.id}
166
+ type="button"
167
+ role="tab"
168
+ aria-selected={isActive}
169
+ className={cx(
170
+ 'memory-source-button',
171
+ isActive && 'memory-source-button-active',
172
+ )}
173
+ onClick={() => setActive(s.id)}
174
+ >
175
+ <Icon size={16} aria-hidden />
176
+ <span>{s.label}</span>
177
+ </button>
178
+ );
179
+ })}
180
+ </Stack>
181
+ </nav>
152
182
 
153
- {/* ── Main panel ─────────────────────────────────────── */}
154
- <div className="memory-main" key={active}>
155
- {renderPanel()}
156
- </div>
157
- </div>
158
- </div>
183
+ {/* Main panel */}
184
+ <Box as="div" key={active} data-testid="memory-main-panel">
185
+ {renderPanel()}
186
+ </Box>
187
+ </Grid>
188
+ </Stack>
189
+ </Box>
159
190
  );
160
191
  }
192
+
161
193
  export const Memory = React.memo(MemoryInner);
@@ -52,8 +52,6 @@ import {
52
52
  EyeOff,
53
53
  Eye,
54
54
  X,
55
- Sun,
56
- Moon,
57
55
  } from 'lucide-react';
58
56
 
59
57
  import {
@@ -134,20 +132,8 @@ function OverviewInner({
134
132
  // hide without losing the row from the underlying store. v3.15.0.
135
133
  const [hiddenKeys, setHiddenKeys] = useState<Set<string>>(new Set());
136
134
 
137
- // Theme toggle local because ThemeProvider doesn't wrap the app at
138
- // the App.tsx level yet. Toggles `data-theme` on <html>; tokens.css
139
- // handles the actual colour swap.
140
- const [isDark, setIsDark] = useState<boolean>(() => {
141
- if (typeof document === 'undefined') return true;
142
- const attr = document.documentElement.getAttribute('data-theme');
143
- return attr !== 'light';
144
- });
145
-
146
- useEffect(() => {
147
- if (typeof document === 'undefined') return;
148
- if (isDark) document.documentElement.removeAttribute('data-theme');
149
- else document.documentElement.setAttribute('data-theme', 'light');
150
- }, [isDark]);
135
+ // v7.0.4theme toggle is now hoisted to Topbar (see
136
+ // components/Topbar.tsx); no per-view state needed here.
151
137
 
152
138
  // Load hidden set once on mount — v3.15.0.
153
139
  useEffect(() => {
@@ -536,13 +522,6 @@ function OverviewInner({
536
522
  >
537
523
  Refresh
538
524
  </UiButton>
539
- <IconButton
540
- variant="ghost"
541
- size="md"
542
- onClick={() => setIsDark((v) => !v)}
543
- aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}
544
- icon={isDark ? <Sun size={14} /> : <Moon size={14} />}
545
- />
546
525
  </Inline>
547
526
  </Inline>
548
527
 
@@ -59,8 +59,8 @@ describe('Sidebar — settingsMode', () => {
59
59
  // Should show the back button
60
60
  expect(screen.getByRole('button', { name: /exit settings/i })).toBeInTheDocument();
61
61
  // Should show section groups (use selector to avoid duplicate text matches)
62
- expect(screen.getByText('General', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
63
- expect(screen.getByText('Core', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
62
+ expect(screen.getByText('General', { selector: '.sidebar-section-label' })).toBeInTheDocument();
63
+ expect(screen.getByText('Core', { selector: '.sidebar-section-label' })).toBeInTheDocument();
64
64
  });
65
65
 
66
66
  it('shows Back button that calls onExitSettings', async () => {
@@ -41,7 +41,7 @@ describe('SettingsNav', () => {
41
41
  expect(onExitSettings).toHaveBeenCalledTimes(1);
42
42
  });
43
43
 
44
- it('renders section groups with group-label class', () => {
44
+ it('renders section groups with shared sidebar-section-label class', () => {
45
45
  render(
46
46
  <SettingsNav
47
47
  activeSection={null}
@@ -49,11 +49,11 @@ describe('SettingsNav', () => {
49
49
  onExitSettings={onExitSettings}
50
50
  />,
51
51
  );
52
- expect(screen.getByText('General', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
53
- expect(screen.getByText('Core', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
54
- expect(screen.getByText('Agents', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
55
- expect(screen.getByText('Experience', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
56
- expect(screen.getByText('Data', { selector: '.settings-nav-group-label' })).toBeInTheDocument();
52
+ expect(screen.getByText('General', { selector: '.sidebar-section-label' })).toBeInTheDocument();
53
+ expect(screen.getByText('Core', { selector: '.sidebar-section-label' })).toBeInTheDocument();
54
+ expect(screen.getByText('Agents', { selector: '.sidebar-section-label' })).toBeInTheDocument();
55
+ expect(screen.getByText('Experience', { selector: '.sidebar-section-label' })).toBeInTheDocument();
56
+ expect(screen.getByText('Data', { selector: '.sidebar-section-label' })).toBeInTheDocument();
57
57
  });
58
58
 
59
59
  it('renders section items within each group', () => {
@@ -167,17 +167,37 @@ export async function buildSdk({ dryRun = false } = {}) {
167
167
  // PATH — especially when invoked from a globally npm-installed package
168
168
  // whose lifecycle scripts run with a minimal env. Without the absolute
169
169
  // path, `bun run build:sdk` shells out to `tsc`, finds no shim on PATH,
170
- // and fails with `tsc: command not found`. Prefer the absolute path so
171
- // we don't depend on PATH inheritance.
170
+ // and fails with `tsc: command not found`.
171
+ //
172
+ // Bun's bin dir contains only `bun` and `bunx` — NOT `tsc`. So even
173
+ // when bun runs `build:sdk` (which executes the `tsc` line from
174
+ // package.json), the child shell still needs `tsc` on PATH. The npm
175
+ // global bin dir (where `npm install -g typescript` lands the shim) is
176
+ // the typical source — resolve it via `npm root -g`'s parent and
177
+ // prepend both bun's bin and the npm global bin to the child PATH.
172
178
  const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
173
179
  let cmd, args, childEnv;
174
180
  if (existsSync(bunFromHome)) {
175
181
  cmd = bunFromHome;
176
182
  args = ['run', 'build:sdk'];
177
- // Make bun's bin dir visible to the child so its tsc shim resolves.
178
- const bunBin = dirname(bunFromHome);
179
183
  const pathDelim = process.platform === 'win32' ? ';' : ':';
180
- childEnv = { ...process.env, PATH: `${bunBin}${pathDelim}${process.env.PATH || ''}` };
184
+ const bunBin = dirname(bunFromHome);
185
+ let npmGlobalBin = '';
186
+ try {
187
+ const npmRootG = execSync('npm root -g', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
188
+ // npm root -g returns .../lib/node_modules — parent is .../lib, and
189
+ // the bin dir lives at .../bin. So walk up one and over.
190
+ npmGlobalBin = join(dirname(npmRootG), '..', 'bin');
191
+ } catch {
192
+ // npm not on PATH — fall back to the standard location.
193
+ npmGlobalBin = process.platform === 'win32'
194
+ ? join(process.env.APPDATA || '', 'npm')
195
+ : join(process.env.HOME || '', '.local', 'bin');
196
+ }
197
+ const pathParts = [bunBin];
198
+ if (npmGlobalBin && existsSync(npmGlobalBin)) pathParts.push(npmGlobalBin);
199
+ pathParts.push(process.env.PATH || '');
200
+ childEnv = { ...process.env, PATH: pathParts.join(pathDelim) };
181
201
  } else if (haveCmd('bun')) {
182
202
  cmd = 'bun';
183
203
  args = ['run', 'build:sdk'];
@@ -203,6 +223,81 @@ export async function buildSdk({ dryRun = false } = {}) {
203
223
  }
204
224
  }
205
225
 
226
+ /**
227
+ * Build the dashboard SPA (Vite production bundle) so the Node server
228
+ * in `bizar-dash/src/server/server.mjs` can serve the React UI from
229
+ * `bizar-dash/dist/index.html` + `dist/assets/*`. Without this step,
230
+ * npm installs that don't ship a pre-built `dist/` (the published
231
+ * tarball excludes it because it is `.gitignore`'d) leave the
232
+ * dashboard serving 404s for every asset, and the page renders blank
233
+ * with errors like `can't access property 'useState', et is undefined`.
234
+ *
235
+ * v7.0.3 — `install.sh` + `bizar update` previously skipped the
236
+ * dashboard build entirely. Symptom: a fresh `bizar install` /
237
+ * `bizar update` overwrote the package's pre-existing `dist/` (if
238
+ * any) and the dashboard's first request returned a 404 for the
239
+ * bundle, followed by the React error above in the browser console.
240
+ *
241
+ * Same bun / npx resolution strategy as `buildSdk()`. The vite config
242
+ * reads the same `tsconfig.json` for type info, but the heavy lifting
243
+ * is `vite build` itself which uses esbuild internally, so no tsc
244
+ * dependency here.
245
+ *
246
+ * Idempotent: skips when `bizar-dash/dist/index.html` already exists.
247
+ * Non-fatal: returns `{ ok: false, message }` rather than throwing —
248
+ * the rest of the provision flow should still complete.
249
+ */
250
+ export async function buildDash({ dryRun = false } = {}) {
251
+ const dashDir = join(REPO_ROOT, 'bizar-dash');
252
+ const distIndex = join(dashDir, 'dist', 'index.html');
253
+
254
+ if (!existsSync(join(dashDir, 'src', 'web', 'index.html'))) {
255
+ return { ok: true, skipped: true, message: `Dashboard src not found at ${join(dashDir, 'src', 'web', 'index.html')} — skipping build` };
256
+ }
257
+
258
+ if (existsSync(distIndex)) {
259
+ return { ok: true, skipped: true, message: `Dashboard dist already present — skipping build (delete ${join(dashDir, 'dist')} to force)` };
260
+ }
261
+
262
+ if (dryRun) {
263
+ return { ok: true, skipped: false, message: `would build dashboard at ${dashDir}` };
264
+ }
265
+
266
+ const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
267
+ let cmd, args, childEnv;
268
+ if (existsSync(bunFromHome)) {
269
+ cmd = bunFromHome;
270
+ args = ['run', 'build:dash'];
271
+ const pathDelim = process.platform === 'win32' ? ';' : ':';
272
+ const bunBin = dirname(bunFromHome);
273
+ const pathParts = [bunBin, process.env.PATH || ''];
274
+ childEnv = { ...process.env, PATH: pathParts.join(pathDelim) };
275
+ } else if (haveCmd('bun')) {
276
+ cmd = 'bun';
277
+ args = ['run', 'build:dash'];
278
+ childEnv = process.env;
279
+ } else {
280
+ cmd = 'npx';
281
+ args = ['--yes', 'vite', 'build'];
282
+ childEnv = process.env;
283
+ }
284
+
285
+ try {
286
+ logInfo(`building dashboard with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
287
+ // `vite build` writes to the configured outDir (../../dist from
288
+ // src/web/). On a non-bun shell the cwd must be REPO_ROOT because
289
+ // the configured `root` is relative. Bun handles this either way.
290
+ execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 240_000, env: childEnv });
291
+ if (existsSync(distIndex)) {
292
+ return { ok: true, skipped: false, message: 'Dashboard built (dist/index.html present)' };
293
+ }
294
+ return { ok: false, message: `build completed but ${distIndex} still missing — check vite.config.ts outDir` };
295
+ } catch (err) {
296
+ const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
297
+ return { ok: false, message: `Dashboard build failed: ${stderr}` };
298
+ }
299
+ }
300
+
206
301
  function readTextSafe(file, fallback = '') {
207
302
  try {
208
303
  if (!existsSync(file)) return fallback;
@@ -892,6 +987,14 @@ export async function runProvision(opts = {}) {
892
987
  // full story.
893
988
  await runStep('Building SDK', () => buildSdk({ dryRun }));
894
989
 
990
+ // ── 4c. Build the dashboard SPA so dist/index.html exists. ───────────
991
+ // The Node server in `bizar-dash/src/server/server.mjs` serves the
992
+ // React UI from `bizar-dash/dist/`. The published npm tarball excludes
993
+ // `dist/` because it is `.gitignore`'d, so a fresh install must
994
+ // rebuild it. Skips when already present (idempotent). See
995
+ // `buildDash()` JSDoc for the v7.0.3 reason this step exists.
996
+ await runStep('Building dashboard', () => buildDash({ dryRun }));
997
+
895
998
  // ── 9. settings.json (MCP + hooks + permissions + env) ──────────────
896
999
  section('Writing settings.json');
897
1000
  const settingsStep = writeClaudeSettings({ dryRun, force });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "7.0.1",
3
+ "version": "7.0.4",
4
4
  "description": "Norse-pantheon multi-agent system for Claude Code — 14 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. Ships as a single npm package with the dashboard server, Claude Code MCP server, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": 1,
3
+ "agents": [
4
+ {
5
+ "agentId": "agent-ed692060-767f-4f8d-951f-6be038e63725",
6
+ "type": "coder",
7
+ "name": "ada",
8
+ "status": "terminated",
9
+ "taskCount": 0,
10
+ "createdAt": "2026-07-12T14:48:32.781Z",
11
+ "lastHeartbeatAt": "2026-07-12T14:48:32.781Z",
12
+ "terminatedAt": "2026-07-12T14:48:32.781Z",
13
+ "terminationReason": "smoke",
14
+ "gracefulTermination": true
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": 1,
3
+ "swarms": []
4
+ }
@@ -1,4 +0,0 @@
1
- import{r as o,j as e,R as ye}from"./react-vendor-D3pra6w-.js";import{u as Y}from"./Toast-WTxc3m2g.js";import{c as T,W as be,a as k,_ as ke}from"./mobile-layout-Ckru3HKJ.js";import{ae as G,bf as V,C as me,E as ie,R as O,bg as we,D as Ne,bh as Ce,aB as ce,bi as Se,i as ze,g as pe,aj as se,a6 as Ae,b1 as Re,ag as Le,Z as Ie,r as te,a4 as ne,J as Ee,t as Be,aY as Pe,X as oe,aC as xe,e as Te,d as $e,a3 as Me,ad as De,a9 as Ue}from"./icons-B0AKnYI3.js";const b=o.forwardRef(function({variant:s="secondary",size:l="md",iconOnly:c=!1,loading:r=!1,disabled:p,className:x,children:i,...n},u){return e.jsxs("button",{ref:u,type:n.type??"button",disabled:p||r,className:T("btn",`btn-${s}`,`btn-size-${l}`,c&&"btn-icon",r&&"btn-loading",x),...n,children:[r?e.jsx("span",{className:"btn-spinner","aria-hidden":!0}):null,i]})});function Qe(...a){const s=[],l=c=>{if(c){if(typeof c=="string"||typeof c=="number"){s.push(String(c));return}if(Array.isArray(c)){for(const r of c)l(r);return}if(typeof c=="object")for(const r in c)c[r]&&s.push(r)}};for(const c of a)l(c);return s.join(" ")}function E({variant:a="elevated",interactive:s=!1,className:l,children:c,...r}){return e.jsx("div",{className:T("card",`card-${a}`,s&&"card-interactive",l),...r,children:c})}function B({children:a,className:s}){return e.jsx("h3",{className:T("card-title",s),children:a})}function P({children:a,className:s}){return e.jsx("div",{className:T("card-meta",s),children:a})}const ae=[{id:"bizar",name:"Bizar CLI"},{id:"bizar-dash",name:"Dashboard"},{id:"bizar-plugin",name:"Opencode Plugin"}];function Je(){const a=Y(),[s,l]=o.useState({current:{},latest:null,checking:!1,updating:!1,hasUpdates:!1,requiresRestart:!1,perPackage:{}});o.useEffect(()=>{const i=new be;return i.on(n=>{(n.type==="update:progress"||n.type==="update:log"||n.type==="update:complete")&&l(u=>{if(n.type==="update:complete")return{...u,updating:!1,requiresRestart:!!n.requiresRestart};if(n.type==="update:log"){const j=n,y=u.perPackage[j.pkg]||{logs:[]};return{...u,perPackage:{...u.perPackage,[j.pkg]:{...y,logs:[...(y.logs||[]).slice(-50),j.line]}}}}const g=n;return{...u,perPackage:{...u.perPackage,[g.pkg]:{...u.perPackage[g.pkg],status:g.status,error:g.error,newVersion:g.newVersion}}}})}),()=>i.close()},[]),o.useEffect(()=>{k.get("/updates/status").then(i=>l(n=>({...n,current:i.current}))).catch(i=>l(n=>({...n,error:i.message})))},[]);const c=async()=>{l(i=>({...i,checking:!0,error:void 0}));try{const i=await k.get("/updates/check");l(n=>({...n,checking:!1,current:i.current,latest:i.latest,hasUpdates:i.hasUpdates}))}catch(i){l(n=>({...n,checking:!1,error:i.message}))}},r=async()=>{if(confirm("Update Bizar packages? The dashboard will restart automatically.")){l(i=>({...i,updating:!0,requiresRestart:!1,perPackage:{},error:void 0}));try{await k.post("/updates/apply",{packages:["bizar","bizar-dash","bizar-plugin"]})}catch(i){l(n=>({...n,updating:!1,error:i.message}))}}},p=async()=>{if(confirm("Restart the dashboard? You will be disconnected briefly."))try{await k.post("/restart"),a.info("Restarting…",3e3),setTimeout(()=>window.location.reload(),3e3)}catch{a.error("Restart failed")}},x=s.checking||s.updating;return e.jsxs(E,{id:"settings-updates","data-section":"updates",children:[e.jsxs(B,{children:[e.jsx(G,{size:14})," Updates"]}),e.jsx(P,{children:"Check installed Bizar packages and apply dashboard updates."}),e.jsxs("div",{className:"updates-current",children:[e.jsx("h4",{children:"Installed versions"}),e.jsx("ul",{children:ae.map(i=>e.jsxs("li",{children:[e.jsx("span",{children:i.name}),e.jsx("code",{className:"mono",children:s.current[i.id]||"—"})]},i.id))})]}),s.latest&&e.jsxs("div",{className:"updates-latest",children:[e.jsx("h4",{children:"Latest available"}),e.jsx("ul",{children:ae.map(i=>{var j;const n=s.current[i.id],u=(j=s.latest)==null?void 0:j[i.id],g=n&&u&&n!==u;return e.jsxs("li",{className:g?"updates-outdated":"updates-current-version",children:[e.jsx("span",{children:i.name}),e.jsxs("code",{className:"mono",children:[u||"—",g&&e.jsx("span",{className:"updates-badge",children:"update available"})]})]},i.id)})})]}),s.updating&&e.jsx("div",{className:"updates-progress-rows",children:ae.map(i=>{const n=s.perPackage[i.id]||{status:"idle",logs:[]};return e.jsxs("div",{className:"updates-pkg-row",children:[e.jsxs("div",{className:"updates-pkg-row-header",children:[e.jsx("span",{className:"updates-pkg-name",children:i.name}),e.jsxs("div",{className:"updates-pkg-status",children:[n.status==="starting"&&e.jsx("span",{className:"btn-spinner"}),n.status==="installing"&&e.jsx("span",{className:"btn-spinner"}),n.status==="done"&&e.jsx(V,{size:14,className:"icon-success"}),n.status==="error"&&e.jsx(me,{size:14,className:"icon-error"}),e.jsx("span",{children:n.status}),n.newVersion&&e.jsxs("code",{className:"mono",style:{fontSize:11},children:["→ ",n.newVersion]})]})]}),n.logs.length>0&&e.jsxs("details",{className:"updates-pkg-logs",children:[e.jsxs("summary",{children:["npm output (",n.logs.length," lines)"]}),e.jsx("pre",{children:n.logs.join(`
2
- `)})]}),n.status==="error"&&n.error&&e.jsxs("div",{className:"updates-pkg-error",children:[e.jsx(ie,{size:12})," ",n.error]})]},i.id)})}),e.jsxs("div",{className:"updates-actions",children:[e.jsxs(b,{onClick:c,disabled:x,children:[s.checking?e.jsx("span",{className:"btn-spinner"}):e.jsx(O,{size:14}),"Check for updates"]}),e.jsxs(b,{variant:"primary",onClick:r,disabled:!s.hasUpdates||s.updating,children:[s.updating?e.jsx("span",{className:"btn-spinner"}):e.jsx(G,{size:14}),s.updating?"Updating…":s.hasUpdates?"Update now":"Up to date"]}),s.requiresRestart&&e.jsxs(b,{variant:"danger",onClick:p,children:[e.jsx(O,{size:14})," Restart Dashboard"]})]}),s.error&&e.jsxs("div",{className:"updates-error",children:[e.jsx(ie,{size:14}),e.jsx("span",{children:s.error})]})]})}function _e(a,s,{delay:l=800,onSaved:c,onError:r}={}){const[p,x]=o.useState(a),[i,n]=o.useState("idle"),u=o.useRef(null),g=o.useRef(null),j=o.useRef(a),y=o.useRef(!1),S=o.useCallback(async m=>{n("saving");try{await s(m),j.current=m,g.current=null,n("saved"),c==null||c(),setTimeout(()=>n("idle"),2e3)}catch(f){n("error"),r==null||r(f)}},[s,c,r]),L=o.useCallback(m=>{x(m),g.current=m,y.current=!0,u.current&&clearTimeout(u.current),u.current=setTimeout(()=>{u.current=null,y.current=!1;const f=g.current;g.current=null,f!==null&&S(f)},l)},[l,S]),C=o.useCallback(()=>{u.current&&(clearTimeout(u.current),u.current=null,y.current=!1)},[]),h=o.useCallback(async()=>{if(u.current){clearTimeout(u.current),u.current=null,y.current=!1;const m=g.current;g.current=null,m!==null&&await S(m)}},[S]);return o.useEffect(()=>()=>{if(u.current){clearTimeout(u.current),u.current=null;const m=g.current;g.current=null,m!==null&&(n("saving"),s(m).then(()=>{j.current=m,n("saved"),c==null||c()}).catch(f=>{n("error"),r==null||r(f)}).finally(()=>{setTimeout(()=>n("idle"),2e3)}))}},[s,c,r]),{value:p,setValue:L,status:i,save:S,flush:h,clearDebounce:C,hasPendingDebounceRef:y,pendingValueRef:g}}function de({initialValue:a,saveFn:s,delay:l=800,render:c,className:r}){const{value:p,setValue:x,status:i,save:n,clearDebounce:u,hasPendingDebounceRef:g,pendingValueRef:j}=_e(a,s,{delay:l}),y=ye.useCallback(()=>{g.current&&(u(),j.current!==null&&n(j.current))},[u,g,n,j]);return e.jsxs("div",{className:`autosave-field ${r||""} ${i}`,children:[c({value:p,onChange:x,onBlur:y}),e.jsxs("span",{className:"autosave-status",role:"status","aria-live":"polite",children:[i==="saving"&&e.jsx(we,{className:"spinning",size:12}),i==="saved"&&e.jsx(Ne,{size:12}),i==="error"&&e.jsx(me,{size:12})]})]})}const He=[{id:"topnav",label:"Top nav"},{id:"sidebar",label:"Sidebar"},{id:"both",label:"Both"}];function es({settings:a,patchUi:s,patchTop:l,autoSave:c}){return e.jsxs(e.Fragment,{children:[e.jsxs(E,{id:"settings-layout","data-section":"layout",children:[e.jsxs(B,{children:[e.jsx(Ce,{size:14})," UI layout"]}),e.jsx(P,{children:"Choose how the dashboard's navigation is presented."}),e.jsx("div",{className:"layout-row","data-setting-id":"ui.layout",children:He.map(r=>e.jsx("button",{type:"button",className:T("layout-card",a.ui.layout===r.id&&"layout-card-active"),onClick:()=>s({layout:r.id}),children:e.jsx("span",{className:"layout-card-label",children:r.label})},r.id))}),e.jsxs("label",{className:"checkbox-row","data-setting-id":"ui.showHeader",children:[e.jsx("input",{type:"checkbox",checked:a.ui.showHeader,onChange:r=>s({showHeader:r.target.checked})}),e.jsx("span",{children:"Show header"})]}),e.jsxs("label",{className:"checkbox-row","data-setting-id":"ui.showStatusBar",children:[e.jsx("input",{type:"checkbox",checked:a.ui.showStatusBar,onChange:r=>s({showStatusBar:r.target.checked})}),e.jsx("span",{children:"Show status bar"})]}),e.jsxs("div",{className:"field","data-setting-id":"ui.defaultTab",children:[e.jsx("label",{className:"field-label",children:"Default tab"}),e.jsxs("select",{className:"select",value:a.ui.defaultTab,onChange:r=>s({defaultTab:r.target.value}),children:[e.jsx("option",{value:"overview",children:"Overview"}),e.jsx("option",{value:"chat",children:"Chat"}),e.jsx("option",{value:"agents",children:"Agents"}),e.jsx("option",{value:"artifacts",children:"Plans"}),e.jsx("option",{value:"projects",children:"Projects"}),e.jsx("option",{value:"tasks",children:"Tasks"}),e.jsx("option",{value:"config",children:"Config"}),e.jsx("option",{value:"settings",children:"Settings"}),e.jsx("option",{value:"mods",children:"Mods"}),e.jsx("option",{value:"schedules",children:"Schedules"})]})]})]}),e.jsxs(E,{id:"settings-general","data-section":"general",children:[e.jsx(B,{children:"General"}),e.jsx(P,{children:"Default agent + model override."}),e.jsxs("div",{className:"field","data-setting-id":"defaultAgent",children:[e.jsx("label",{className:"field-label",htmlFor:"set-default-agent",children:"Default agent"}),e.jsx(de,{initialValue:a.defaultAgent||"",saveFn:async r=>{l("defaultAgent",r),c==null||c("defaultAgent",r)},render:({value:r,onChange:p,onBlur:x})=>e.jsx("input",{id:"set-default-agent",className:"input",type:"text",placeholder:"e.g. odin",value:r,onChange:i=>p(i.target.value),onBlur:x})})]}),e.jsxs("div",{className:"field","data-setting-id":"defaultModel",children:[e.jsx("label",{className:"field-label",htmlFor:"set-default-model",children:"Model override"}),e.jsx(de,{initialValue:a.defaultModel||"",saveFn:async r=>{l("defaultModel",r),c==null||c("defaultModel",r)},render:({value:r,onChange:p,onBlur:x})=>e.jsx("input",{id:"set-default-model",className:"input",type:"text",placeholder:"(leave empty for provider default)",value:r,onChange:i=>p(i.target.value),onBlur:x})})]})]})]})}function ss({initialStatus:a}){var m;const[s,l]=o.useState(a??null),[c,r]=o.useState(""),[p,x]=o.useState(!1),[i,n]=o.useState(null);o.useEffect(()=>{s===null&&k.get("/tailscale/status").then(f=>l(f)).catch(()=>l(null))},[]);const u=async()=>{try{const f=await k.get("/tailscale/status");l(f)}catch{n("Failed to load Tailscale status")}},g=async()=>{if(c.trim()){x(!0),n(null);try{await k.post("/tailscale/setup",{authKey:c.trim()}),await u(),r("")}catch(f){n(f.message||"Authentication failed")}finally{x(!1)}}},j=async()=>{var f,w,z;if(s){x(!0),n(null);try{await k.post("/tailscale/setup",{port:((f=s.settings)==null?void 0:f.port)||4321,https:((w=s.settings)==null?void 0:w.https)!==!1,hostname:((z=s.settings)==null?void 0:z.hostname)||""}),await u()}catch($){n($.message||"Setup failed")}finally{x(!1)}}},y=async()=>{x(!0),n(null);try{await k.post("/tailscale/unserve"),await u()}catch(f){n(f.message||"Remove failed")}finally{x(!1)}},S=(s==null?void 0:s.installed)??!1,L=(s==null?void 0:s.authenticated)??!1,C=((m=s==null?void 0:s.settings)==null?void 0:m.enabled)??!1,h=C?`https://${(s==null?void 0:s.hostname)||"bizar-dash"}`:null;return S?e.jsxs(E,{id:"settings-tailscale-auth","data-section":"tailscale",children:[e.jsxs(B,{children:[e.jsx(ce,{size:14})," Tailscale Integration"]}),e.jsx(P,{children:"Expose the dashboard over your Tailscale network using an auth key."}),i&&e.jsx("p",{style:{color:"var(--color-error, #f85149)",fontSize:"0.85rem"},children:i}),L?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"status-row",style:{display:"flex",alignItems:"center",gap:"0.5rem",marginBottom:"0.75rem"},children:[e.jsx(V,{size:16,style:{color:"var(--color-success, #3fb950)"}}),e.jsxs("span",{children:["Authenticated as ",e.jsx("strong",{children:(s==null?void 0:s.hostname)||"unknown"})]})]}),C&&h?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"status-row",style:{display:"flex",alignItems:"center",gap:"0.5rem",marginBottom:"0.75rem"},children:[e.jsx(Se,{size:16}),e.jsx("a",{href:h,target:"_blank",rel:"noreferrer",children:h})]}),e.jsx(b,{variant:"secondary",size:"sm",onClick:y,disabled:p,children:"Remove serve"})]}):e.jsx(b,{variant:"primary",size:"sm",onClick:j,disabled:p,children:"Set up Tailscale serve"})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"muted",style:{marginBottom:"0.75rem"},children:"Not authenticated with Tailscale."}),e.jsxs("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[e.jsx("input",{type:"text",className:"input",placeholder:"tskey-...",value:c,onChange:f=>r(f.target.value),style:{flex:1,minWidth:"200px"},disabled:p}),e.jsx(b,{variant:"primary",size:"sm",onClick:g,disabled:p||!c.trim(),children:"Authenticate"})]})]})]}):e.jsxs(E,{id:"settings-tailscale-auth","data-section":"tailscale",children:[e.jsxs(B,{children:[e.jsx(ce,{size:14})," Tailscale Integration"]}),e.jsx(P,{children:"Tailscale is not installed on this machine."}),e.jsxs("p",{className:"muted",children:["Install from"," ",e.jsx("a",{href:"https://tailscale.com/download",target:"_blank",rel:"noreferrer",children:"tailscale.com/download"})]})]})}function as({settings:a,patchTop:s}){var l,c,r;return e.jsxs(E,{id:"settings-system-llm","data-section":"system-llm",children:[e.jsxs(B,{children:[e.jsx(ze,{size:14})," System LLM API"]}),e.jsx(P,{children:"Configures the LLM used for automatic title generation, prompt enhancement, summarization, and other system-level calls."}),e.jsxs("label",{className:"checkbox-row","data-setting-id":"systemLlm.enabled",children:[e.jsx("input",{type:"checkbox",checked:!!((l=a.systemLlm)!=null&&l.enabled),onChange:p=>{const x=a.systemLlm||{enabled:!0,provider:"cline",model:"cline/deepseek-v4-flash-free"};s("systemLlm",{...x,enabled:p.target.checked})}}),e.jsx("span",{children:"Enable system LLM calls"})]}),e.jsxs("div",{className:"field","data-setting-id":"systemLlm.provider",style:{marginTop:"var(--space-3)"},children:[e.jsx("label",{className:"field-label",htmlFor:"set-system-llm-provider",children:"Provider"}),e.jsxs("select",{id:"set-system-llm-provider",className:"select",value:((c=a.systemLlm)==null?void 0:c.provider)||"cline",onChange:p=>{const x=a.systemLlm||{enabled:!0,provider:"cline",model:"cline/deepseek-v4-flash-free"};s("systemLlm",{...x,provider:p.target.value})},children:[e.jsx("option",{value:"cline",children:"Cline"}),e.jsx("option",{value:"openrouter",children:"OpenRouter"}),e.jsx("option",{value:"minimax",children:"MiniMax"})]})]}),e.jsxs("div",{className:"field","data-setting-id":"systemLlm.model",children:[e.jsx("label",{className:"field-label",htmlFor:"set-system-llm-model",children:"Model"}),e.jsx("input",{id:"set-system-llm-model",className:"input mono",type:"text",placeholder:"cline/deepseek-v4-flash-free",value:((r=a.systemLlm)==null?void 0:r.model)||"cline/deepseek-v4-flash-free",onChange:p=>{const x=a.systemLlm||{enabled:!0,provider:"cline",model:"cline/deepseek-v4-flash-free"};s("systemLlm",{...x,model:p.target.value})}}),e.jsxs("p",{className:"field-help",children:["The API key is read from ",e.jsx("code",{children:"auth.json"})," for the selected provider. Leave the default model for best results."]})]}),e.jsxs("div",{style:{marginTop:"var(--space-4)"},children:[e.jsx("h4",{style:{margin:"0 0 var(--space-2)"},children:"Features using this API"}),e.jsxs("ul",{className:"settings-feature-list",style:{margin:0,paddingLeft:"var(--space-4)",lineHeight:1.8},children:[e.jsx("li",{children:"Auto-title generation for new chat sessions"}),e.jsx("li",{children:"Auto-title generation for new tasks"}),e.jsx("li",{children:'"Enhance prompt" button in the task input'}),e.jsx("li",{children:'"Enhance prompt" button in the chat composer'}),e.jsx("li",{className:"muted",style:{fontSize:12},children:"Future: summarization, name generation, and more"})]})]})]})}const Ve=[{value:"anthropic",label:"Anthropic"},{value:"anyllm",label:"AnyLLM"},{value:"litellm-anthropic",label:"LiteLLM (Anthropic)"},{value:"litellm-openai",label:"LiteLLM (OpenAI)"}];function Oe({settings:a,onPatch:s}){const l=Y(),[c,r]=o.useState(null),[p,x]=o.useState(null),[i,n]=o.useState(!1),[u,g]=o.useState(!1),[j,y]=o.useState(!1),[S,L]=o.useState(!1),[C,h]=o.useState(!1),[m,f]=o.useState(!1),w=o.useCallback(async()=>{n(!0);try{const[d,U]=await Promise.all([k.get("/headroom/status"),k.get("/headroom/stats?hours=24").catch(()=>({error:"no stats"}))]);r(d),U.error||x(U)}catch(d){l.error(`Failed to load Headroom status: ${d.message}`)}finally{n(!1)}},[l]);o.useEffect(()=>{w()},[w]);const z=async()=>{g(!0);try{const d=await k.post("/headroom/install",{force:!0});d.installed?(l.success(`Headroom installed via ${d.method}.`),await w()):l.error('Headroom install failed. Try: pip install "headroom-ai[all]"')}catch(d){l.error(`Install failed: ${d.message}`)}finally{g(!1)}},$=async()=>{y(!0);try{(await k.post("/headroom/wrap",{port:a.port})).ok?(l.success("cline wrapped with Headroom."),await w()):l.error("Wrap failed.")}catch(d){l.error(`Wrap failed: ${d.message}`)}finally{y(!1)}},_=async()=>{L(!0);try{(await k.post("/headroom/unwrap")).ok?(l.success("cline unwrapped from Headroom."),await w()):l.error("Unwrap failed.")}catch(d){l.error(`Unwrap failed: ${d.message}`)}finally{L(!1)}},W=async()=>{h(!0);try{(await k.post("/headroom/proxy/start",{port:a.port,host:a.host})).ok?(l.success(`Headroom proxy started on ${a.host}:${a.port}.`),await w()):l.error("Proxy start failed.")}catch(d){l.error(`Proxy start failed: ${d.message}`)}finally{h(!1)}},K=async()=>{f(!0);try{(await k.post("/headroom/proxy/stop")).ok?(l.success("Headroom proxy stopped."),await w()):l.error("Proxy stop failed.")}catch(d){l.error(`Proxy stop failed: ${d.message}`)}finally{f(!1)}},Z=async()=>{try{const{spawn:d}=await ke(async()=>{const{spawn:U}=await import("./__vite-browser-external-BIHI7g3E.js");return{spawn:U}},[]);d("headroom",["dashboard"],{detached:!0,stdio:"ignore"})}catch{l.error("Could not open Headroom dashboard. Run `headroom dashboard` manually.")}},{installed:R,version:q,proxyRunning:M,proxyPort:X,wrapped:A}=c||{},H=(p==null?void 0:p.compressionRatio)??0,D=(p==null?void 0:p.tokensSaved)??0;return e.jsx("div",{id:"settings-headroom","data-section":"headroom",children:e.jsxs(E,{children:[e.jsxs(B,{children:[e.jsx(pe,{size:14,style:{color:M?"var(--success)":"var(--text-dim)"}}),"Headroom",R&&q&&e.jsxs("span",{style:{fontSize:11,color:"var(--text-dim)",marginLeft:6},children:["v",q," ",M?"✓":"✗"]}),!R&&e.jsx("span",{style:{fontSize:11,color:"var(--error)",marginLeft:6},children:"not installed"})]}),e.jsx(P,{children:"Context compression for token efficiency. Compresses tool outputs, logs, and conversation history by 60–95% before they reach the model."}),e.jsxs("div",{style:{display:"flex",gap:16,flexWrap:"wrap",marginTop:12,marginBottom:12},children:[e.jsxs("div",{className:"headroom-status-item",children:[R?e.jsx(V,{size:12,style:{color:"var(--success)"}}):e.jsx(se,{size:12,style:{color:"var(--error)"}}),e.jsx("span",{children:R?"Installed":"Not installed"})]}),e.jsxs("div",{className:"headroom-status-item",children:[M?e.jsx(V,{size:12,style:{color:"var(--success)"}}):e.jsx(se,{size:12,style:{color:"var(--text-dim)"}}),e.jsxs("span",{children:["Proxy ",M?`${X} ✓`:"stopped"]})]}),e.jsxs("div",{className:"headroom-status-item",children:[A?e.jsx(V,{size:12,style:{color:"var(--success)"}}):e.jsx(se,{size:12,style:{color:"var(--text-dim)"}}),e.jsxs("span",{children:["cline ",A?"wrapped":"not wrapped"]})]})]}),(D>0||H>0)&&e.jsxs("div",{style:{display:"flex",gap:24,marginBottom:12},children:[e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:20,fontWeight:700,color:"var(--success)"},children:H>0?`${Math.round(H*100)}%`:"—"}),e.jsx("div",{className:"muted",style:{fontSize:11},children:"Compression ratio"})]}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:20,fontWeight:700,color:"var(--accent)"},children:D>0?D.toLocaleString():"—"}),e.jsx("div",{className:"muted",style:{fontSize:11},children:"Tokens saved (24h)"})]})]}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:8},children:[R?e.jsxs(e.Fragment,{children:[e.jsxs(b,{variant:"secondary",size:"sm",onClick:$,disabled:j||A,title:A?"cline is already wrapped":"Wrap cline to route through Headroom proxy",children:[j?e.jsx("span",{className:"btn-spinner"}):e.jsx(Ae,{size:13}),j?"Wrapping…":A?"Wrapped ✓":"Wrap cline"]}),A&&e.jsxs(b,{variant:"ghost",size:"sm",onClick:_,disabled:S,children:[S?e.jsx("span",{className:"btn-spinner"}):e.jsx(O,{size:13}),S?"Unwrapping…":"Unwrap"]})]}):e.jsxs(b,{variant:"primary",size:"sm",onClick:z,disabled:u,children:[u?e.jsx("span",{className:"btn-spinner"}):e.jsx(G,{size:13}),u?"Installing…":"Install Headroom"]}),R&&e.jsxs(e.Fragment,{children:[M?e.jsxs(b,{variant:"secondary",size:"sm",onClick:K,disabled:m,children:[m?e.jsx("span",{className:"btn-spinner"}):e.jsx(Re,{size:13}),m?"Stopping…":"Stop proxy"]}):e.jsxs(b,{variant:"secondary",size:"sm",onClick:W,disabled:C,children:[C?e.jsx("span",{className:"btn-spinner"}):e.jsx(Le,{size:13}),C?"Starting…":"Start proxy"]}),e.jsxs(b,{variant:"ghost",size:"sm",onClick:Z,children:[e.jsx(Ie,{size:13}),"Dashboard"]})]}),e.jsx(b,{variant:"ghost",size:"sm",onClick:w,disabled:i,children:e.jsx(O,{size:13,className:i?"spin":""})})]}),e.jsxs("div",{style:{marginTop:16},children:[e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.enabled,onChange:d=>s({enabled:d.target.checked})}),e.jsx("span",{children:"Enable Headroom"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.autoInstall,onChange:d=>s({autoInstall:d.target.checked})}),e.jsx("span",{children:"Auto-install if missing on dashboard startup"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.autoStart,onChange:d=>s({autoStart:d.target.checked})}),e.jsx("span",{children:"Auto-start proxy on dashboard startup"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.autoWrap,onChange:d=>s({autoWrap:d.target.checked})}),e.jsx("span",{children:"Auto-wrap cline on dashboard startup"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.routeAllProviders,onChange:d=>s({routeAllProviders:d.target.checked})}),e.jsx("span",{children:"Route all providers through Headroom proxy"})]}),e.jsxs("div",{className:"task-form-row",style:{marginTop:12},children:[e.jsxs("div",{className:"task-form-field",children:[e.jsx("label",{className:"field-label",children:"Proxy port"}),e.jsx("input",{type:"number",className:"input",value:a.port,min:1,max:65535,onChange:d=>s({port:Math.max(1,Math.min(65535,parseInt(d.target.value,10)||8787))})})]}),e.jsxs("div",{className:"task-form-field",children:[e.jsx("label",{className:"field-label",children:"Proxy host"}),e.jsx("input",{type:"text",className:"input",value:a.host,onChange:d=>s({host:d.target.value})})]})]}),e.jsxs("div",{className:"task-form-row",children:[e.jsxs("div",{className:"task-form-field",children:[e.jsx("label",{className:"field-label",children:"Backend"}),e.jsx("select",{className:"select",value:a.backend,onChange:d=>s({backend:d.target.value}),children:Ve.map(d=>e.jsx("option",{value:d.value,children:d.label},d.value))})]}),e.jsxs("div",{className:"task-form-field",children:[e.jsx("label",{className:"field-label",children:"Monthly budget (USD)"}),e.jsx("input",{type:"number",className:"input",value:a.budget,min:0,step:1,onChange:d=>s({budget:Math.max(0,parseFloat(d.target.value)||0)})}),e.jsx("span",{className:"field-help",children:"0 = unlimited"})]})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.outputShaper,onChange:d=>s({outputShaper:d.target.checked})}),e.jsx("span",{children:"Enable output shaper"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.telemetry,onChange:d=>s({telemetry:d.target.checked})}),e.jsx("span",{children:"Enable telemetry"})]})]})]})})}const ue={enabled:!0,autoInstall:!0,port:8787,host:"127.0.0.1",outputShaper:!1,telemetry:!1,budget:0,backend:"anthropic",autoStart:!0,autoWrap:!0,routeAllProviders:!0};function ts({settings:a,setSettings:s,setDirty:l}){return e.jsx(Oe,{settings:a.headroom||ue,onPatch:c=>{s(r=>({...r,headroom:{...r.headroom||ue,...c}})),l(!0)}})}function Fe(){const a=Y(),[s,l]=o.useState([]),[c,r]=o.useState(new Set),[p,x]=o.useState(!0),[i,n]=o.useState(""),[u,g]=o.useState(!0),j=o.useCallback(async()=>{x(!0);try{const[h,m]=await Promise.all([k.get("/activity"),k.get("/activity/hidden")]);l(Array.isArray(h.items)?h.items:[]),r(new Set(m.hidden||[]))}catch(h){a.error(`Load failed: ${h.message}`)}finally{x(!1)}},[a]);o.useEffect(()=>{j()},[j]);const y=(h,m)=>{const f=`${h.kind||""}|${h.ts||""}|${h.slug||""}|${m}`;let w=0;for(let z=0;z<f.length;z++)w=(w<<5)-w+f.charCodeAt(z)|0;return Math.abs(w).toString(16).padStart(8,"0").slice(0,16)},S=async()=>{try{await k.del("/activity/hide"),r(new Set),a.success("All hidden activity restored.")}catch(h){a.error(`Restore failed: ${h.message}`)}},L=async h=>{try{await k.del(`/activity/hide/${encodeURIComponent(h)}`);const m=new Set(c);m.delete(h),r(m)}catch(m){a.error(`Restore failed: ${m.message}`)}},C=s.filter((h,m)=>{const f=y(h,m);if(!u&&c.has(f))return!1;if(!i)return!0;const w=i.toLowerCase();return[h.kind,h.slug,h.message].some(z=>typeof z=="string"&&z.toLowerCase().includes(w))});return e.jsxs(E,{id:"settings-activity-log","data-section":"activity-log",children:[e.jsxs(B,{children:[e.jsx(pe,{size:14})," Activity log",e.jsxs("span",{className:"muted",style:{fontWeight:400,marginLeft:8,fontSize:12},children:[s.length," total · ",c.size," hidden"]}),e.jsx(b,{variant:"ghost",size:"sm",style:{marginLeft:"auto"},onClick:j,title:"Reload",children:e.jsx(O,{size:12})})]}),e.jsxs(P,{children:["Full history from ",e.jsx("code",{children:"~/.bizar/activity.log"}),". Hiding an item in the Overview only hides it there — the entry stays here."]}),e.jsxs("div",{className:"activity-log-toolbar",children:[e.jsxs("div",{className:"activity-log-search",children:[e.jsx(te,{size:12}),e.jsx("input",{type:"text",className:"input",placeholder:"Filter by kind, slug, or message…",value:i,onChange:h=>n(h.target.value)})]}),e.jsxs("label",{className:"activity-log-toggle",children:[e.jsx("input",{type:"checkbox",checked:u,onChange:h=>g(h.target.checked)}),"Show hidden"]}),e.jsxs(b,{variant:"ghost",size:"sm",disabled:c.size===0,onClick:S,title:"Restore all hidden items to the Overview",children:[e.jsx(ne,{size:12})," Restore all"]})]}),p?e.jsx("div",{className:"muted",style:{padding:"12px 0",fontSize:12},children:"Loading…"}):C.length===0?e.jsx("div",{className:"muted",style:{padding:"12px 0",fontSize:12},children:s.length===0?"No activity yet.":"No items match the current filter."}):e.jsxs("div",{className:"activity-log-table-wrap",children:[e.jsxs("table",{className:"activity-log-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Kind"}),e.jsx("th",{children:"Detail"}),e.jsx("th",{children:"Time"}),e.jsx("th",{})]})}),e.jsx("tbody",{children:C.slice(0,200).map((h,m)=>{const f=y(h,m),w=c.has(f);return e.jsxs("tr",{className:T(w&&"activity-log-row-hidden"),children:[e.jsx("td",{className:"activity-log-kind",children:h.kind||"activity"}),e.jsx("td",{className:"activity-log-detail",children:h.message||h.slug||"—"}),e.jsx("td",{className:"activity-log-time mono",children:h.ts?new Date(h.ts).toLocaleString():"—"}),e.jsx("td",{children:w?e.jsx(b,{variant:"ghost",size:"sm",onClick:()=>L(f),title:"Restore to Overview",children:e.jsx(ne,{size:12})}):e.jsx("span",{className:"activity-log-state-tag",children:"shown"})})]},`${h.ts}-${m}`)})})]}),C.length>200&&e.jsxs("div",{className:"muted",style:{fontSize:11,padding:"8px 0"},children:["Showing first 200 of ",C.length,"."]})]})]})}function We({about:a}){return e.jsxs(E,{id:"settings-about","data-section":"about",children:[e.jsxs(B,{children:[e.jsx(Ee,{size:14})," About"]}),e.jsx(P,{children:"Build metadata."}),e.jsxs("dl",{className:"about-table",children:[e.jsx("dt",{children:"Version"}),e.jsx("dd",{className:"mono",children:(a==null?void 0:a.version)||"—"}),e.jsx("dt",{children:"Homepage"}),e.jsx("dd",{children:e.jsx("a",{href:(a==null?void 0:a.homepage)||"https://github.com/DrB0rk/BizarHarness",target:"_blank",rel:"noopener noreferrer",children:(a==null?void 0:a.homepage)||"https://github.com/DrB0rk/BizarHarness"})}),e.jsx("dt",{children:"License"}),e.jsx("dd",{children:(a==null?void 0:a.license)||"MIT"})]})]})}function ns({about:a}){return e.jsxs(e.Fragment,{children:[e.jsx(Fe,{}),e.jsx(We,{about:a})]})}function Ke({onError:a}){const s=Y(),[l,c]=o.useState([]),[r,p]=o.useState(!1),[x,i]=o.useState(!1),[n,u]=o.useState(new Set),[g,j]=o.useState(null),[y,S]=o.useState(""),[L,C]=o.useState(!1),[h,m]=o.useState(""),[f,w]=o.useState(""),[z,$]=o.useState(!1),[_,W]=o.useState(""),[K,Z]=o.useState(!1),[R,q]=o.useState(""),[M,X]=o.useState(new Set),A=o.useCallback(async()=>{p(!0);try{const t=await k.get("/env-vars");c(t)}catch(t){s.error(`Load failed: ${t.message}`),a==null||a(t.message)}finally{p(!1)}},[s,a]);o.useEffect(()=>{A()},[A]);const H=t=>{u(N=>{const v=new Set(N);return v.has(t)?v.delete(t):v.add(t),v})},D=async()=>{const t=h.trim().toUpperCase(),N=f.trim();if(!t||!/^BIZAR_[A-Z0-9_]+$/.test(t)){s.warning("Name must match BIZAR_<NAME> (e.g. BIZAR_MINIMAX_KEY)");return}if(!N){s.warning("Value is required.");return}if(l.some(v=>v.name===t)){s.warning(`${t} already exists.`);return}i(!0);try{await k.post("/env-vars",{name:t,value:N}),s.success(`${t} created.`),C(!1),m(""),w(""),await A()}catch(v){s.error(`Create failed: ${v.message}`)}finally{i(!1)}},d=t=>{j(t.name),S(t.value===t.value.replace(/\*/g,"")?t.value:""),u(N=>{const v=new Set(N);return v.add(t.name),v})},U=async t=>{if(!y.trim()){s.warning("Value cannot be empty.");return}i(!0);try{await k.put(`/env-vars/${encodeURIComponent(t)}`,{value:y.trim()}),s.success(`${t} updated.`),j(null),await A()}catch(N){s.error(`Update failed: ${N.message}`)}finally{i(!1)}},re=async t=>{if(confirm(`Delete ${t}? This cannot be undone.`))try{await k.del(`/env-vars/${encodeURIComponent(t)}`),s.success(`${t} deleted.`),await A()}catch(N){s.error(`Delete failed: ${N.message}`)}},ge=async()=>{if(!_.trim()){s.warning("Nothing to import.");return}Z(!0);try{const t=await k.post("/env-vars/bulk-import",{envContent:_});s.success(`Imported ${t.imported}, skipped ${t.skipped}.`),t.errors.length>0&&s.warning(`${t.errors.length} lines had errors: ${t.errors.slice(0,3).join("; ")}${t.errors.length>3?"…":""}`),W(""),$(!1),await A()}catch(t){s.error(`Bulk import failed: ${t.message}`)}finally{Z(!1)}},je=async()=>{try{const t=await k.get("/env-vars/export"),N=new Blob([t],{type:"text/plain"}),v=URL.createObjectURL(N),I=document.createElement("a");I.href=v,I.download=".env",I.click(),URL.revokeObjectURL(v),s.success("Exported .env file.")}catch(t){s.error(`Export failed: ${t.message}`)}},F=o.useMemo(()=>{if(!R.trim())return l;const t=R.toLowerCase();return l.filter(N=>N.name.toLowerCase().includes(t))},[l,R]),Q=o.useMemo(()=>{const t={};for(const N of F){const v=N.name.split("_")[0]+"_";(t[v]??(t[v]=[])).push(N)}return t},[F]),fe=t=>{X(N=>{const v=new Set(N);return v.has(t)?v.delete(t):v.add(t),v})},le=o.useMemo(()=>{const t=["BIZAR_","PROVIDER_","MODEL_","API_"];return Object.keys(Q).sort((v,I)=>{const J=t.indexOf(v),ee=t.indexOf(I);return J===-1&&ee===-1?v.localeCompare(I):J===-1?1:ee===-1?-1:J-ee})},[Q]);if(r)return e.jsx("div",{className:"muted",style:{padding:"16px 0",fontSize:13},children:"Loading env vars…"});const ve=le.length>1;return e.jsxs("div",{className:"env-var-manager",children:[e.jsxs("div",{className:"env-var-toolbar",children:[e.jsxs(b,{variant:"primary",size:"sm",onClick:()=>C(!0),children:[e.jsx(Be,{size:12})," New variable"]}),e.jsxs(b,{variant:"secondary",size:"sm",onClick:()=>$(t=>!t),className:z?"active":"",children:[e.jsx(Pe,{size:12})," Bulk import"]}),e.jsxs(b,{variant:"ghost",size:"sm",onClick:je,title:"Export as .env",children:[e.jsx(G,{size:12})," Export"]}),e.jsxs("div",{className:"env-var-search-wrap",children:[e.jsx(te,{size:12,className:"env-var-search-icon"}),e.jsx("input",{className:"env-var-search input",placeholder:"Filter…",value:R,onChange:t=>q(t.target.value)})]}),e.jsx(b,{variant:"ghost",size:"sm",onClick:A,title:"Reload",children:e.jsx(O,{size:12})})]}),z&&e.jsxs("div",{className:"env-var-bulk-panel",children:[e.jsxs("div",{className:"env-var-bulk-header",children:[e.jsxs("span",{children:["Paste ",e.jsx("code",{children:"KEY=value"})," lines (one per line):"]}),e.jsx(b,{variant:"ghost",size:"sm",onClick:()=>$(!1),children:e.jsx(oe,{size:12})})]}),e.jsx("textarea",{className:"input mono env-var-bulk-textarea",placeholder:`BIZAR_API_KEY=your-key-here
3
- PROVIDER_OPENAI_KEY=sk-...
4
- MODEL_KEY=gem-...`,value:_,onChange:t=>W(t.target.value),rows:6}),e.jsxs("div",{className:"env-var-bulk-footer",children:[e.jsx(b,{variant:"primary",size:"sm",onClick:ge,disabled:K||!_.trim(),children:K?"Importing…":"Import"}),e.jsx("span",{className:"muted",style:{fontSize:11},children:"Existing keys are skipped unless re-created individually. Invalid lines are reported."})]})]}),l.length===0&&!L&&e.jsxs("div",{className:"env-var-empty",children:[e.jsx(xe,{size:20}),e.jsx("span",{children:"No BIZAR_* env vars yet."}),e.jsx("span",{style:{fontSize:11},children:"Create one to store API keys securely."})]}),L&&e.jsxs("div",{className:"env-var-add-form",children:[e.jsxs("div",{className:"env-var-add-row",children:[e.jsx("input",{className:"input mono",style:{width:240},placeholder:"BIZAR_NAME",value:h,onChange:t=>m(t.target.value.toUpperCase()),onKeyDown:t=>{t.key==="Enter"&&D(),t.key==="Escape"&&C(!1)},autoFocus:!0}),e.jsx("input",{className:"input mono",type:"password",placeholder:"value (API key, token, etc.)",value:f,onChange:t=>w(t.target.value),onKeyDown:t=>{t.key==="Enter"&&D(),t.key==="Escape"&&C(!1)}}),e.jsxs(b,{variant:"primary",size:"sm",onClick:D,disabled:x,children:[e.jsx(V,{size:12})," Create"]}),e.jsx(b,{variant:"ghost",size:"sm",onClick:()=>{C(!1),m(""),w("")},children:e.jsx(oe,{size:12})})]}),e.jsxs("p",{style:{fontSize:11,color:"var(--text-dim)",margin:0},children:["Names must match ",e.jsxs("code",{children:["BIZAR_",e.jsx("var",{children:"NAME"})]})," (uppercase letters, digits, underscores)."]})]}),F.length>0&&(ve?e.jsx("div",{className:"env-var-groups",children:le.map(t=>{const N=Q[t],v=M.has(t);return e.jsxs("div",{className:"env-var-group",children:[e.jsxs("button",{type:"button",className:"env-var-group-header",onClick:()=>fe(t),children:[v?e.jsx(Te,{size:12}):e.jsx($e,{size:12}),e.jsx("span",{className:"env-var-group-prefix mono",children:t}),e.jsxs("span",{className:"env-var-group-count muted",children:["(",N.length,")"]})]}),!v&&e.jsx("div",{className:"env-var-list",children:N.map(I=>e.jsx(he,{v:I,editing:g,editValue:y,isRevealed:n.has(I.name),onToggleReveal:H,onStartEdit:d,onEditValueChange:S,onHandleUpdate:U,onCancelEdit:()=>j(null),onDelete:re,saving:x},I.name))})]},t)})}):e.jsx("div",{className:"env-var-list",children:F.map(t=>e.jsx(he,{v:t,editing:g,editValue:y,isRevealed:n.has(t.name),onToggleReveal:H,onStartEdit:d,onEditValueChange:S,onHandleUpdate:U,onCancelEdit:()=>j(null),onDelete:re,saving:x},t.name))})),F.length===0&&l.length>0&&e.jsxs("div",{className:"env-var-empty",children:[e.jsx(te,{size:16}),e.jsxs("span",{children:['No env vars match "',R,'"']})]})]})}function he({v:a,editing:s,editValue:l,isRevealed:c,onToggleReveal:r,onStartEdit:p,onEditValueChange:x,onHandleUpdate:i,onCancelEdit:n,onDelete:u,saving:g}){const j=s===a.name;return e.jsxs("div",{className:T("env-var-row",j&&"env-var-row-editing"),children:[e.jsx("span",{className:"env-var-name mono",children:a.name}),j?e.jsx("input",{className:"input mono",type:"password",style:{flex:1,minWidth:200},value:l,onChange:y=>x(y.target.value),onKeyDown:y=>{y.key==="Enter"&&i(a.name),y.key==="Escape"&&n()},autoFocus:!0}):e.jsx("span",{className:T("env-var-value mono",!c&&"env-var-value-masked"),children:a.value}),e.jsx("span",{className:"env-var-source muted",children:a.source}),e.jsxs("div",{className:"env-var-actions",children:[!j&&e.jsx("button",{type:"button",className:"icon-btn",title:c?"Hide":"Reveal",onClick:()=>r(a.name),children:c?e.jsx(Me,{size:12}):e.jsx(ne,{size:12})}),j?e.jsxs(e.Fragment,{children:[e.jsx(b,{variant:"primary",size:"sm",onClick:()=>i(a.name),disabled:g,children:"Save"}),e.jsx(b,{variant:"ghost",size:"sm",onClick:n,children:"Cancel"})]}):e.jsxs(e.Fragment,{children:[e.jsx("button",{type:"button",className:"icon-btn",title:"Edit",onClick:()=>p(a),children:e.jsx(De,{size:12})}),e.jsx("button",{type:"button",className:"icon-btn icon-btn-danger",title:"Delete",onClick:()=>u(a.name),children:e.jsx(Ue,{size:12})})]})]})]})}function rs(){return e.jsxs(E,{id:"settings-env-vars","data-section":"env-vars",children:[e.jsxs(B,{children:[e.jsx(xe,{size:14})," Environment Variables"]}),e.jsxs(P,{children:["Manage BIZAR_* env vars (API keys, secrets). Values are stored in ",e.jsx("code",{children:"~/.config/bizar/env.json"})," (mode 0600)."]}),e.jsx(Ke,{})]})}export{de as A,b as B,E as C,rs as E,es as G,ts as H,as as S,ss as T,Je as U,B as a,P as b,Qe as c,ns as d};