@polderlabs/bizar 7.0.0 → 7.0.2

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 (75) hide show
  1. package/README.md +125 -47
  2. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js +114 -0
  3. package/bizar-dash/dist/assets/EnvVarsSection-BfhAz3ax.js.map +1 -0
  4. package/bizar-dash/dist/assets/MobileChat-DLWdv6tQ.js +1 -0
  5. package/bizar-dash/dist/assets/{MobileChat-D7zRhU9K.js.map → MobileChat-DLWdv6tQ.js.map} +1 -1
  6. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js +1 -0
  7. package/bizar-dash/dist/assets/MobileSettings-Bxv62uPY.js.map +1 -0
  8. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js +26 -0
  9. package/bizar-dash/dist/assets/Toast-ATSAmbdi.js.map +1 -0
  10. package/bizar-dash/dist/assets/main-306CZfn6.js +430 -0
  11. package/bizar-dash/dist/assets/main-306CZfn6.js.map +1 -0
  12. package/bizar-dash/dist/assets/main-G3bNDpY3.css +1 -0
  13. package/bizar-dash/dist/assets/mobile-C_naUN91.js +1 -0
  14. package/bizar-dash/dist/assets/{mobile-DXYCA0hC.js.map → mobile-C_naUN91.js.map} +1 -1
  15. package/bizar-dash/dist/assets/mobile-layout-BWLtwtfQ.css +1 -0
  16. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js +211 -0
  17. package/bizar-dash/dist/assets/mobile-layout-GZ3bE8L6.js.map +1 -0
  18. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js +65 -0
  19. package/bizar-dash/dist/assets/useSlashCommands-BOwD7lNb.js.map +1 -0
  20. package/bizar-dash/dist/index.html +7 -13
  21. package/bizar-dash/dist/mobile.html +3 -8
  22. package/bizar-dash/src/server/memory-consolidator.mjs +8 -1
  23. package/bizar-dash/src/web/mobile/MobileApp.tsx +33 -2
  24. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +2 -2
  25. package/bizar-dash/src/web/mobile/MobileEval.tsx +4 -4
  26. package/bizar-dash/src/web/mobile/MobileMemory.tsx +2 -2
  27. package/bizar-dash/src/web/mobile/MobileSettings.tsx +3 -3
  28. package/bizar-dash/src/web/mobile/MobileTasks.tsx +2 -2
  29. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +3 -3
  30. package/bizar-dash/src/web/styles/mobile.css +41 -0
  31. package/bizar-dash/src/web/styles/tasks-redesign.css +34 -12
  32. package/bizar-dash/src/web/ui/controls/Textarea.tsx +56 -0
  33. package/bizar-dash/src/web/ui/controls/controls.css +22 -0
  34. package/bizar-dash/src/web/ui/controls/index.ts +2 -0
  35. package/bizar-dash/src/web/ui/layout/ViewHeader.tsx +48 -0
  36. package/bizar-dash/src/web/ui/layout/index.ts +1 -0
  37. package/bizar-dash/src/web/ui/layout/layout.css +51 -0
  38. package/bizar-dash/src/web/ui/styles/tokens.css +13 -0
  39. package/bizar-dash/src/web/views/Agents.tsx +10 -2
  40. package/bizar-dash/src/web/views/Overview.tsx +2 -13
  41. package/bizar-dash/src/web/views/Tasks.tsx +1 -7
  42. package/bizar-dash/tests/ui/controls/Textarea.test.tsx +56 -0
  43. package/bizar-dash/tests/ui/layout/ViewHeader.test.tsx +47 -0
  44. package/cli/provision-claude.mjs +94 -0
  45. package/package.json +1 -1
  46. package/bizar-dash/dist/assets/EnvVarsSection-CW6fWKds.js +0 -4
  47. package/bizar-dash/dist/assets/EnvVarsSection-CW6fWKds.js.map +0 -1
  48. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +0 -1
  49. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +0 -1
  50. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js.map +0 -1
  51. package/bizar-dash/dist/assets/Toast-WTxc3m2g.js +0 -1
  52. package/bizar-dash/dist/assets/Toast-WTxc3m2g.js.map +0 -1
  53. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +0 -1
  54. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +0 -1
  55. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +0 -1
  56. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +0 -1
  57. package/bizar-dash/dist/assets/icons-B0AKnYI3.js +0 -731
  58. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +0 -1
  59. package/bizar-dash/dist/assets/main-C-Md3DaY.css +0 -1
  60. package/bizar-dash/dist/assets/main-CO8fIxQK.js +0 -18
  61. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +0 -1
  62. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +0 -1
  63. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js.map +0 -1
  64. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +0 -1
  65. package/bizar-dash/dist/assets/mobile-layout-CJnZNLy3.css +0 -1
  66. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +0 -2
  67. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +0 -1
  68. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +0 -1
  69. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +0 -46
  70. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +0 -1
  71. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +0 -2
  72. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +0 -1
  73. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +0 -45
  74. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +0 -1
  75. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -66,6 +66,7 @@ import {
66
66
  Sparkline,
67
67
  BarChart,
68
68
  KeyValueList,
69
+ Textarea,
69
70
  IconButton,
70
71
  Button as UiButton,
71
72
  Badge,
@@ -968,23 +969,11 @@ function OverviewInner({
968
969
  }}
969
970
  >
970
971
  <Stack gap={3}>
971
- <textarea
972
+ <Textarea
972
973
  ref={inputRef}
973
974
  disabled={submitting}
974
975
  aria-label="Describe what you want Odin to do"
975
976
  rows={4}
976
- style={{
977
- width: '100%',
978
- resize: 'vertical',
979
- fontFamily: 'var(--font-sans)',
980
- fontSize: 'var(--text-base)',
981
- lineHeight: 'var(--leading-normal)',
982
- padding: 'var(--space-3) var(--space-4)',
983
- border: '1px solid var(--border-default)',
984
- borderRadius: 'var(--radius-md)',
985
- background: 'var(--surface-1)',
986
- color: 'var(--text-primary)',
987
- }}
988
977
  placeholder="e.g. Implement user authentication with email + password, including registration, login, password reset, and integration tests. Use Bcrypt, JWT tokens, and the existing API style."
989
978
  data-testid="overview-prompt-input"
990
979
  />
@@ -119,12 +119,6 @@ const PRIORITY_BADGE: Record<PriorityKind, BadgeVariant> = {
119
119
  low: 'neutral',
120
120
  };
121
121
 
122
- const PRIORITY_ROW_CLASS: Record<PriorityKind, string> = {
123
- high: 'tasks-wave3__row--priority-high',
124
- normal: 'tasks-wave3__row--priority-normal',
125
- low: 'tasks-wave3__row--priority-low',
126
- };
127
-
128
122
  function TasksInner({
129
123
  snapshot,
130
124
  refreshSnapshot,
@@ -442,7 +436,7 @@ function TaskRow({
442
436
  : 'normal';
443
437
  return (
444
438
  <div
445
- className={cx('tasks-wave3__row', PRIORITY_ROW_CLASS[priority])}
439
+ className={cx('tasks-wave3__row')}
446
440
  data-task-id={task.id}
447
441
  data-tick={tick}
448
442
  role="button"
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { Textarea } from '../../../src/web/ui/controls/Textarea';
5
+
6
+ describe('Textarea', () => {
7
+ it('renders and accepts typed input', async () => {
8
+ const onChange = vi.fn();
9
+ const user = userEvent.setup();
10
+ render(<Textarea value="" onChange={onChange} aria-label="Description" />);
11
+ const input = screen.getByRole('textbox');
12
+ await user.type(input, 'hello');
13
+ expect(onChange).toHaveBeenCalled();
14
+ expect(input).toBeInTheDocument();
15
+ });
16
+
17
+ it('applies the size modifier class on the wrapper', () => {
18
+ render(<Textarea value="" onChange={() => {}} inputSize="lg" aria-label="Lg" />);
19
+ // .field-size-lg sits on the .field wrapper, not the inner textarea.
20
+ expect(screen.getByLabelText('Lg').parentElement).toHaveClass('field-size-lg');
21
+ });
22
+
23
+ it('marks itself aria-invalid when error is set', () => {
24
+ render(<Textarea value="" onChange={() => {}} error="Required" aria-label="Err" />);
25
+ const input = screen.getByLabelText('Err');
26
+ expect(input).toHaveAttribute('aria-invalid', 'true');
27
+ expect(input).toHaveClass('field-error');
28
+ });
29
+
30
+ it('is disabled when disabled is set', () => {
31
+ render(<Textarea value="" onChange={() => {}} disabled aria-label="Dis" />);
32
+ expect(screen.getByLabelText('Dis')).toBeDisabled();
33
+ });
34
+
35
+ it('renders the hint slot when provided', () => {
36
+ render(
37
+ <Textarea
38
+ value=""
39
+ onChange={() => {}}
40
+ hint={<span data-testid="hint">Markdown ok</span>}
41
+ aria-label="With hint"
42
+ />,
43
+ );
44
+ expect(screen.getByTestId('hint')).toBeInTheDocument();
45
+ });
46
+
47
+ it('defaults rows to 4 when not provided', () => {
48
+ render(<Textarea value="" onChange={() => {}} aria-label="R" />);
49
+ expect(screen.getByLabelText('R')).toHaveAttribute('rows', '4');
50
+ });
51
+
52
+ it('honours an explicit rows override', () => {
53
+ render(<Textarea value="" onChange={() => {}} rows={8} aria-label="R8" />);
54
+ expect(screen.getByLabelText('R8')).toHaveAttribute('rows', '8');
55
+ });
56
+ });
@@ -0,0 +1,47 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { ViewHeader } from '../../../src/web/ui/layout/ViewHeader';
4
+
5
+ describe('ViewHeader', () => {
6
+ it('renders title and subtitle', () => {
7
+ render(<ViewHeader title="Tasks" subtitle="Live kanban board" />);
8
+ expect(screen.getByText('Tasks')).toBeInTheDocument();
9
+ expect(screen.getByText('Live kanban board')).toBeInTheDocument();
10
+ });
11
+
12
+ it('renders the actions slot', () => {
13
+ render(
14
+ <ViewHeader
15
+ title="Tasks"
16
+ actions={<button>Refresh</button>}
17
+ />,
18
+ );
19
+ expect(screen.getByText('Refresh')).toBeInTheDocument();
20
+ });
21
+
22
+ it('renders an eyebrow row above the title when provided', () => {
23
+ const { container } = render(
24
+ <ViewHeader title="Overview" eyebrow={<span>Dashboard</span>} />,
25
+ );
26
+ const eyebrow = container.querySelector('.bizar-view-header__eyebrow');
27
+ expect(eyebrow).toBeInTheDocument();
28
+ expect(eyebrow?.textContent).toBe('Dashboard');
29
+ });
30
+
31
+ it('uses <h1> for the page title (not a panel-level h3)', () => {
32
+ const { container } = render(<ViewHeader title="Overview" />);
33
+ const h1 = container.querySelector('h1');
34
+ expect(h1).toBeInTheDocument();
35
+ expect(h1?.textContent).toBe('Overview');
36
+ });
37
+
38
+ it('accepts an extra className for view-specific tweaks', () => {
39
+ const { container } = render(<ViewHeader title="X" className="agents-header" />);
40
+ expect(container.querySelector('.bizar-view-header')).toHaveClass('agents-header');
41
+ });
42
+
43
+ it('renders nothing in the actions slot when not provided', () => {
44
+ const { container } = render(<ViewHeader title="No actions" />);
45
+ expect(container.querySelector('.bizar-view-header__actions')).not.toBeInTheDocument();
46
+ });
47
+ });
@@ -116,6 +116,93 @@ function haveCmd(cmd) {
116
116
  }
117
117
  }
118
118
 
119
+ /**
120
+ * Build the SDK workspace so its compiled JS output lives at
121
+ * `<pkg>/packages/sdk/dist/`. The dashboard imports the SDK memory
122
+ * module from `dist/memory/index.js`, not from `src/memory/index.js`,
123
+ * so without this build step the npm-installed package ships a TS
124
+ * source tree that the dashboard cannot run.
125
+ *
126
+ * v7.0.1 — `install.sh` + `bizar update` previously skipped this step
127
+ * entirely. Symptom: `bizar dash start` crashes with
128
+ * `Cannot find module .../packages/sdk/dist/memory/index.js`
129
+ * (or .../src/memory/index.js for older import paths). Root cause: the
130
+ * SDK's `package.json` advertises `./dist/*` exports, but the SDK's
131
+ * `tsconfig.json` is never compiled by the installer.
132
+ *
133
+ * Bun resolution: when bun is invoked from a globally npm-installed
134
+ * package, its lifecycle script runs with a minimal PATH that does
135
+ * NOT include `~/.bun/bin`. So `bun run build:sdk` (which shells out to
136
+ * `tsc` via bun's bin shim) fails with `tsc: command not found`. To
137
+ * dodge that, we resolve bun to its absolute path
138
+ * (`$HOME/.bun/bin/bun`) and prepend that dir to the child process
139
+ * PATH so bun's tsc shim is visible.
140
+ *
141
+ * Idempotent: skips when `dist/memory/index.js` already exists.
142
+ * Non-fatal: returns `{ ok: false, message }` rather than throwing —
143
+ * the rest of the provision flow should still complete (some operators
144
+ * run a separate build pipeline, e.g. a CI step).
145
+ */
146
+ export async function buildSdk({ dryRun = false } = {}) {
147
+ const sdkRoot = join(REPO_ROOT, 'packages', 'sdk');
148
+ const srcEntry = join(sdkRoot, 'src', 'memory', 'index.ts');
149
+ const distEntry = join(sdkRoot, 'dist', 'memory', 'index.js');
150
+
151
+ if (!existsSync(srcEntry)) {
152
+ // No SDK TS source — this is a stripped install or an unusual layout.
153
+ // Don't fail the whole provision over it; just report.
154
+ return { ok: true, skipped: true, message: `SDK src not found at ${srcEntry} — skipping build (likely a non-bundled install)` };
155
+ }
156
+
157
+ if (existsSync(distEntry)) {
158
+ return { ok: true, skipped: true, message: `SDK dist already present — skipping build (delete ${join(sdkRoot, 'dist')} to force)` };
159
+ }
160
+
161
+ if (dryRun) {
162
+ return { ok: true, skipped: false, message: `would build SDK at ${sdkRoot}` };
163
+ }
164
+
165
+ // Resolve bun to an absolute path. `command -v bun` finds whatever is
166
+ // on PATH, but bun's bin dir (typically `~/.bun/bin`) is NOT always on
167
+ // PATH — especially when invoked from a globally npm-installed package
168
+ // whose lifecycle scripts run with a minimal env. Without the absolute
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.
172
+ const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
173
+ let cmd, args, childEnv;
174
+ if (existsSync(bunFromHome)) {
175
+ cmd = bunFromHome;
176
+ 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
+ const pathDelim = process.platform === 'win32' ? ';' : ':';
180
+ childEnv = { ...process.env, PATH: `${bunBin}${pathDelim}${process.env.PATH || ''}` };
181
+ } else if (haveCmd('bun')) {
182
+ cmd = 'bun';
183
+ args = ['run', 'build:sdk'];
184
+ childEnv = process.env;
185
+ } else {
186
+ // Fallback: npx tsc. Requires a network round-trip on first use but
187
+ // works on systems without bun installed (CI, minimal containers).
188
+ cmd = 'npx';
189
+ args = ['--yes', 'tsc', '-p', 'packages/sdk/tsconfig.json'];
190
+ childEnv = process.env;
191
+ }
192
+
193
+ try {
194
+ logInfo(`building SDK with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
195
+ execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 180_000, env: childEnv });
196
+ if (existsSync(distEntry)) {
197
+ return { ok: true, skipped: false, message: 'SDK built (dist/memory/index.js present)' };
198
+ }
199
+ return { ok: false, message: `build completed but ${distEntry} still missing — check tsconfig outDir` };
200
+ } catch (err) {
201
+ const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
202
+ return { ok: false, message: `SDK build failed: ${stderr}` };
203
+ }
204
+ }
205
+
119
206
  function readTextSafe(file, fallback = '') {
120
207
  try {
121
208
  if (!existsSync(file)) return fallback;
@@ -798,6 +885,13 @@ export async function runProvision(opts = {}) {
798
885
  await runStep('Syncing rules', () => syncRulesFiles({ dryRun, force }));
799
886
  await runStep('Syncing hooks', () => syncHookFiles({ dryRun, force }));
800
887
 
888
+ // ── 4b. Build the SDK workspace so dist/ exists. ────────────────────
889
+ // The dashboard imports from packages/sdk/dist/* at runtime; without
890
+ // this step v7.0.0+ npm installs crash on dashboard startup because
891
+ // the SDK ships as TS source only. See `buildSdk()` JSDoc for the
892
+ // full story.
893
+ await runStep('Building SDK', () => buildSdk({ dryRun }));
894
+
801
895
  // ── 9. settings.json (MCP + hooks + permissions + env) ──────────────
802
896
  section('Writing settings.json');
803
897
  const settingsStep = writeClaudeSettings({ dryRun, force });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "7.0.0",
3
+ "version": "7.0.2",
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": {
@@ -1,4 +0,0 @@
1
- import{r as c,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-DSmtNPja.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=c.forwardRef(function({variant:s="secondary",size:l="md",iconOnly:d=!1,loading:i=!1,disabled:p,className:x,children:r,...n},u){return e.jsxs("button",{ref:u,type:n.type??"button",disabled:p||i,className:T("btn",`btn-${s}`,`btn-size-${l}`,d&&"btn-icon",i&&"btn-loading",x),...n,children:[i?e.jsx("span",{className:"btn-spinner","aria-hidden":!0}):null,r]})});function E({variant:a="elevated",interactive:s=!1,className:l,children:d,...i}){return e.jsx("div",{className:T("card",`card-${a}`,s&&"card-interactive",l),...i,children:d})}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 Qe(){const a=Y(),[s,l]=c.useState({current:{},latest:null,checking:!1,updating:!1,hasUpdates:!1,requiresRestart:!1,perPackage:{}});c.useEffect(()=>{const r=new be;return r.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}}}})}),()=>r.close()},[]),c.useEffect(()=>{k.get("/updates/status").then(r=>l(n=>({...n,current:r.current}))).catch(r=>l(n=>({...n,error:r.message})))},[]);const d=async()=>{l(r=>({...r,checking:!0,error:void 0}));try{const r=await k.get("/updates/check");l(n=>({...n,checking:!1,current:r.current,latest:r.latest,hasUpdates:r.hasUpdates}))}catch(r){l(n=>({...n,checking:!1,error:r.message}))}},i=async()=>{if(confirm("Update Bizar packages? The dashboard will restart automatically.")){l(r=>({...r,updating:!0,requiresRestart:!1,perPackage:{},error:void 0}));try{await k.post("/updates/apply",{packages:["bizar","bizar-dash","bizar-plugin"]})}catch(r){l(n=>({...n,updating:!1,error:r.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(r=>e.jsxs("li",{children:[e.jsx("span",{children:r.name}),e.jsx("code",{className:"mono",children:s.current[r.id]||"—"})]},r.id))})]}),s.latest&&e.jsxs("div",{className:"updates-latest",children:[e.jsx("h4",{children:"Latest available"}),e.jsx("ul",{children:ae.map(r=>{var j;const n=s.current[r.id],u=(j=s.latest)==null?void 0:j[r.id],g=n&&u&&n!==u;return e.jsxs("li",{className:g?"updates-outdated":"updates-current-version",children:[e.jsx("span",{children:r.name}),e.jsxs("code",{className:"mono",children:[u||"—",g&&e.jsx("span",{className:"updates-badge",children:"update available"})]})]},r.id)})})]}),s.updating&&e.jsx("div",{className:"updates-progress-rows",children:ae.map(r=>{const n=s.perPackage[r.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:r.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]})]},r.id)})}),e.jsxs("div",{className:"updates-actions",children:[e.jsxs(b,{onClick:d,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:i,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:d,onError:i}={}){const[p,x]=c.useState(a),[r,n]=c.useState("idle"),u=c.useRef(null),g=c.useRef(null),j=c.useRef(a),y=c.useRef(!1),S=c.useCallback(async m=>{n("saving");try{await s(m),j.current=m,g.current=null,n("saved"),d==null||d(),setTimeout(()=>n("idle"),2e3)}catch(v){n("error"),i==null||i(v)}},[s,d,i]),L=c.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 v=g.current;g.current=null,v!==null&&S(v)},l)},[l,S]),C=c.useCallback(()=>{u.current&&(clearTimeout(u.current),u.current=null,y.current=!1)},[]),h=c.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 c.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"),d==null||d()}).catch(v=>{n("error"),i==null||i(v)}).finally(()=>{setTimeout(()=>n("idle"),2e3)}))}},[s,d,i]),{value:p,setValue:L,status:r,save:S,flush:h,clearDebounce:C,hasPendingDebounceRef:y,pendingValueRef:g}}function de({initialValue:a,saveFn:s,delay:l=800,render:d,className:i}){const{value:p,setValue:x,status:r,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 ${i||""} ${r}`,children:[d({value:p,onChange:x,onBlur:y}),e.jsxs("span",{className:"autosave-status",role:"status","aria-live":"polite",children:[r==="saving"&&e.jsx(we,{className:"spinning",size:12}),r==="saved"&&e.jsx(Ne,{size:12}),r==="error"&&e.jsx(me,{size:12})]})]})}const He=[{id:"topnav",label:"Top nav"},{id:"sidebar",label:"Sidebar"},{id:"both",label:"Both"}];function Je({settings:a,patchUi:s,patchTop:l,autoSave:d}){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(i=>e.jsx("button",{type:"button",className:T("layout-card",a.ui.layout===i.id&&"layout-card-active"),onClick:()=>s({layout:i.id}),children:e.jsx("span",{className:"layout-card-label",children:i.label})},i.id))}),e.jsxs("label",{className:"checkbox-row","data-setting-id":"ui.showHeader",children:[e.jsx("input",{type:"checkbox",checked:a.ui.showHeader,onChange:i=>s({showHeader:i.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:i=>s({showStatusBar:i.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:i=>s({defaultTab:i.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 i=>{l("defaultAgent",i),d==null||d("defaultAgent",i)},render:({value:i,onChange:p,onBlur:x})=>e.jsx("input",{id:"set-default-agent",className:"input",type:"text",placeholder:"e.g. odin",value:i,onChange:r=>p(r.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 i=>{l("defaultModel",i),d==null||d("defaultModel",i)},render:({value:i,onChange:p,onBlur:x})=>e.jsx("input",{id:"set-default-model",className:"input",type:"text",placeholder:"(leave empty for provider default)",value:i,onChange:r=>p(r.target.value),onBlur:x})})]})]})]})}function es({initialStatus:a}){var m;const[s,l]=c.useState(a??null),[d,i]=c.useState(""),[p,x]=c.useState(!1),[r,n]=c.useState(null);c.useEffect(()=>{s===null&&k.get("/tailscale/status").then(v=>l(v)).catch(()=>l(null))},[]);const u=async()=>{try{const v=await k.get("/tailscale/status");l(v)}catch{n("Failed to load Tailscale status")}},g=async()=>{if(d.trim()){x(!0),n(null);try{await k.post("/tailscale/setup",{authKey:d.trim()}),await u(),i("")}catch(v){n(v.message||"Authentication failed")}finally{x(!1)}}},j=async()=>{var v,w,z;if(s){x(!0),n(null);try{await k.post("/tailscale/setup",{port:((v=s.settings)==null?void 0:v.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(v){n(v.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."}),r&&e.jsx("p",{style:{color:"var(--color-error, #f85149)",fontSize:"0.85rem"},children:r}),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:d,onChange:v=>i(v.target.value),style:{flex:1,minWidth:"200px"},disabled:p}),e.jsx(b,{variant:"primary",size:"sm",onClick:g,disabled:p||!d.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 ss({settings:a,patchTop:s}){var l,d,i;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:((d=a.systemLlm)==null?void 0:d.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:((i=a.systemLlm)==null?void 0:i.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(),[d,i]=c.useState(null),[p,x]=c.useState(null),[r,n]=c.useState(!1),[u,g]=c.useState(!1),[j,y]=c.useState(!1),[S,L]=c.useState(!1),[C,h]=c.useState(!1),[m,v]=c.useState(!1),w=c.useCallback(async()=>{n(!0);try{const[o,U]=await Promise.all([k.get("/headroom/status"),k.get("/headroom/stats?hours=24").catch(()=>({error:"no stats"}))]);i(o),U.error||x(U)}catch(o){l.error(`Failed to load Headroom status: ${o.message}`)}finally{n(!1)}},[l]);c.useEffect(()=>{w()},[w]);const z=async()=>{g(!0);try{const o=await k.post("/headroom/install",{force:!0});o.installed?(l.success(`Headroom installed via ${o.method}.`),await w()):l.error('Headroom install failed. Try: pip install "headroom-ai[all]"')}catch(o){l.error(`Install failed: ${o.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(o){l.error(`Wrap failed: ${o.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(o){l.error(`Unwrap failed: ${o.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(o){l.error(`Proxy start failed: ${o.message}`)}finally{h(!1)}},K=async()=>{v(!0);try{(await k.post("/headroom/proxy/stop")).ok?(l.success("Headroom proxy stopped."),await w()):l.error("Proxy stop failed.")}catch(o){l.error(`Proxy stop failed: ${o.message}`)}finally{v(!1)}},Z=async()=>{try{const{spawn:o}=await ke(async()=>{const{spawn:U}=await import("./__vite-browser-external-BIHI7g3E.js");return{spawn:U}},[]);o("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}=d||{},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:r,children:e.jsx(O,{size:13,className:r?"spin":""})})]}),e.jsxs("div",{style:{marginTop:16},children:[e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.enabled,onChange:o=>s({enabled:o.target.checked})}),e.jsx("span",{children:"Enable Headroom"})]}),e.jsxs("label",{className:"checkbox-row",children:[e.jsx("input",{type:"checkbox",checked:a.autoInstall,onChange:o=>s({autoInstall:o.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:o=>s({autoStart:o.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:o=>s({autoWrap:o.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:o=>s({routeAllProviders:o.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:o=>s({port:Math.max(1,Math.min(65535,parseInt(o.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:o=>s({host:o.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:o=>s({backend:o.target.value}),children:Ve.map(o=>e.jsx("option",{value:o.value,children:o.label},o.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:o=>s({budget:Math.max(0,parseFloat(o.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:o=>s({outputShaper:o.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:o=>s({telemetry:o.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 as({settings:a,setSettings:s,setDirty:l}){return e.jsx(Oe,{settings:a.headroom||ue,onPatch:d=>{s(i=>({...i,headroom:{...i.headroom||ue,...d}})),l(!0)}})}function Fe(){const a=Y(),[s,l]=c.useState([]),[d,i]=c.useState(new Set),[p,x]=c.useState(!0),[r,n]=c.useState(""),[u,g]=c.useState(!0),j=c.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:[]),i(new Set(m.hidden||[]))}catch(h){a.error(`Load failed: ${h.message}`)}finally{x(!1)}},[a]);c.useEffect(()=>{j()},[j]);const y=(h,m)=>{const v=`${h.kind||""}|${h.ts||""}|${h.slug||""}|${m}`;let w=0;for(let z=0;z<v.length;z++)w=(w<<5)-w+v.charCodeAt(z)|0;return Math.abs(w).toString(16).padStart(8,"0").slice(0,16)},S=async()=>{try{await k.del("/activity/hide"),i(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(d);m.delete(h),i(m)}catch(m){a.error(`Restore failed: ${m.message}`)}},C=s.filter((h,m)=>{const v=y(h,m);if(!u&&d.has(v))return!1;if(!r)return!0;const w=r.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 · ",d.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:r,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:d.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 v=y(h,m),w=d.has(v);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(v),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 ts({about:a}){return e.jsxs(e.Fragment,{children:[e.jsx(Fe,{}),e.jsx(We,{about:a})]})}function Ke({onError:a}){const s=Y(),[l,d]=c.useState([]),[i,p]=c.useState(!1),[x,r]=c.useState(!1),[n,u]=c.useState(new Set),[g,j]=c.useState(null),[y,S]=c.useState(""),[L,C]=c.useState(!1),[h,m]=c.useState(""),[v,w]=c.useState(""),[z,$]=c.useState(!1),[_,W]=c.useState(""),[K,Z]=c.useState(!1),[R,q]=c.useState(""),[M,X]=c.useState(new Set),A=c.useCallback(async()=>{p(!0);try{const t=await k.get("/env-vars");d(t)}catch(t){s.error(`Load failed: ${t.message}`),a==null||a(t.message)}finally{p(!1)}},[s,a]);c.useEffect(()=>{A()},[A]);const H=t=>{u(N=>{const f=new Set(N);return f.has(t)?f.delete(t):f.add(t),f})},D=async()=>{const t=h.trim().toUpperCase(),N=v.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(f=>f.name===t)){s.warning(`${t} already exists.`);return}r(!0);try{await k.post("/env-vars",{name:t,value:N}),s.success(`${t} created.`),C(!1),m(""),w(""),await A()}catch(f){s.error(`Create failed: ${f.message}`)}finally{r(!1)}},o=t=>{j(t.name),S(t.value===t.value.replace(/\*/g,"")?t.value:""),u(N=>{const f=new Set(N);return f.add(t.name),f})},U=async t=>{if(!y.trim()){s.warning("Value cannot be empty.");return}r(!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{r(!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"}),f=URL.createObjectURL(N),I=document.createElement("a");I.href=f,I.download=".env",I.click(),URL.revokeObjectURL(f),s.success("Exported .env file.")}catch(t){s.error(`Export failed: ${t.message}`)}},F=c.useMemo(()=>{if(!R.trim())return l;const t=R.toLowerCase();return l.filter(N=>N.name.toLowerCase().includes(t))},[l,R]),Q=c.useMemo(()=>{const t={};for(const N of F){const f=N.name.split("_")[0]+"_";(t[f]??(t[f]=[])).push(N)}return t},[F]),ve=t=>{X(N=>{const f=new Set(N);return f.has(t)?f.delete(t):f.add(t),f})},le=c.useMemo(()=>{const t=["BIZAR_","PROVIDER_","MODEL_","API_"];return Object.keys(Q).sort((f,I)=>{const J=t.indexOf(f),ee=t.indexOf(I);return J===-1&&ee===-1?f.localeCompare(I):J===-1?1:ee===-1?-1:J-ee})},[Q]);if(i)return e.jsx("div",{className:"muted",style:{padding:"16px 0",fontSize:13},children:"Loading env vars…"});const fe=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:v,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&&(fe?e.jsx("div",{className:"env-var-groups",children:le.map(t=>{const N=Q[t],f=M.has(t);return e.jsxs("div",{className:"env-var-group",children:[e.jsxs("button",{type:"button",className:"env-var-group-header",onClick:()=>ve(t),children:[f?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,")"]})]}),!f&&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:o,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:o,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:d,onToggleReveal:i,onStartEdit:p,onEditValueChange:x,onHandleUpdate:r,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"&&r(a.name),y.key==="Escape"&&n()},autoFocus:!0}):e.jsx("span",{className:T("env-var-value mono",!d&&"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:d?"Hide":"Reveal",onClick:()=>i(a.name),children:d?e.jsx(Me,{size:12}):e.jsx(ne,{size:12})}),j?e.jsxs(e.Fragment,{children:[e.jsx(b,{variant:"primary",size:"sm",onClick:()=>r(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 ns(){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,ns as E,Je as G,as as H,ss as S,es as T,Qe as U,B as a,P as b,ts as c};