@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.
- package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js +114 -0
- package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js.map +1 -0
- package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BRFjer8p.js.map → MobileChat-DLWdv6tQ.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-Dw5jyJWi.js.map → MobileSettings-Bxv62uPY.js.map} +1 -1
- package/bizar-dash/dist/assets/Toast-ATSAmbdi.js +26 -0
- package/bizar-dash/dist/assets/Toast-ATSAmbdi.js.map +1 -0
- package/bizar-dash/dist/assets/main-CJAtrosO.css +1 -0
- package/bizar-dash/dist/assets/main-DKKj9dsq.js +430 -0
- package/bizar-dash/dist/assets/main-DKKj9dsq.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-C_naUN91.js +1 -0
- package/bizar-dash/dist/assets/{mobile-DRZsK3ic.js.map → mobile-C_naUN91.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js +211 -0
- package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js +65 -0
- package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js.map +1 -0
- package/bizar-dash/dist/index.html +6 -12
- package/bizar-dash/dist/mobile.html +2 -7
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/bizar-dash/src/web/components/SettingsNav.tsx +9 -4
- package/bizar-dash/src/web/components/Topbar.tsx +45 -0
- package/bizar-dash/src/web/styles/main.css +87 -89
- package/bizar-dash/src/web/styles/memory.css +30 -35
- package/bizar-dash/src/web/styles/settings.css +6 -26
- package/bizar-dash/src/web/ui/data/data.css +6 -1
- package/bizar-dash/src/web/ui/index.ts +1 -0
- package/bizar-dash/src/web/views/Memory.tsx +94 -62
- package/bizar-dash/src/web/views/Overview.tsx +2 -23
- package/bizar-dash/tests/settings-layout.test.tsx +2 -2
- package/bizar-dash/tests/settings-nav.test.tsx +6 -6
- package/cli/provision-claude.mjs +108 -5
- package/package.json +1 -1
- package/packages/sdk/.harness/agents.json +17 -0
- package/packages/sdk/.harness/topology.json +4 -0
- package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js +0 -4
- package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js.map +0 -1
- package/bizar-dash/dist/assets/MobileChat-BRFjer8p.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js +0 -1
- package/bizar-dash/dist/assets/Toast-WTxc3m2g.js +0 -1
- package/bizar-dash/dist/assets/Toast-WTxc3m2g.js.map +0 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +0 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +0 -1
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +0 -1
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +0 -1
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js +0 -731
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +0 -1
- package/bizar-dash/dist/assets/main-BE4Uh9im.css +0 -1
- package/bizar-dash/dist/assets/main-DqC58FEZ.js +0 -18
- package/bizar-dash/dist/assets/main-DqC58FEZ.js.map +0 -1
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +0 -1
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-DRZsK3ic.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js.map +0 -1
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +0 -1
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +0 -46
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +0 -45
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
// src/web/views/Memory.tsx —
|
|
1
|
+
// src/web/views/Memory.tsx — v7.0.4 dedicated Memory tab.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
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 {
|
|
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 =
|
|
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
|
|
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
|
|
119
|
+
return null;
|
|
103
120
|
}
|
|
104
121
|
};
|
|
105
122
|
|
|
106
123
|
return (
|
|
107
|
-
<div className="view view-memory
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
//
|
|
138
|
-
//
|
|
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.4 — theme 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: '.
|
|
63
|
-
expect(screen.getByText('Core', { selector: '.
|
|
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
|
|
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: '.
|
|
53
|
-
expect(screen.getByText('Core', { selector: '.
|
|
54
|
-
expect(screen.getByText('Agents', { selector: '.
|
|
55
|
-
expect(screen.getByText('Experience', { selector: '.
|
|
56
|
-
expect(screen.getByText('Data', { selector: '.
|
|
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', () => {
|
package/cli/provision-claude.mjs
CHANGED
|
@@ -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`.
|
|
171
|
-
//
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|
|
@@ -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};
|