@polderlabs/bizar 4.8.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bizar-dash/dist/assets/icons-CFqu2M-c.js +656 -0
- package/bizar-dash/dist/assets/icons-CFqu2M-c.js.map +1 -0
- package/bizar-dash/dist/assets/index-DmpSFPJY.js +9 -0
- package/bizar-dash/dist/assets/index-DmpSFPJY.js.map +1 -0
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js +16 -0
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js.map +1 -0
- package/bizar-dash/dist/assets/{main-DX_Jh8Wc.css → main-ZAfGKENE.css} +1 -1
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js +29 -0
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js +2 -0
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-DHXXbn1A.js +1 -0
- package/bizar-dash/dist/assets/{mobile-Chvf9u_B.js.map → mobile-DHXXbn1A.js.map} +1 -1
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js +40 -0
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js.map +1 -0
- package/bizar-dash/dist/index.html +6 -3
- package/bizar-dash/dist/mobile.html +5 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/skills/eval/SKILL.md +237 -0
- package/bizar-dash/src/server/api.mjs +28 -0
- package/bizar-dash/src/server/auth.mjs +155 -1
- package/bizar-dash/src/server/eval-store.mjs +226 -0
- package/bizar-dash/src/server/eval.mjs +347 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +109 -0
- package/bizar-dash/src/server/memory-store.mjs +121 -0
- package/bizar-dash/src/server/ocr.mjs +55 -0
- package/bizar-dash/src/server/otel.mjs +133 -0
- package/bizar-dash/src/server/plugins/registry.mjs +363 -0
- package/bizar-dash/src/server/plugins/sandbox.mjs +655 -0
- package/bizar-dash/src/server/plugins/store.mjs +659 -0
- package/bizar-dash/src/server/routes/chat.mjs +246 -170
- package/bizar-dash/src/server/routes/clipboard.mjs +173 -0
- package/bizar-dash/src/server/routes/eval.mjs +147 -0
- package/bizar-dash/src/server/routes/memory.mjs +46 -0
- package/bizar-dash/src/server/routes/ocr.mjs +182 -0
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +82 -48
- package/bizar-dash/src/server/routes/plugins.mjs +220 -0
- package/bizar-dash/src/server/routes/users.mjs +84 -0
- package/bizar-dash/src/server/routes/voice.mjs +131 -0
- package/bizar-dash/src/server/routes/workspaces.mjs +204 -0
- package/bizar-dash/src/server/server.mjs +40 -0
- package/bizar-dash/src/server/voice-store.mjs +202 -0
- package/bizar-dash/src/server/voice-transcribe.mjs +72 -0
- package/bizar-dash/src/server/workspaces.mjs +626 -0
- package/bizar-dash/src/web/components/InviteDialog.tsx +205 -0
- package/bizar-dash/src/web/components/ScreenshotCapture.tsx +138 -0
- package/bizar-dash/src/web/components/ScreenshotOCR.tsx +42 -0
- package/bizar-dash/src/web/components/SettingsSearch.tsx +204 -89
- package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +182 -0
- package/bizar-dash/src/web/components/VoiceRecorder.tsx +104 -0
- package/bizar-dash/src/web/components/WorkspaceSelector.tsx +158 -0
- package/bizar-dash/src/web/lib/search.ts +115 -0
- package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +10 -35
- package/bizar-dash/src/web/mobile/views/QrCodePanel.tsx +69 -0
- package/bizar-dash/src/web/styles/memory.css +166 -1
- package/bizar-dash/src/web/styles/settings.css +80 -0
- package/bizar-dash/src/web/views/Memory.tsx +22 -2
- package/bizar-dash/src/web/views/Settings.tsx +99 -0
- package/bizar-dash/src/web/views/Workspace.tsx +294 -0
- package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +23 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphLegend.tsx +29 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphPanel.tsx +192 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphView.tsx +336 -0
- package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +101 -0
- package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +189 -0
- package/bizar-dash/tests/bundle-analysis.test.mjs +73 -0
- package/bizar-dash/tests/clipboard.test.mjs +147 -0
- package/bizar-dash/tests/components/screenshot-ocr.test.tsx +75 -0
- package/bizar-dash/tests/components/settings-search.test.tsx +180 -0
- package/bizar-dash/tests/components/workspace-selector.test.tsx +73 -0
- package/bizar-dash/tests/deploy-templates.test.mjs +100 -0
- package/bizar-dash/tests/docker-build.test.mjs +96 -0
- package/bizar-dash/tests/eval/fixtures.test.mjs +141 -0
- package/bizar-dash/tests/eval/report.test.mjs +284 -0
- package/bizar-dash/tests/eval/runner.test.mjs +471 -0
- package/bizar-dash/tests/lib/search-fuzzy.test.ts +149 -0
- package/bizar-dash/tests/memory-graph-view.test.tsx +69 -0
- package/bizar-dash/tests/memory-graph.test.mjs +95 -0
- package/bizar-dash/tests/ocr.test.mjs +87 -0
- package/bizar-dash/tests/otel.test.mjs +188 -0
- package/bizar-dash/tests/plugins-registry.test.mjs +387 -0
- package/bizar-dash/tests/plugins-sandbox.test.mjs +374 -0
- package/bizar-dash/tests/plugins-store.test.mjs +455 -0
- package/bizar-dash/tests/users.test.mjs +108 -0
- package/bizar-dash/tests/voice-recorder.test.tsx +95 -0
- package/bizar-dash/tests/voice-store.test.mjs +148 -0
- package/bizar-dash/tests/voice-transcribe.test.mjs +87 -0
- package/bizar-dash/tests/workspaces.test.mjs +527 -0
- package/cli/bin.mjs +72 -2
- package/cli/commands/clip.mjs +146 -0
- package/cli/commands/dash.mjs +6 -0
- package/cli/commands/deploy/cloudflare.mjs +250 -0
- package/cli/commands/deploy/docker.mjs +221 -0
- package/cli/commands/deploy/fly.mjs +161 -0
- package/cli/commands/deploy/vercel.mjs +225 -0
- package/cli/commands/deploy.mjs +240 -0
- package/cli/commands/eval.mjs +378 -0
- package/cli/commands/marketplace.mjs +64 -0
- package/cli/commands/ocr.mjs +165 -0
- package/cli/commands/plugin.mjs +358 -0
- package/cli/commands/voice.mjs +211 -0
- package/cli/commands/workspace.mjs +247 -0
- package/package.json +12 -2
- package/templates/deploy/cloudflare/README.md +32 -0
- package/templates/deploy/cloudflare/functions-index.template.js +15 -0
- package/templates/deploy/cloudflare/wrangler.toml.template +9 -0
- package/templates/deploy/docker/.env.template +16 -0
- package/templates/deploy/docker/README.md +58 -0
- package/templates/deploy/docker/docker-compose.template.yml +23 -0
- package/templates/deploy/fly/README.md +35 -0
- package/templates/deploy/fly/fly.toml.template +28 -0
- package/templates/deploy/vercel/README.md +29 -0
- package/templates/deploy/vercel/api-index.template.js +18 -0
- package/templates/deploy/vercel/vercel.json.template +16 -0
- package/templates/eval-fixtures/README.md +58 -0
- package/templates/eval-fixtures/code-search-basic.json +28 -0
- package/templates/eval-fixtures/latency-bounds.json +16 -0
- package/templates/eval-fixtures/regression-suite.json +79 -0
- package/templates/eval-fixtures/response-format.json +30 -0
- package/templates/eval-fixtures/tool-call-correctness.json +24 -0
- package/templates/plugin-template/README.md +121 -0
- package/templates/plugin-template/index.js +66 -0
- package/templates/plugin-template/plugin.json +42 -0
- package/templates/plugin-template/tests/plugin.test.js +83 -0
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js +0 -361
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js +0 -351
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-Chvf9u_B.js +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/workspace.mjs
|
|
3
|
+
*
|
|
4
|
+
* v5.0.0 — Workspace management CLI — talks to the running dashboard's HTTP API.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
|
|
11
|
+
const HOME = homedir();
|
|
12
|
+
|
|
13
|
+
// ── Config dir helper ──────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function getBizarConfigDir() {
|
|
16
|
+
if (process.platform === 'win32') {
|
|
17
|
+
return process.env.APPDATA
|
|
18
|
+
? join(process.env.APPDATA, 'bizar')
|
|
19
|
+
: join(homedir(), '.config', 'bizar');
|
|
20
|
+
}
|
|
21
|
+
return process.env.XDG_CONFIG_HOME
|
|
22
|
+
? join(process.env.XDG_CONFIG_HOME, 'bizar')
|
|
23
|
+
: join(homedir(), '.config', 'bizar');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getDashboardPort() {
|
|
27
|
+
const portFile = join(getBizarConfigDir(), 'dashboard.port');
|
|
28
|
+
try {
|
|
29
|
+
const port = parseInt(readFileSync(portFile, 'utf8').trim(), 10);
|
|
30
|
+
if (Number.isFinite(port) && port > 0) return port;
|
|
31
|
+
} catch {
|
|
32
|
+
/* fall through */
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── API helpers ───────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
async function postJson(baseUrl, path, body) {
|
|
40
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
body: JSON.stringify(body || {}),
|
|
44
|
+
});
|
|
45
|
+
const text = await res.text();
|
|
46
|
+
let json = null;
|
|
47
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
50
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
51
|
+
}
|
|
52
|
+
return json;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getJson(baseUrl, path) {
|
|
56
|
+
const res = await fetch(`${baseUrl}${path}`);
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
let json = null;
|
|
59
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
62
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
63
|
+
}
|
|
64
|
+
return json;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function delJson(baseUrl, path) {
|
|
68
|
+
const res = await fetch(`${baseUrl}${path}`, { method: 'DELETE' });
|
|
69
|
+
const text = await res.text();
|
|
70
|
+
let json = null;
|
|
71
|
+
try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const msg = json?.message || json?.error || text || `HTTP ${res.status}`;
|
|
74
|
+
throw new Error(`${path} failed: ${msg}`);
|
|
75
|
+
}
|
|
76
|
+
return json;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Help ───────────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export function showWorkspaceHelp() {
|
|
82
|
+
console.log(`
|
|
83
|
+
bizar workspace — Manage workspaces (via the dashboard's HTTP API)
|
|
84
|
+
|
|
85
|
+
Usage:
|
|
86
|
+
bizar workspace list List all workspaces
|
|
87
|
+
bizar workspace create <name> Create a new workspace
|
|
88
|
+
bizar workspace switch <id> Set active workspace
|
|
89
|
+
bizar workspace invite <email> [role] Generate invite URL (role: admin|editor|viewer)
|
|
90
|
+
bizar workspace accept <token> Accept an invite
|
|
91
|
+
bizar workspace members List workspace members
|
|
92
|
+
bizar workspace remove-member <userId> Remove a member
|
|
93
|
+
|
|
94
|
+
Description:
|
|
95
|
+
Subcommands call the running dashboard's HTTP API. If no dashboard is
|
|
96
|
+
reachable, you'll be told to run \`bizar dash start\` first.
|
|
97
|
+
|
|
98
|
+
Workspaces let you share access with team members. Each workspace has
|
|
99
|
+
its own members, settings, and data.
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
bizar workspace list
|
|
103
|
+
bizar workspace create "Engineering"
|
|
104
|
+
bizar workspace invite alice@example.com editor
|
|
105
|
+
bizar workspace accept abc123def456
|
|
106
|
+
bizar workspace members
|
|
107
|
+
bizar workspace remove-member usr_abc123
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Command runner ───────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export async function runWorkspaceCommand(args) {
|
|
114
|
+
const sub = args[0];
|
|
115
|
+
const positional = args.slice(1).filter((a) => !a.startsWith('-'));
|
|
116
|
+
const flags = args.slice(1).filter((a) => a.startsWith('-'));
|
|
117
|
+
|
|
118
|
+
if (!sub || sub === '--help' || sub === '-h' || flags.includes('--help') || flags.includes('-h')) {
|
|
119
|
+
showWorkspaceHelp();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const port = getDashboardPort();
|
|
124
|
+
if (!port) {
|
|
125
|
+
console.error(chalk.red(' ✗ Dashboard is not running (no port file).'));
|
|
126
|
+
console.error(chalk.dim(' Start it first: `bizar dash start --bg`'));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
if (sub === 'list') {
|
|
133
|
+
const r = await getJson(baseUrl, '/api/workspaces');
|
|
134
|
+
const workspaces = r.workspaces || [];
|
|
135
|
+
if (workspaces.length === 0) {
|
|
136
|
+
console.log(chalk.dim(' (no workspaces)'));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
console.log(chalk.bold(' Workspaces:'));
|
|
140
|
+
for (const { workspace, role } of workspaces) {
|
|
141
|
+
const badge = role === 'admin' ? chalk.green('admin') : role === 'editor' ? chalk.yellow('editor') : chalk.blue('viewer');
|
|
142
|
+
console.log(` ${workspace.id.padEnd(20)} ${workspace.name.padEnd(30)} ${badge}`);
|
|
143
|
+
}
|
|
144
|
+
} else if (sub === 'create') {
|
|
145
|
+
const name = positional[0];
|
|
146
|
+
if (!name) {
|
|
147
|
+
console.error(chalk.red(' ✗ Missing workspace name. Usage: bizar workspace create <name>'));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
const r = await postJson(baseUrl, '/api/workspaces', { name });
|
|
151
|
+
console.log(chalk.green(` ✓ Created workspace "${r.workspace.name}" (${r.workspace.id})`));
|
|
152
|
+
} else if (sub === 'switch') {
|
|
153
|
+
const wsId = positional[0];
|
|
154
|
+
if (!wsId) {
|
|
155
|
+
console.error(chalk.red(' ✗ Missing workspace id. Usage: bizar workspace switch <id>'));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
// Verify workspace exists
|
|
159
|
+
try {
|
|
160
|
+
await getJson(baseUrl, `/api/workspaces/${wsId}`);
|
|
161
|
+
} catch {
|
|
162
|
+
console.error(chalk.red(` ✗ Workspace not found: ${wsId}`));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.green(` ✓ Switched to workspace ${wsId}`));
|
|
166
|
+
console.log(chalk.dim(' Note: Run `bizar workspace list` to see workspace ids'));
|
|
167
|
+
} else if (sub === 'invite') {
|
|
168
|
+
const email = positional[0];
|
|
169
|
+
if (!email) {
|
|
170
|
+
console.error(chalk.red(' ✗ Missing email. Usage: bizar workspace invite <email> [role]'));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const role = positional[1] || 'editor';
|
|
174
|
+
if (!['admin', 'editor', 'viewer'].includes(role)) {
|
|
175
|
+
console.error(chalk.red(` ✗ Invalid role: ${role}. Must be admin, editor, or viewer.`));
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
// Get first workspace id (or use BIZAR_WORKSPACE_ID env)
|
|
179
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
180
|
+
if (!wsId) {
|
|
181
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var or run from a workspace context.'));
|
|
182
|
+
console.error(chalk.dim(' Run `bizar workspace list` to find your workspace id.'));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
const r = await postJson(baseUrl, `/api/workspaces/${wsId}/invites`, { email, role });
|
|
186
|
+
console.log(chalk.green(` ✓ Invite created for ${email} as ${role}`));
|
|
187
|
+
console.log(chalk.dim(' Invite URL:'));
|
|
188
|
+
console.log(` ${r.url}`);
|
|
189
|
+
} else if (sub === 'accept') {
|
|
190
|
+
const token = positional[0];
|
|
191
|
+
if (!token) {
|
|
192
|
+
console.error(chalk.red(' ✗ Missing invite token. Usage: bizar workspace accept <token>'));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
// For CLI acceptance, we need user info - prompt or use env
|
|
196
|
+
const email = process.env.BIZAR_USER_EMAIL;
|
|
197
|
+
const name = process.env.BIZAR_USER_NAME || 'CLI User';
|
|
198
|
+
if (!email) {
|
|
199
|
+
console.error(chalk.red(' ✗ Set BIZAR_USER_EMAIL env var with your email address.'));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const r = await postJson(baseUrl, `/api/invites/${token}/accept`, { email, name });
|
|
203
|
+
console.log(chalk.green(` ✓ Joined workspace "${r.workspace.name}" as ${r.role}`));
|
|
204
|
+
} else if (sub === 'members') {
|
|
205
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
206
|
+
if (!wsId) {
|
|
207
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var or run from a workspace context.'));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const r = await getJson(baseUrl, `/api/workspaces/${wsId}`);
|
|
211
|
+
const members = r.members || [];
|
|
212
|
+
if (members.length === 0) {
|
|
213
|
+
console.log(chalk.dim(' (no members)'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(chalk.bold(` Members of ${r.workspace.name}:`));
|
|
217
|
+
for (const m of members) {
|
|
218
|
+
const badge = m.role === 'admin' ? chalk.green('admin') : m.role === 'editor' ? chalk.yellow('editor') : chalk.blue('viewer');
|
|
219
|
+
console.log(` ${m.userId.padEnd(20)} ${(m.name || m.email).padEnd(20)} ${badge}`);
|
|
220
|
+
}
|
|
221
|
+
} else if (sub === 'remove-member') {
|
|
222
|
+
const userId = positional[0];
|
|
223
|
+
if (!userId) {
|
|
224
|
+
console.error(chalk.red(' ✗ Missing user id. Usage: bizar workspace remove-member <userId>'));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
const wsId = process.env.BIZAR_WORKSPACE_ID;
|
|
228
|
+
if (!wsId) {
|
|
229
|
+
console.error(chalk.red(' ✗ Set BIZAR_WORKSPACE_ID env var.'));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
await delJson(baseUrl, `/api/workspaces/${wsId}/members/${userId}`);
|
|
233
|
+
console.log(chalk.green(` ✓ Removed member ${userId}`));
|
|
234
|
+
} else {
|
|
235
|
+
console.error(chalk.red(` ✗ Unknown workspace subcommand: ${sub}`));
|
|
236
|
+
showWorkspaceHelp();
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error(chalk.red(` ✗ ${err.message}`));
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function run(name, args, isHelpRequest) {
|
|
246
|
+
await runWorkspaceCommand(args);
|
|
247
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"cors": "^2.8.6",
|
|
56
56
|
"croner": "^10.0.1",
|
|
57
57
|
"express": "^4.22.0",
|
|
58
|
+
"formidable": "^3.5.0",
|
|
58
59
|
"fuse.js": "^7.0.0",
|
|
59
60
|
"inquirer": "^12.0.0",
|
|
60
61
|
"lucide-react": "^0.460.0",
|
|
@@ -65,8 +66,15 @@
|
|
|
65
66
|
"react-dom": "^18.3.0",
|
|
66
67
|
"react-markdown": "^9.0.0",
|
|
67
68
|
"remark-gfm": "^4.0.0",
|
|
69
|
+
"tesseract.js": "^5.1.1",
|
|
68
70
|
"ws": "^8.18.0",
|
|
69
|
-
"zod": "4.1.8"
|
|
71
|
+
"zod": "4.1.8",
|
|
72
|
+
"@opentelemetry/api": "^1.9.1",
|
|
73
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.52.1",
|
|
74
|
+
"@opentelemetry/resources": "^1.30.1",
|
|
75
|
+
"@opentelemetry/sdk-node": "^0.52.1",
|
|
76
|
+
"@opentelemetry/sdk-trace-node": "^1.30.1",
|
|
77
|
+
"@opentelemetry/semantic-conventions": "^1.41.1"
|
|
70
78
|
},
|
|
71
79
|
"peerDependencies": {
|
|
72
80
|
"@opencode-ai/plugin": ">=1.17.0"
|
|
@@ -87,7 +95,9 @@
|
|
|
87
95
|
"@types/react": "^18.3.0",
|
|
88
96
|
"@types/react-dom": "^18.3.0",
|
|
89
97
|
"@vitejs/plugin-react": "^4.3.0",
|
|
98
|
+
"js-yaml": "^5.2.1",
|
|
90
99
|
"jsdom": "^25.0.1",
|
|
100
|
+
"smol-toml": "^1.7.0",
|
|
91
101
|
"typescript": "^5.6.0",
|
|
92
102
|
"vite": "^5.0.0",
|
|
93
103
|
"vitest": "^4.1.9"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Cloudflare Deployment
|
|
2
|
+
|
|
3
|
+
This directory contains the scaffold files for deploying the Bizar dashboard
|
|
4
|
+
to Cloudflare Pages + Workers. Generated by `bizar deploy --to cloudflare`.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `wrangler.toml` — Cloudflare Pages project configuration
|
|
9
|
+
- `functions/index.js` — Workers function entry (handles SPA routing)
|
|
10
|
+
- `public/` — Static dashboard bundle (built from `npm run build`)
|
|
11
|
+
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
1. The dashboard is built with `npm run build` → `dist/`
|
|
15
|
+
2. Built assets are copied to `public/`
|
|
16
|
+
3. `wrangler.toml` configures the Pages project
|
|
17
|
+
4. `functions/index.js` handles SPA routing for non-asset paths
|
|
18
|
+
5. The deployment is pushed to Cloudflare via wrangler CLI or REST API
|
|
19
|
+
|
|
20
|
+
## Manual deployment
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Install wrangler
|
|
24
|
+
npm i -g wrangler
|
|
25
|
+
|
|
26
|
+
# Login to Cloudflare
|
|
27
|
+
wrangler login
|
|
28
|
+
|
|
29
|
+
# Deploy from the scaffold directory
|
|
30
|
+
cd bizar-deploy/cloudflare
|
|
31
|
+
npx wrangler pages deploy . --project-name bizar-dash
|
|
32
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Cloudflare Workers entry — Bizar dashboard
|
|
2
|
+
// This file becomes functions/index.js in the deployment.
|
|
3
|
+
|
|
4
|
+
export async function onRequest(context) {
|
|
5
|
+
const { request, env } = context;
|
|
6
|
+
const url = new URL(request.url);
|
|
7
|
+
|
|
8
|
+
// Serve static assets from Pages KV
|
|
9
|
+
if (url.pathname.startsWith("/assets/")) {
|
|
10
|
+
return env.ASSETS.fetch(request);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// SPA fallback — serve index.html for all non-asset routes
|
|
14
|
+
return env.ASSETS.fetch(new Request(url.origin + "/index.html", request));
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Bizar dashboard environment variables
|
|
2
|
+
# Copy this file to .env and fill in your values.
|
|
3
|
+
|
|
4
|
+
# Dashboard
|
|
5
|
+
# BIZAR_DASHBOARD_PORT=4097
|
|
6
|
+
# BIZAR_DASHBOARD_HOST=0.0.0.0
|
|
7
|
+
|
|
8
|
+
# OpenTelemetry (optional)
|
|
9
|
+
# BIZAR_OTEL=1
|
|
10
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
|
|
11
|
+
|
|
12
|
+
# Tailscale (optional)
|
|
13
|
+
# TAILSCALE_AUTHKEY=tskey-auth-xxxxxxxxxxxxxxxxxxxx
|
|
14
|
+
|
|
15
|
+
# Headroom context compression (optional)
|
|
16
|
+
# HEADROOM_ENABLED=1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Docker Deployment
|
|
2
|
+
|
|
3
|
+
This directory contains the scaffold files for deploying the Bizar dashboard
|
|
4
|
+
as a Docker container. Generated by `bizar deploy --to docker`.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `docker-compose.yml` — Docker Compose configuration for self-hosting
|
|
9
|
+
- `.env.template` — Environment variable template
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- Docker Engine 24+ (or Docker Desktop)
|
|
14
|
+
- Docker Compose v2 (`docker compose` plugin)
|
|
15
|
+
- A container registry account (GitHub Container Registry, Docker Hub, etc.)
|
|
16
|
+
|
|
17
|
+
## How it works
|
|
18
|
+
|
|
19
|
+
The `bizar deploy --to docker` command:
|
|
20
|
+
|
|
21
|
+
1. Builds the Docker image using the project's `Dockerfile`
|
|
22
|
+
2. Tags it with `<registry>/<image-name>:<version>` and `:latest`
|
|
23
|
+
3. Pushes both tags to the registry
|
|
24
|
+
4. With `--compose`: generates `docker-compose.yml` + `.env.template`
|
|
25
|
+
|
|
26
|
+
## Self-hosting with Docker Compose
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Generate compose files
|
|
30
|
+
bizar deploy --to docker --compose
|
|
31
|
+
|
|
32
|
+
# Edit .env with your settings
|
|
33
|
+
cp .env.template .env
|
|
34
|
+
$EDITOR .env
|
|
35
|
+
|
|
36
|
+
# Start the dashboard
|
|
37
|
+
cd bizar-deploy/docker
|
|
38
|
+
docker compose up -d
|
|
39
|
+
|
|
40
|
+
# Check logs
|
|
41
|
+
docker compose logs -f
|
|
42
|
+
|
|
43
|
+
# Stop
|
|
44
|
+
docker compose down
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Manual build & push
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Build
|
|
51
|
+
docker build -t ghcr.io/my-org/bizar-dash:latest .
|
|
52
|
+
|
|
53
|
+
# Push
|
|
54
|
+
docker push ghcr.io/my-org/bizar-dash:latest
|
|
55
|
+
|
|
56
|
+
# Run
|
|
57
|
+
docker run -d -p 4097:4097 ghcr.io/my-org/bizar-dash:latest
|
|
58
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: "3.8"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
bizar-dash:
|
|
5
|
+
image: ghcr.io/your-org/bizar-dash:latest
|
|
6
|
+
container_name: bizar-dash
|
|
7
|
+
restart: unless-stopped
|
|
8
|
+
ports:
|
|
9
|
+
- "4097:4097"
|
|
10
|
+
- "4098:4098"
|
|
11
|
+
environment:
|
|
12
|
+
- BIZAR_DASHBOARD_PORT=4097
|
|
13
|
+
- BIZAR_DASHBOARD_HOST=0.0.0.0
|
|
14
|
+
- NODE_ENV=production
|
|
15
|
+
env_file:
|
|
16
|
+
- .env
|
|
17
|
+
volumes:
|
|
18
|
+
- bizar-config:/home/bizar/.config/bizar
|
|
19
|
+
- bizar-memory:/home/bizar/.local/share/bizar/memory
|
|
20
|
+
|
|
21
|
+
volumes:
|
|
22
|
+
bizar-config:
|
|
23
|
+
bizar-memory:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Fly.io Deployment
|
|
2
|
+
|
|
3
|
+
This directory contains the scaffold file for deploying the Bizar dashboard
|
|
4
|
+
to Fly.io. Generated by `bizar deploy --to fly`.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `fly.toml` — Fly.io application configuration
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- [flyctl](https://fly.io/docs/getting-started/installing-flyctl/) installed
|
|
13
|
+
- A Fly.io account (sign up: `flyctl auth signup`)
|
|
14
|
+
- Dockerfile at the project root (already present in BizarHarness)
|
|
15
|
+
|
|
16
|
+
## How it works
|
|
17
|
+
|
|
18
|
+
1. `fly.toml` references the project's `Dockerfile` for building
|
|
19
|
+
2. The app is launched on Fly.io (if not already created)
|
|
20
|
+
3. `flyctl deploy` builds and deploys the container
|
|
21
|
+
4. The dashboard is served at `https://<app-name>.fly.dev`
|
|
22
|
+
|
|
23
|
+
## Manual deployment
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Login
|
|
27
|
+
flyctl auth login
|
|
28
|
+
|
|
29
|
+
# Launch (first time)
|
|
30
|
+
cd bizar-deploy/fly
|
|
31
|
+
flyctl launch --name bizar-dash --region iad --now
|
|
32
|
+
|
|
33
|
+
# Deploy (subsequent times)
|
|
34
|
+
flyctl deploy --app bizar-dash
|
|
35
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# fly.toml — Bizar dashboard
|
|
2
|
+
# Generated by `bizar deploy --to fly`
|
|
3
|
+
|
|
4
|
+
app = "bizar-dash"
|
|
5
|
+
primary_region = "iad"
|
|
6
|
+
|
|
7
|
+
[build]
|
|
8
|
+
dockerfile = "../Dockerfile"
|
|
9
|
+
|
|
10
|
+
[http_service]
|
|
11
|
+
internal_port = 4097
|
|
12
|
+
force_https = true
|
|
13
|
+
auto_stop_machines = true
|
|
14
|
+
auto_start_machines = true
|
|
15
|
+
min_machines_running = 0
|
|
16
|
+
|
|
17
|
+
[[vm]]
|
|
18
|
+
memory = "512mb"
|
|
19
|
+
cpu_kind = "shared"
|
|
20
|
+
cpus = 1
|
|
21
|
+
|
|
22
|
+
[env]
|
|
23
|
+
BIZAR_DASHBOARD_PORT = "4097"
|
|
24
|
+
BIZAR_DASHBOARD_HOST = "0.0.0.0"
|
|
25
|
+
NODE_ENV = "production"
|
|
26
|
+
|
|
27
|
+
[deploy]
|
|
28
|
+
strategy = "rolling"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Vercel Deployment
|
|
2
|
+
|
|
3
|
+
This directory contains the scaffold files for deploying the Bizar dashboard
|
|
4
|
+
to Vercel. Generated by `bizar deploy --to vercel`.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- `vercel.json` — Vercel project configuration
|
|
9
|
+
- `api/index.js` — Serverless function entry (Express app that serves the SPA)
|
|
10
|
+
- `public/` — Static dashboard bundle (built from `npm run build`)
|
|
11
|
+
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
1. The dashboard is built with `npm run build` → `dist/`
|
|
15
|
+
2. Built assets are copied to `public/`
|
|
16
|
+
3. `vercel.json` routes API calls to the serverless function and everything
|
|
17
|
+
else to the static SPA
|
|
18
|
+
4. The deployment is pushed to Vercel via the REST API
|
|
19
|
+
|
|
20
|
+
## Manual deployment
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Install Vercel CLI
|
|
24
|
+
npm i -g vercel
|
|
25
|
+
|
|
26
|
+
# Deploy from the scaffold directory
|
|
27
|
+
cd bizar-deploy/vercel
|
|
28
|
+
vercel --prod
|
|
29
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Vercel serverless entry — Bizar dashboard
|
|
2
|
+
// This file becomes api/index.js in the deployment.
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
// Serve static files from the dashboard build
|
|
11
|
+
app.use(express.static(join(__dirname, '..', 'public')));
|
|
12
|
+
|
|
13
|
+
// Fallback to index.html for SPA routing
|
|
14
|
+
app.get('*', (_req, res) => {
|
|
15
|
+
res.sendFile(join(__dirname, '..', 'public', 'index.html'));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default app;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bizar-dash",
|
|
3
|
+
"version": 2,
|
|
4
|
+
"buildCommand": "npm run build",
|
|
5
|
+
"outputDirectory": "public",
|
|
6
|
+
"routes": [
|
|
7
|
+
{ "src": "/api/(.*)", "dest": "/api/index.js" },
|
|
8
|
+
{ "src": "/(.*)", "dest": "/public/$1" }
|
|
9
|
+
],
|
|
10
|
+
"functions": {
|
|
11
|
+
"api/index.js": {
|
|
12
|
+
"memory": 256,
|
|
13
|
+
"maxDuration": 30
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Eval Fixtures
|
|
2
|
+
|
|
3
|
+
Example fixture suite for the BizarHarness eval framework.
|
|
4
|
+
|
|
5
|
+
## Fixtures
|
|
6
|
+
|
|
7
|
+
| File | Description | Tags |
|
|
8
|
+
|------|-------------|------|
|
|
9
|
+
| `code-search-basic.json` | Verifies agent can find a function definition | smoke, code-search |
|
|
10
|
+
| `tool-call-correctness.json` | Verifies correct tool usage | smoke, tools |
|
|
11
|
+
| `response-format.json` | Verifies response is valid JSON with expected shape | smoke, format |
|
|
12
|
+
| `latency-bounds.json` | Verifies response arrives within latency budget | smoke, latency |
|
|
13
|
+
| `regression-suite.json` | Suite that runs all of the above | regression |
|
|
14
|
+
|
|
15
|
+
## Running
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Run all fixtures
|
|
19
|
+
bizar eval run ./templates/eval-fixtures
|
|
20
|
+
|
|
21
|
+
# Run with custom concurrency
|
|
22
|
+
bizar eval run ./templates/eval-fixtures --concurrency=3
|
|
23
|
+
|
|
24
|
+
# List recent runs
|
|
25
|
+
bizar eval list
|
|
26
|
+
|
|
27
|
+
# Compare two runs
|
|
28
|
+
bizar eval diff run_2026-07-05 run_2026-07-04
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Fixture Schema
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"id": "unique-fixture-id",
|
|
36
|
+
"name": "Human Readable Name",
|
|
37
|
+
"description": "What this fixture verifies",
|
|
38
|
+
"agent": "thor",
|
|
39
|
+
"prompt": "The prompt to send to the agent",
|
|
40
|
+
"expected": {
|
|
41
|
+
"contains": ["expected substring 1", "expected substring 2"],
|
|
42
|
+
"notContains": ["forbidden substring"],
|
|
43
|
+
"regex": ["expected regex pattern"],
|
|
44
|
+
"jsonSchema": null,
|
|
45
|
+
"maxTokens": 2000,
|
|
46
|
+
"maxLatencyMs": 30000
|
|
47
|
+
},
|
|
48
|
+
"tags": ["smoke", "my-tag"]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Writing Fixtures
|
|
53
|
+
|
|
54
|
+
1. **Start simple** — begin with a `contains` check
|
|
55
|
+
2. **Be specific** — match exact strings, not approximate ones
|
|
56
|
+
3. **Use `notContains`** for things the agent should NOT do (e.g., TODO, FIXME, apologetic phrases)
|
|
57
|
+
4. **Latency budgets** — set `maxLatencyMs` to catch regressions
|
|
58
|
+
5. **Tag wisely** — use tags to filter fixtures: `bizar eval run --tag smoke`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "code-search-basic",
|
|
3
|
+
"name": "Basic code search",
|
|
4
|
+
"description": "Verifies the agent can find a function definition using the sembe search tool",
|
|
5
|
+
"agent": "thor",
|
|
6
|
+
"prompt": "Find the function named 'add' in this repository. Show its signature and parameter names. Do not guess — use the code search tool to locate it first.",
|
|
7
|
+
"expected": {
|
|
8
|
+
"contains": [
|
|
9
|
+
"function add",
|
|
10
|
+
"a",
|
|
11
|
+
"b"
|
|
12
|
+
],
|
|
13
|
+
"notContains": [
|
|
14
|
+
"TODO",
|
|
15
|
+
"FIXME",
|
|
16
|
+
"Not found",
|
|
17
|
+
"could not locate"
|
|
18
|
+
],
|
|
19
|
+
"regex": [
|
|
20
|
+
"function\\s+add\\s*\\(",
|
|
21
|
+
"const\\s+add\\s*=\\s*\\("
|
|
22
|
+
],
|
|
23
|
+
"jsonSchema": null,
|
|
24
|
+
"maxTokens": 500,
|
|
25
|
+
"maxLatencyMs": 30000
|
|
26
|
+
},
|
|
27
|
+
"tags": ["code-search", "smoke"]
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "latency-bounds",
|
|
3
|
+
"name": "Latency bounds",
|
|
4
|
+
"description": "Verifies the agent responds within a strict latency budget",
|
|
5
|
+
"agent": "thor",
|
|
6
|
+
"prompt": "Respond with exactly the word 'ok' and nothing else.",
|
|
7
|
+
"expected": {
|
|
8
|
+
"contains": ["ok"],
|
|
9
|
+
"notContains": [],
|
|
10
|
+
"regex": [],
|
|
11
|
+
"jsonSchema": null,
|
|
12
|
+
"maxTokens": 50,
|
|
13
|
+
"maxLatencyMs": 10000
|
|
14
|
+
},
|
|
15
|
+
"tags": ["latency", "performance"]
|
|
16
|
+
}
|