@polderlabs/bizar 10.0.7 → 10.1.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/src/server/api.mjs +2 -6
- package/bizar-dash/src/server/diagnostics-store.mjs +5 -35
- package/bizar-dash/src/server/mod-security.mjs +1 -2
- package/bizar-dash/src/server/providers-store.mjs +1 -1
- package/bizar-dash/src/server/routes/_shared.mjs +0 -17
- package/bizar-dash/src/server/routes/model-router.mjs +103 -0
- package/bizar-dash/src/server/routes/model-router.test.mjs +76 -0
- package/bizar-dash/src/server/server.mjs +1 -27
- package/bizar-dash/tests/cli-bugfixes.test.mjs +1 -1
- package/bizar-dash/tests/cli-error-visibility.test.mjs +1 -1
- package/bizar-dash/tests/cli-refactor.test.mjs +0 -1
- package/bizar-dash/tests/diagnostics-store.test.mjs +0 -1
- package/cli/bin.mjs +1 -15
- package/cli/cli-commands-validation.test.mjs +1 -1
- package/cli/commands/lightrag.mjs +1 -1
- package/cli/commands/util.mjs +1 -1
- package/cli/copy.mjs +1 -47
- package/cli/doctor.mjs +3 -3
- package/cli/install.mjs +3 -37
- package/cli/service-controller.mjs +0 -192
- package/cli/service-env.mjs +2 -9
- package/cli/utils.mjs +0 -7
- package/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/scripts/check-deps.mjs +0 -24
- package/bizar-dash/skills/headroom/SKILL.md +0 -94
- package/bizar-dash/src/server/headroom.mjs +0 -649
- package/bizar-dash/src/server/routes/headroom.mjs +0 -126
- package/bizar-dash/tests/headroom-install.test.mjs +0 -173
- package/bizar-dash/tests/headroom-settings.test.mjs +0 -126
- package/bizar-dash/tests/headroom-status.test.mjs +0 -117
- package/cli/commands/headroom.mjs +0 -204
|
@@ -102,9 +102,6 @@ function linuxUnitPath() {
|
|
|
102
102
|
function linuxEnvPath() {
|
|
103
103
|
return join(bizarConfigDir(), 'service.env');
|
|
104
104
|
}
|
|
105
|
-
function linuxHeadroomUnitPath() {
|
|
106
|
-
return join(userUnitDir(), 'bizar-headroom.service');
|
|
107
|
-
}
|
|
108
105
|
function darwinPlistPath() {
|
|
109
106
|
return join(launchAgentsDir(), 'com.bizar.dashboard.plist');
|
|
110
107
|
}
|
|
@@ -178,161 +175,6 @@ function linuxEnvFileContent({ projectRoot }) {
|
|
|
178
175
|
return buildServiceEnvFile({ bizarHome: bizarConfigDir(), repoPath: projectRoot });
|
|
179
176
|
}
|
|
180
177
|
|
|
181
|
-
// ── Headroom companion service (Linux) ─────────────────────────────────────────
|
|
182
|
-
|
|
183
|
-
function linuxHeadroomUnitContent() {
|
|
184
|
-
const envPath = join(bizarConfigDir(), 'service.env');
|
|
185
|
-
return [
|
|
186
|
-
'[Unit]',
|
|
187
|
-
'Description=Bizar Headroom context-compression proxy',
|
|
188
|
-
'After=bizar.service',
|
|
189
|
-
'Wants=bizar.service',
|
|
190
|
-
'',
|
|
191
|
-
'[Service]',
|
|
192
|
-
'Type=simple',
|
|
193
|
-
`EnvironmentFile=${envPath}`,
|
|
194
|
-
'ExecStart=/bin/sh -c "command -v headroom >/dev/null 2>&1 && headroom proxy cline || exit 0"',
|
|
195
|
-
'Restart=on-failure',
|
|
196
|
-
'RestartSec=10',
|
|
197
|
-
'TimeoutStopSec=15',
|
|
198
|
-
'',
|
|
199
|
-
'[Install]',
|
|
200
|
-
'WantedBy=default.target',
|
|
201
|
-
'',
|
|
202
|
-
].join('\n');
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function darwinHeadroomPlistPath() {
|
|
206
|
-
return join(launchAgentsDir(), 'com.bizar.headroom.plist');
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function darwinHeadroomPlistContent({ nodePath, projectRoot }) {
|
|
210
|
-
const envContent = buildServiceEnvFile({ bizarHome: bizarConfigDir(), repoPath: projectRoot });
|
|
211
|
-
const envVars = parseEnvFile(envContent);
|
|
212
|
-
const envXml = envVars
|
|
213
|
-
.map(([k, v]) => ` <key>${xmlEscape(k)}</key>\n <string>${xmlEscape(v)}</string>`)
|
|
214
|
-
.join('\n');
|
|
215
|
-
return [
|
|
216
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
217
|
-
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
218
|
-
'<plist version="1.0">',
|
|
219
|
-
'<dict>',
|
|
220
|
-
' <key>Label</key>',
|
|
221
|
-
' <string>com.bizar.headroom</string>',
|
|
222
|
-
' <key>ProgramArguments</key>',
|
|
223
|
-
' <array>',
|
|
224
|
-
` <string>${xmlEscape(nodePath)}</string>`,
|
|
225
|
-
' <string>-c</string>',
|
|
226
|
-
' <string>command -v headroom >/dev/null 2>&1 && headroom proxy cline || exit 0</string>',
|
|
227
|
-
' </array>',
|
|
228
|
-
' <key>EnvironmentVariables</key>',
|
|
229
|
-
' <dict>',
|
|
230
|
-
envXml,
|
|
231
|
-
' </dict>',
|
|
232
|
-
' <key>KeepAlive</key>',
|
|
233
|
-
' <true/>',
|
|
234
|
-
' <key>RunAtLoad</key>',
|
|
235
|
-
' <true/>',
|
|
236
|
-
' <key>StandardOutPath</key>',
|
|
237
|
-
` <string>${xmlEscape(join(bizarConfigDir(), 'headroom.log'))}</string>`,
|
|
238
|
-
' <key>StandardErrorPath</key>',
|
|
239
|
-
` <string>${xmlEscape(join(bizarConfigDir(), 'headroom.log'))}</string>`,
|
|
240
|
-
' <key>WorkingDirectory</key>',
|
|
241
|
-
` <string>${xmlEscape(projectRoot)}</string>`,
|
|
242
|
-
'</dict>',
|
|
243
|
-
'</plist>',
|
|
244
|
-
'',
|
|
245
|
-
].join('\n');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function installHeadroomServiceLinux({ force = false }) {
|
|
249
|
-
// Honour BIZAR_HEADROOM_AUTOSTART from process.env (already merged by buildServiceEnvFile)
|
|
250
|
-
const headroomAutoStart = process.env.BIZAR_HEADROOM_AUTOSTART !== '0';
|
|
251
|
-
if (!headroomAutoStart) {
|
|
252
|
-
return ok({ note: 'headroom autostart disabled via BIZAR_HEADROOM_AUTOSTART=0' });
|
|
253
|
-
}
|
|
254
|
-
if (!existsSync(userUnitDir())) {
|
|
255
|
-
try { mkdirSync(userUnitDir(), { recursive: true }); } catch (err) {
|
|
256
|
-
return fail(`cannot create ${userUnitDir()}: ${err.message}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const unitPath = linuxHeadroomUnitPath();
|
|
260
|
-
const want = linuxHeadroomUnitContent();
|
|
261
|
-
if (!force && existsSync(unitPath)) {
|
|
262
|
-
const have = readFileSync(unitPath, 'utf8').trim() + '\n';
|
|
263
|
-
if (have === want.trim() + '\n') {
|
|
264
|
-
return ok({ alreadyInstalled: true, unitPath, note: 'headroom unit already matches' });
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
writeFileSync(unitPath, want, { encoding: 'utf8', mode: 0o644 });
|
|
269
|
-
} catch (err) {
|
|
270
|
-
return fail(`write ${unitPath}: ${err.message}`);
|
|
271
|
-
}
|
|
272
|
-
runCmd('systemctl', ['--user', 'daemon-reload']);
|
|
273
|
-
const r = runCmd('systemctl', ['--user', 'enable', '--now', 'bizar-headroom.service']);
|
|
274
|
-
if (r.error || (r.status !== 0 && r.status !== null)) {
|
|
275
|
-
return fail(`headroom service enable failed: ${r.stderr || r.error?.message || `exit ${r.status}`}`, { unitPath });
|
|
276
|
-
}
|
|
277
|
-
return ok({ unitPath, note: 'headroom companion service installed and started' });
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function uninstallHeadroomServiceLinux() {
|
|
281
|
-
const unitPath = linuxHeadroomUnitPath();
|
|
282
|
-
if (!existsSync(unitPath)) {
|
|
283
|
-
return ok({ unitPath: null, note: 'no headroom systemd unit installed' });
|
|
284
|
-
}
|
|
285
|
-
runCmd('systemctl', ['--user', 'disable', '--now', 'bizar-headroom.service']);
|
|
286
|
-
try { unlinkSync(unitPath); } catch { /* ignore */ }
|
|
287
|
-
runCmd('systemctl', ['--user', 'daemon-reload']);
|
|
288
|
-
return ok({ unitPath: null, note: 'headroom systemd unit removed' });
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function installHeadroomServiceDarwin({ nodePath, projectRoot, force = false }) {
|
|
292
|
-
const headroomAutoStart = process.env.BIZAR_HEADROOM_AUTOSTART !== '0';
|
|
293
|
-
if (!headroomAutoStart) {
|
|
294
|
-
return ok({ note: 'headroom autostart disabled via BIZAR_HEADROOM_AUTOSTART=0' });
|
|
295
|
-
}
|
|
296
|
-
const dir = launchAgentsDir();
|
|
297
|
-
if (!existsSync(dir)) {
|
|
298
|
-
try { mkdirSync(dir, { recursive: true }); } catch (err) {
|
|
299
|
-
return fail(`cannot create ${dir}: ${err.message}`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
const plistPath = darwinHeadroomPlistPath();
|
|
303
|
-
const want = darwinHeadroomPlistContent({ nodePath, projectRoot });
|
|
304
|
-
if (!force && existsSync(plistPath)) {
|
|
305
|
-
const have = readFileSync(plistPath, 'utf8').trim() + '\n';
|
|
306
|
-
if (have === want.trim() + '\n') {
|
|
307
|
-
const lst = runCmd('launchctl', ['list']);
|
|
308
|
-
if (lst.status === 0 && /com\.bizar\.headroom/.test(lst.stdout)) {
|
|
309
|
-
return ok({ alreadyInstalled: true, unitPath: plistPath, note: 'headroom plist matches and is loaded' });
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
try {
|
|
314
|
-
writeFileSync(plistPath, want, { encoding: 'utf8', mode: 0o644 });
|
|
315
|
-
try { chmodSync(plistPath, 0o644); } catch { /* best-effort */ }
|
|
316
|
-
} catch (err) {
|
|
317
|
-
return fail(`write ${plistPath}: ${err.message}`);
|
|
318
|
-
}
|
|
319
|
-
runCmd('launchctl', ['unload', plistPath]);
|
|
320
|
-
const load = runCmd('launchctl', ['load', '-w', plistPath]);
|
|
321
|
-
if (load.error || (load.status !== 0 && load.status !== null)) {
|
|
322
|
-
return fail(`launchctl load failed for headroom: ${load.stderr || load.error?.message || `exit ${load.status}`}`, { unitPath: plistPath });
|
|
323
|
-
}
|
|
324
|
-
return ok({ unitPath: plistPath, note: 'headroom companion plist installed and loaded' });
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function uninstallHeadroomServiceDarwin() {
|
|
328
|
-
const plistPath = darwinHeadroomPlistPath();
|
|
329
|
-
if (!existsSync(plistPath)) {
|
|
330
|
-
return ok({ unitPath: null, note: 'no headroom launchd plist installed' });
|
|
331
|
-
}
|
|
332
|
-
runCmd('launchctl', ['unload', plistPath]);
|
|
333
|
-
try { unlinkSync(plistPath); } catch { /* ignore */ }
|
|
334
|
-
return ok({ unitPath: null, note: 'headroom launchd plist removed' });
|
|
335
|
-
}
|
|
336
178
|
|
|
337
179
|
function installServiceLinux({ nodePath, projectRoot, force = false }) {
|
|
338
180
|
if (!existsSync(userUnitDir())) {
|
|
@@ -393,13 +235,6 @@ function installServiceLinux({ nodePath, projectRoot, force = false }) {
|
|
|
393
235
|
return fail(`systemctl --user enable --now failed: ${r.stderr || r.error?.message || `exit ${r.status}`}`, { unitPath });
|
|
394
236
|
}
|
|
395
237
|
|
|
396
|
-
// Also install headroom companion service (non-fatal if it fails)
|
|
397
|
-
const hr = installHeadroomServiceLinux({ force });
|
|
398
|
-
if (!hr.ok) {
|
|
399
|
-
// Non-fatal: headroom is optional
|
|
400
|
-
console.log(` ${hr.error || 'headroom companion install failed'}`);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
238
|
return ok({
|
|
404
239
|
unitPath,
|
|
405
240
|
note: 'systemd user unit installed and started (or already running)',
|
|
@@ -562,12 +397,6 @@ function installServiceDarwin({ nodePath, projectRoot, force = false }) {
|
|
|
562
397
|
return fail(`launchctl load failed: ${load.stderr || load.error?.message || `exit ${load.status}`}`, { unitPath: plistPath });
|
|
563
398
|
}
|
|
564
399
|
|
|
565
|
-
// Also install headroom companion service (non-fatal if it fails)
|
|
566
|
-
const hr = installHeadroomServiceDarwin({ nodePath, projectRoot, force });
|
|
567
|
-
if (!hr.ok) {
|
|
568
|
-
console.log(` ${hr.error || 'headroom companion install failed'}`);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
400
|
return ok({ unitPath: plistPath, note: 'plist installed and loaded' });
|
|
572
401
|
}
|
|
573
402
|
|
|
@@ -755,30 +584,9 @@ export function uninstallService() {
|
|
|
755
584
|
else if (PLATFORM === 'darwin') result = uninstallServiceDarwin();
|
|
756
585
|
else if (PLATFORM === 'win32') result = uninstallServiceWindows();
|
|
757
586
|
else return fail(`unsupported platform: ${PLATFORM}`);
|
|
758
|
-
// Also uninstall headroom companion
|
|
759
|
-
if (PLATFORM === 'linux') uninstallHeadroomServiceLinux();
|
|
760
|
-
if (PLATFORM === 'darwin') uninstallHeadroomServiceDarwin();
|
|
761
587
|
return result;
|
|
762
588
|
}
|
|
763
589
|
|
|
764
|
-
/**
|
|
765
|
-
* Install the headroom companion service only (does NOT install the
|
|
766
|
-
* main dashboard service). Idempotent. Safe to re-run.
|
|
767
|
-
*/
|
|
768
|
-
export function installHeadroomService(opts = {}) {
|
|
769
|
-
const nodePath = resolveNodePath(opts.nodePath);
|
|
770
|
-
const projectRoot = resolveRepoRoot(opts.projectRoot);
|
|
771
|
-
if (PLATFORM === 'linux') return installHeadroomServiceLinux({ force: !!opts.force });
|
|
772
|
-
if (PLATFORM === 'darwin') return installHeadroomServiceDarwin({ nodePath, projectRoot, force: !!opts.force });
|
|
773
|
-
return ok({ note: `headroom companion not supported on ${PLATFORM}` });
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
export function uninstallHeadroomService() {
|
|
777
|
-
if (PLATFORM === 'linux') return uninstallHeadroomServiceLinux();
|
|
778
|
-
if (PLATFORM === 'darwin') return uninstallHeadroomServiceDarwin();
|
|
779
|
-
return ok({ note: `headroom companion not supported on ${PLATFORM}` });
|
|
780
|
-
}
|
|
781
|
-
|
|
782
590
|
/**
|
|
783
591
|
* v5.x — Restart the service (issue #7).
|
|
784
592
|
*
|
package/cli/service-env.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* v5.x — Central env file builder for the Bizar service.
|
|
5
5
|
*
|
|
6
6
|
* Produces the content for `~/.config/bizar/service.env` (mode 0600),
|
|
7
|
-
* which is sourced by the systemd/launchd service
|
|
7
|
+
* which is sourced by the systemd/launchd service.
|
|
8
8
|
*
|
|
9
9
|
* All BIZAR_* vars are written here so the service has a consistent view
|
|
10
10
|
* of the environment. Values already present in `process.env` are honoured
|
|
@@ -31,7 +31,6 @@ export function defaultBizarEnv() {
|
|
|
31
31
|
BIZAR_DASHBOARD_PORT: '4097',
|
|
32
32
|
BIZAR_DASHBOARD_HOST: '127.0.0.1',
|
|
33
33
|
BIZAR_LOG_LEVEL: 'info',
|
|
34
|
-
BIZAR_HEADROOM_AUTOSTART: '1',
|
|
35
34
|
BIZAR_LIGHTRAG_AUTOSTART: '1',
|
|
36
35
|
BIZAR_MEMORY_VAULT: join(home, '.bizar_memory'),
|
|
37
36
|
CLINE_SERVER_PASSWORD: '',
|
|
@@ -77,11 +76,10 @@ function generatePassword() {
|
|
|
77
76
|
* @param {object} opts
|
|
78
77
|
* @param {string} [opts.bizarHome] – default: ~/.config/bizar
|
|
79
78
|
* @param {string} [opts.repoPath] – absolute path to the repo root
|
|
80
|
-
* @param {boolean} [opts.headroomAutoStart] – default: true
|
|
81
79
|
* @param {boolean} [opts.lightragAutoStart] – default: true
|
|
82
80
|
* @returns {string} env file content (does NOT write to disk)
|
|
83
81
|
*/
|
|
84
|
-
export function buildServiceEnvFile({ bizarHome, repoPath,
|
|
82
|
+
export function buildServiceEnvFile({ bizarHome, repoPath, lightragAutoStart = true } = {}) {
|
|
85
83
|
const home = homedir();
|
|
86
84
|
const defaults = defaultBizarEnv();
|
|
87
85
|
|
|
@@ -91,11 +89,6 @@ export function buildServiceEnvFile({ bizarHome, repoPath, headroomAutoStart = t
|
|
|
91
89
|
BIZAR_DASHBOARD_PORT: process.env.BIZAR_DASHBOARD_PORT || defaults.BIZAR_DASHBOARD_PORT,
|
|
92
90
|
BIZAR_DASHBOARD_HOST: process.env.BIZAR_DASHBOARD_HOST || defaults.BIZAR_DASHBOARD_HOST,
|
|
93
91
|
BIZAR_LOG_LEVEL: process.env.BIZAR_LOG_LEVEL || defaults.BIZAR_LOG_LEVEL,
|
|
94
|
-
BIZAR_HEADROOM_AUTOSTART: String(
|
|
95
|
-
process.env.BIZAR_HEADROOM_AUTOSTART !== undefined
|
|
96
|
-
? (process.env.BIZAR_HEADROOM_AUTOSTART === '1' || process.env.BIZAR_HEADROOM_AUTOSTART === 'true' ? '1' : '0')
|
|
97
|
-
: (headroomAutoStart ? '1' : '0')
|
|
98
|
-
),
|
|
99
92
|
BIZAR_LIGHTRAG_AUTOSTART: String(
|
|
100
93
|
process.env.BIZAR_LIGHTRAG_AUTOSTART !== undefined
|
|
101
94
|
? (process.env.BIZAR_LIGHTRAG_AUTOSTART === '1' || process.env.BIZAR_LIGHTRAG_AUTOSTART === 'true' ? '1' : '0')
|
package/cli/utils.mjs
CHANGED
|
@@ -121,13 +121,6 @@ export async function detectClaude() {
|
|
|
121
121
|
return { exists, version, configDir: claudeConfigDir(), agentsDir: claudeAgentsDir() };
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export async function detectHeadroom() {
|
|
125
|
-
return commandExists('headroom');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Alias for backward-compat during migration
|
|
129
|
-
export const detectRtk = detectHeadroom;
|
|
130
|
-
|
|
131
124
|
export async function detectSemble() {
|
|
132
125
|
if (commandExists('semble')) {
|
|
133
126
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "10.0
|
|
3
|
+
"version": "10.1.0",
|
|
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": {
|
package/scripts/check-deps.mjs
CHANGED
|
@@ -152,13 +152,6 @@ function readGhVersion() {
|
|
|
152
152
|
return m ? m[1] : raw.split(' ')[0];
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
function readHeadroomVersion() {
|
|
156
|
-
const raw = safeExec('headroom', ['--version']);
|
|
157
|
-
if (!raw) return null;
|
|
158
|
-
const m = raw.match(/(\d+\.\d+\.\d+)/);
|
|
159
|
-
return m ? m[1] : raw.split(' ')[0];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
155
|
function readSembleVersion() {
|
|
163
156
|
const raw = safeExec('semble', ['--version']);
|
|
164
157
|
if (!raw) return null;
|
|
@@ -187,7 +180,6 @@ function windowsInstallCmd(name) {
|
|
|
187
180
|
case 'pip': return 'python -m pip install --upgrade pip 2>nul || pip install --upgrade pip 2>nul';
|
|
188
181
|
case 'jq': return 'winget install jqlang.jq 2>nul || choco install jq -y 2>nul';
|
|
189
182
|
case 'gh': return 'winget install GitHub.cli 2>nul || choco install gh -y 2>nul';
|
|
190
|
-
case 'headroom':
|
|
191
183
|
case 'semble': return 'npm install -g ' + name + ' 2>nul';
|
|
192
184
|
default: return null;
|
|
193
185
|
}
|
|
@@ -204,7 +196,6 @@ function macInstallCmd(name) {
|
|
|
204
196
|
case 'pip': return 'python3 -m pip install --upgrade pip';
|
|
205
197
|
case 'jq': return 'brew install jq';
|
|
206
198
|
case 'gh': return 'brew install gh';
|
|
207
|
-
case 'headroom':
|
|
208
199
|
case 'semble': return 'npm install -g ' + name;
|
|
209
200
|
default: return null;
|
|
210
201
|
}
|
|
@@ -219,7 +210,6 @@ function linuxInstallCmd(name) {
|
|
|
219
210
|
case 'jq': return 'nix-shell -p jq';
|
|
220
211
|
case 'git': return 'nix-shell -p git';
|
|
221
212
|
case 'gh': return 'nix-shell -p gh';
|
|
222
|
-
case 'headroom':
|
|
223
213
|
case 'semble': return `nix-env -iA nixpkgs.${name}`;
|
|
224
214
|
default: return null;
|
|
225
215
|
}
|
|
@@ -264,7 +254,6 @@ function linuxInstallCmd(name) {
|
|
|
264
254
|
if (LINUX_DISTRO === 'alpine') return `${sudo}apk add --no-cache gh`;
|
|
265
255
|
if (LINUX_DISTRO === 'void') return `${sudo}xbps-install -S gh`;
|
|
266
256
|
return `${sudo}${pm} install -y gh`;
|
|
267
|
-
case 'headroom':
|
|
268
257
|
case 'semble':
|
|
269
258
|
return `npm install -g ${name}`;
|
|
270
259
|
default:
|
|
@@ -425,19 +414,6 @@ export async function checkDeps({ strict = false } = {}) {
|
|
|
425
414
|
}
|
|
426
415
|
}
|
|
427
416
|
|
|
428
|
-
// --- headroom ---
|
|
429
|
-
{
|
|
430
|
-
const current = readHeadroomVersion();
|
|
431
|
-
const entry = { name: 'headroom', status: 'missing', current, required: 'recommended' };
|
|
432
|
-
if (which('headroom')) {
|
|
433
|
-
entry.status = 'present';
|
|
434
|
-
present.push(entry);
|
|
435
|
-
} else {
|
|
436
|
-
entry.installCmd = installCmdFor('headroom');
|
|
437
|
-
missing.push(entry);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
417
|
// --- semble ---
|
|
442
418
|
{
|
|
443
419
|
const current = readSembleVersion();
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: headroom
|
|
3
|
-
description: Headroom — context compression layer for AI agents. Routes cline and other LLM clients through a local proxy that compresses tool outputs, logs, RAG chunks, and conversation history by 60–95%.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Headroom
|
|
7
|
-
|
|
8
|
-
Headroom is a context compression layer that sits between cline (or any LLM client) and the upstream provider. It intercepts tool call outputs, conversation history, and RAG chunks and compresses them before they reach the model — typically reducing token usage by 60–95% with no loss in answer quality.
|
|
9
|
-
|
|
10
|
-
## When to Use
|
|
11
|
-
|
|
12
|
-
- **Token budget is tight**: If a task is approaching the context window limit, Headroom can reclaim 60–90% of the tokens spent on tool outputs.
|
|
13
|
-
- **Long conversations**: The compression is cumulative — the longer the session, the more Headroom saves.
|
|
14
|
-
- **Verbose tool output**: RAG retrieval, file globbing, grep results, and diagnostic output are the highest-value compression targets.
|
|
15
|
-
- **Multi-step implementation**: Large features with many tool calls benefit most.
|
|
16
|
-
|
|
17
|
-
## Architecture
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
cline → headroom proxy (localhost:8787) → LLM provider
|
|
21
|
-
↓
|
|
22
|
-
compresses tool outputs, logs, RAG chunks
|
|
23
|
-
caches responses
|
|
24
|
-
measures savings
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Key Concepts
|
|
28
|
-
|
|
29
|
-
### Compression ratio
|
|
30
|
-
The fraction of tokens Headroom eliminates. A 0.85 compression ratio means 85% of the tokens were removed before reaching the provider.
|
|
31
|
-
|
|
32
|
-
### Wrapped vs unwrapped
|
|
33
|
-
`headroom wrap cline` modifies `~/.config/cline/cline.json` to route traffic through the proxy. `headroom unwrap cline` restores the original configuration.
|
|
34
|
-
|
|
35
|
-
### Proxy vs wrap
|
|
36
|
-
- **Proxy** (`headroom proxy`): The actual HTTP proxy server running on port 8787.
|
|
37
|
-
- **Wrap** (`headroom wrap cline`): The act of configuring cline to use the proxy.
|
|
38
|
-
|
|
39
|
-
You can run the proxy standalone (`headroom proxy`) and point any OpenAI-compatible client at it. The wrap command is just a convenience for cline.
|
|
40
|
-
|
|
41
|
-
## Dashboard Integration
|
|
42
|
-
|
|
43
|
-
The Bizar dashboard manages Headroom through Settings → Headroom:
|
|
44
|
-
|
|
45
|
-
- **Status widget**: Shows live proxy status, compression ratio, tokens saved
|
|
46
|
-
- **Controls**: Install, wrap/unwrap, start/stop proxy, open dashboard
|
|
47
|
-
- **Settings**: Auto-install, auto-start, auto-wrap, route-all-providers, port, host, backend, output shaper, telemetry, budget
|
|
48
|
-
|
|
49
|
-
API endpoints:
|
|
50
|
-
- `GET /api/headroom/status` — live status
|
|
51
|
-
- `GET /api/headroom/stats?hours=24` — compression statistics
|
|
52
|
-
- `POST /api/headroom/install` — install headroom
|
|
53
|
-
- `POST /api/headroom/wrap` — wrap cline
|
|
54
|
-
- `POST /api/headroom/unwrap` — unwrap cline
|
|
55
|
-
- `POST /api/headroom/proxy/start` — start proxy
|
|
56
|
-
- `POST /api/headroom/proxy/stop` — stop proxy
|
|
57
|
-
- `POST /api/headroom/auto-route` — configure all providers to route through the proxy
|
|
58
|
-
|
|
59
|
-
CLI:
|
|
60
|
-
```bash
|
|
61
|
-
bizar headroom status # live status
|
|
62
|
-
bizar headroom stats # compression stats
|
|
63
|
-
bizar headroom install # install headroom
|
|
64
|
-
bizar headroom wrap # wrap cline
|
|
65
|
-
bizar headroom unwrap # unwrap
|
|
66
|
-
bizar headroom start # start proxy
|
|
67
|
-
bizar headroom stop # stop proxy
|
|
68
|
-
bizar headroom doctor # health check
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Environment Variables
|
|
72
|
-
|
|
73
|
-
| Variable | Default | Description |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| `HEADROOM_HOST` | `127.0.0.1` | Proxy bind host |
|
|
76
|
-
| `HEADROOM_PORT` | `8787` | Proxy port |
|
|
77
|
-
| `HEADROOM_OUTPUT_SHAPER` | `0` | Set to `1` to enable output shaping |
|
|
78
|
-
| `HEADROOM_TELEMETRY` | (on) | Set to `off` to disable |
|
|
79
|
-
| `HEADROOM_BUDGET` | `0` | Monthly USD cap (0 = unlimited) |
|
|
80
|
-
| `HEADROOM_CONTEXT_TOOL` | `rtk` | Context tool: `rtk` or `lean-ctx` |
|
|
81
|
-
|
|
82
|
-
## Common Gotchas
|
|
83
|
-
|
|
84
|
-
1. **Proxy must be running before cline starts**: If the proxy is down when cline launches, cline will fail to make LLM calls. Start the proxy first, or enable `autoStart` in Headroom settings.
|
|
85
|
-
|
|
86
|
-
2. **`headroom wrap` edits cline.json**: The wrap command modifies `~/.config/cline/cline.json`. If you use version control for this file, you'll see a diff on every wrap. Use `headroom unwrap` before committing, or ignore the changes.
|
|
87
|
-
|
|
88
|
-
3. **Headroom 0.30.0 doesn't have `headroom plan --tokens`**: This command was removed. Use `headroom perf --hours 24` for actual token savings data.
|
|
89
|
-
|
|
90
|
-
4. **Token savings are cumulative**: The compression ratio compounds over a session. A 15-step implementation that saves 80% at each step uses roughly the same tokens as a 3-step implementation without Headroom.
|
|
91
|
-
|
|
92
|
-
5. **Cache hits**: Headroom caches semantically similar tool calls. Repeated operations (file reads, API probes) can be served from cache entirely, bypassing the LLM.
|
|
93
|
-
|
|
94
|
-
6. **Provider baseURL**: When `routeAllProviders` is enabled, the dashboard rewrites provider baseURLs to point at the proxy. If you configure a new provider manually, you may need to update its baseURL to go through the proxy.
|