@polderlabs/bizar 5.6.0-beta.7 → 5.6.0-beta.8
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/cli/agent-browser-update.mjs +322 -0
- package/cli/agent-browser-update.test.mjs +86 -0
- package/cli/bin.mjs +4 -2
- package/cli/commands/util.mjs +65 -1
- package/cli/provision.mjs +38 -0
- package/install.sh +40 -3
- package/package.json +1 -1
- package/packages/sdk/package.json +1 -1
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/agent-browser-update.mjs
|
|
3
|
+
*
|
|
4
|
+
* v6.0.0 — Install / update / verify the `agent-browser` CLI.
|
|
5
|
+
*
|
|
6
|
+
* `agent-browser` is a native Rust CLI from vercel-labs (~38K★) that
|
|
7
|
+
* gives Bizar agents a complete browser-automation surface:
|
|
8
|
+
* - 100+ typed CLI commands (open, snapshot, click, fill, screenshot, ...)
|
|
9
|
+
* - Native MCP stdio server (`agent-browser mcp`)
|
|
10
|
+
* - Self-healing snapshot-based element refs (`@e2`)
|
|
11
|
+
* - Plugin system (vault, recorder, ...)
|
|
12
|
+
* - Vercel AI SDK + AI Gateway integration
|
|
13
|
+
*
|
|
14
|
+
* This module is the single source of truth for installing and
|
|
15
|
+
* updating `agent-browser`. It is used by:
|
|
16
|
+
* - cli/install.mjs (during `bizar install`)
|
|
17
|
+
* - cli/provision.mjs (during `bizar update`)
|
|
18
|
+
* - cli/agent-browser-up.sh (the bash idempotent starter; on first
|
|
19
|
+
* run it shells to `npm install -g agent-browser` if the binary
|
|
20
|
+
* is missing — this module is the rich equivalent)
|
|
21
|
+
*
|
|
22
|
+
* Public API:
|
|
23
|
+
* detectState() probe what's installed without modifying
|
|
24
|
+
* install(opts) install + download Chrome for Testing
|
|
25
|
+
* update(opts) upgrade to the latest version
|
|
26
|
+
* ensureRunning() bring the daemon up if it's down
|
|
27
|
+
* printStatus() one-line status for the user
|
|
28
|
+
*
|
|
29
|
+
* All functions are idempotent and safe to re-run.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
33
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { homedir } from 'node:os';
|
|
36
|
+
import chalk from 'chalk';
|
|
37
|
+
|
|
38
|
+
const DEFAULT_DAEMON_PORT = 9223;
|
|
39
|
+
const DEFAULT_PROFILE_DIR = join(homedir(), '.agent-browser', 'profile');
|
|
40
|
+
|
|
41
|
+
// ── detection ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Probe the current agent-browser install without modifying anything.
|
|
45
|
+
*
|
|
46
|
+
* Returns a structured state object:
|
|
47
|
+
* {
|
|
48
|
+
* installed: boolean,
|
|
49
|
+
* version: string | null,
|
|
50
|
+
* chromeReady: boolean,
|
|
51
|
+
* daemonRunning: boolean,
|
|
52
|
+
* profileDir: string,
|
|
53
|
+
* daemonPort: number,
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
export function detectState() {
|
|
57
|
+
const state = {
|
|
58
|
+
installed: false,
|
|
59
|
+
version: null,
|
|
60
|
+
chromeReady: false,
|
|
61
|
+
daemonRunning: false,
|
|
62
|
+
profileDir: DEFAULT_PROFILE_DIR,
|
|
63
|
+
daemonPort: DEFAULT_DAEMON_PORT,
|
|
64
|
+
bin: null,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// 1. Find the binary
|
|
68
|
+
const bin = findAgentBrowserBin();
|
|
69
|
+
state.bin = bin;
|
|
70
|
+
if (!bin) return state;
|
|
71
|
+
|
|
72
|
+
// 2. Get the version
|
|
73
|
+
try {
|
|
74
|
+
const out = execSync(`"${bin}" --version`, { encoding: 'utf8', timeout: 5_000 }).trim();
|
|
75
|
+
state.version = out;
|
|
76
|
+
state.installed = true;
|
|
77
|
+
} catch {
|
|
78
|
+
return state;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 3. Check Chrome for Testing
|
|
82
|
+
const chromeDir = join(homedir(), '.cache', 'agent-browser', 'chrome');
|
|
83
|
+
if (existsSync(chromeDir)) state.chromeReady = true;
|
|
84
|
+
|
|
85
|
+
// 4. Check daemon (read env on every call so tests can override)
|
|
86
|
+
const port = parseInt(process.env.AGENT_BROWSER_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
87
|
+
state.daemonPort = port;
|
|
88
|
+
state.daemonRunning = isDaemonUp(port);
|
|
89
|
+
|
|
90
|
+
return state;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function findAgentBrowserBin() {
|
|
94
|
+
// 1. Explicit override
|
|
95
|
+
if (process.env.AGENT_BROWSER_BIN && existsSync(process.env.AGENT_BROWSER_BIN)) {
|
|
96
|
+
return process.env.AGENT_BROWSER_BIN;
|
|
97
|
+
}
|
|
98
|
+
// 2. Well-known npm-global locations
|
|
99
|
+
const candidates = [
|
|
100
|
+
'/usr/local/bin/agent-browser',
|
|
101
|
+
'/opt/homebrew/bin/agent-browser',
|
|
102
|
+
join(homedir(), '.local', 'bin', 'agent-browser'),
|
|
103
|
+
join(homedir(), '.npm', 'bin', 'agent-browser'),
|
|
104
|
+
];
|
|
105
|
+
for (const c of candidates) {
|
|
106
|
+
if (existsSync(c)) return c;
|
|
107
|
+
}
|
|
108
|
+
// 3. PATH lookup
|
|
109
|
+
try {
|
|
110
|
+
const r = execSync('command -v agent-browser', { encoding: 'utf8', timeout: 2_000 }).trim();
|
|
111
|
+
if (r && existsSync(r)) return r;
|
|
112
|
+
} catch {
|
|
113
|
+
// fall through
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isDaemonUp(port) {
|
|
119
|
+
try {
|
|
120
|
+
execSync(
|
|
121
|
+
`curl -fsS --max-time 1 "http://127.0.0.1:${port}/json/version" >/dev/null 2>&1`,
|
|
122
|
+
{ shell: '/bin/bash', timeout: 2_000 },
|
|
123
|
+
);
|
|
124
|
+
return true;
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── install / update ───────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Install agent-browser. Idempotent: skips steps that are already
|
|
134
|
+
* complete. Returns the updated state.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} opts
|
|
137
|
+
* @param {boolean} [opts.silent=false] suppress info output (for CI)
|
|
138
|
+
* @param {boolean} [opts.dryRun=false] print actions, make no changes
|
|
139
|
+
* @param {string} [opts.channel='latest'] npm dist-tag (e.g. 'beta')
|
|
140
|
+
* @param {boolean} [opts.startDaemon=true] start the daemon after install
|
|
141
|
+
*/
|
|
142
|
+
export function install(opts = {}) {
|
|
143
|
+
const { silent = false, dryRun = false, channel = 'latest', startDaemon = true } = opts;
|
|
144
|
+
const log = silent ? () => {} : (msg) => console.log(chalk.cyan(' → ') + msg);
|
|
145
|
+
|
|
146
|
+
const before = detectState();
|
|
147
|
+
if (before.installed) {
|
|
148
|
+
log(`agent-browser ${before.version} already installed at ${before.bin}`);
|
|
149
|
+
if (startDaemon && !before.daemonRunning) {
|
|
150
|
+
log('daemon is down — bringing it up');
|
|
151
|
+
ensureRunning({ silent, dryRun });
|
|
152
|
+
}
|
|
153
|
+
return detectState();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
log(`Installing agent-browser from npm (channel: ${channel})...`);
|
|
157
|
+
if (dryRun) {
|
|
158
|
+
log(`[DRY RUN] would run: npm install -g agent-browser@${channel}`);
|
|
159
|
+
// In dry-run, the binary may not be on PATH. Just return the
|
|
160
|
+
// current state (which includes installed=false) without checking
|
|
161
|
+
// for the after-install state.
|
|
162
|
+
return detectState();
|
|
163
|
+
} else {
|
|
164
|
+
const r = spawnSync('npm', ['install', '-g', `agent-browser@${channel}`], {
|
|
165
|
+
stdio: silent ? 'ignore' : 'inherit',
|
|
166
|
+
timeout: 180_000,
|
|
167
|
+
});
|
|
168
|
+
if (r.status !== 0) {
|
|
169
|
+
throw new Error(`npm install -g agent-browser@${channel} failed (exit ${r.status})`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const after = detectState();
|
|
174
|
+
if (!after.installed) {
|
|
175
|
+
throw new Error('agent-browser still not on PATH after install');
|
|
176
|
+
}
|
|
177
|
+
log(`agent-browser ${after.version} installed`);
|
|
178
|
+
|
|
179
|
+
log('Downloading Chrome for Testing...');
|
|
180
|
+
if (!dryRun) {
|
|
181
|
+
const r = spawnSync(after.bin, ['install'], {
|
|
182
|
+
stdio: silent ? 'ignore' : 'inherit',
|
|
183
|
+
timeout: 300_000,
|
|
184
|
+
});
|
|
185
|
+
if (r.status !== 0) {
|
|
186
|
+
log(chalk.yellow('Chrome download failed — you can retry with `agent-browser install`'));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (startDaemon) {
|
|
191
|
+
ensureRunning({ silent, dryRun });
|
|
192
|
+
}
|
|
193
|
+
return detectState();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Update agent-browser to the latest version. Idempotent.
|
|
198
|
+
*
|
|
199
|
+
* Same shape as install() but uses `agent-browser upgrade` after the
|
|
200
|
+
* first install (which knows how to detect the install method and run
|
|
201
|
+
* the right update command).
|
|
202
|
+
*/
|
|
203
|
+
export function update(opts = {}) {
|
|
204
|
+
const { silent = false, dryRun = false, channel = 'latest', startDaemon = true } = opts;
|
|
205
|
+
const log = silent ? () => {} : (msg) => console.log(chalk.cyan(' → ') + msg);
|
|
206
|
+
|
|
207
|
+
const before = detectState();
|
|
208
|
+
if (!before.installed) {
|
|
209
|
+
log('agent-browser not installed — calling install()');
|
|
210
|
+
return install(opts);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
log(`agent-browser ${before.version} is installed. Upgrading to ${channel}...`);
|
|
214
|
+
if (dryRun) {
|
|
215
|
+
log(`[DRY RUN] would run: agent-browser upgrade`);
|
|
216
|
+
return detectState();
|
|
217
|
+
} else {
|
|
218
|
+
// First, try `agent-browser upgrade` (the self-updater). If it
|
|
219
|
+
// doesn't exist (older version), fall back to `npm update -g`.
|
|
220
|
+
const r = spawnSync(before.bin, ['upgrade'], {
|
|
221
|
+
stdio: silent ? 'ignore' : 'inherit',
|
|
222
|
+
timeout: 180_000,
|
|
223
|
+
});
|
|
224
|
+
if (r.status !== 0) {
|
|
225
|
+
log('`agent-browser upgrade` failed — falling back to `npm update -g`');
|
|
226
|
+
const r2 = spawnSync('npm', ['update', '-g', `agent-browser@${channel}`], {
|
|
227
|
+
stdio: silent ? 'ignore' : 'inherit',
|
|
228
|
+
timeout: 180_000,
|
|
229
|
+
});
|
|
230
|
+
if (r2.status !== 0) {
|
|
231
|
+
throw new Error(`npm update -g agent-browser@${channel} failed (exit ${r2.status})`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const after = detectState();
|
|
237
|
+
if (after.version === before.version) {
|
|
238
|
+
log(`agent-browser already at latest (${after.version})`);
|
|
239
|
+
} else {
|
|
240
|
+
log(`agent-browser updated: ${before.version} → ${after.version}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (startDaemon && !after.daemonRunning) {
|
|
244
|
+
ensureRunning({ silent, dryRun });
|
|
245
|
+
}
|
|
246
|
+
return after;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ── daemon management ──────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Bring the agent-browser daemon up if it's down. Idempotent.
|
|
253
|
+
*
|
|
254
|
+
* On macOS, Linux, and Windows, the daemon is started as a background
|
|
255
|
+
* process using `agent-browser serve`. On success, the daemon listens
|
|
256
|
+
* on `http://127.0.0.1:<port>` and is reachable via the typed CLI
|
|
257
|
+
* and the MCP stdio server.
|
|
258
|
+
*/
|
|
259
|
+
export function ensureRunning(opts = {}) {
|
|
260
|
+
const { silent = false, dryRun = false } = opts;
|
|
261
|
+
const log = silent ? () => {} : (msg) => console.log(chalk.cyan(' → ') + msg);
|
|
262
|
+
|
|
263
|
+
const state = detectState();
|
|
264
|
+
if (!state.installed) {
|
|
265
|
+
log('agent-browser is not installed — call install() first');
|
|
266
|
+
return state;
|
|
267
|
+
}
|
|
268
|
+
if (state.daemonRunning) {
|
|
269
|
+
log(`daemon is up (port ${state.daemonPort})`);
|
|
270
|
+
return state;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
log(`starting daemon on port ${state.daemonPort}...`);
|
|
274
|
+
if (dryRun) {
|
|
275
|
+
log(`[DRY RUN] would run: ${state.bin} serve --port ${state.daemonPort} --headless`);
|
|
276
|
+
} else {
|
|
277
|
+
const r = spawnSync('nohup', [
|
|
278
|
+
state.bin, 'serve',
|
|
279
|
+
'--port', `${state.daemonPort}`,
|
|
280
|
+
'--profile', state.profileDir,
|
|
281
|
+
'--headless',
|
|
282
|
+
], {
|
|
283
|
+
detached: true,
|
|
284
|
+
stdio: 'ignore',
|
|
285
|
+
timeout: 5_000,
|
|
286
|
+
});
|
|
287
|
+
if (r.error) {
|
|
288
|
+
log(chalk.yellow(`daemon spawn failed: ${r.error.message}`));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Wait up to 5s for the daemon to bind
|
|
293
|
+
for (let i = 0; i < 50; i++) {
|
|
294
|
+
if (isDaemonUp(state.daemonPort)) {
|
|
295
|
+
log(`daemon is up (port ${state.daemonPort})`);
|
|
296
|
+
return detectState();
|
|
297
|
+
}
|
|
298
|
+
spawnSync('sleep', ['0.1']);
|
|
299
|
+
}
|
|
300
|
+
log(chalk.yellow('daemon did not bind within 5s — check `agent-browser doctor`'));
|
|
301
|
+
return detectState();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ── status ─────────────────────────────────────────────────────────────
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* One-line status for the user. Returns the state object.
|
|
308
|
+
*/
|
|
309
|
+
export function printStatus() {
|
|
310
|
+
const s = detectState();
|
|
311
|
+
if (!s.installed) {
|
|
312
|
+
console.log(chalk.yellow(' agent-browser: NOT INSTALLED'));
|
|
313
|
+
console.log(chalk.dim(' Install with: npm install -g agent-browser && agent-browser install'));
|
|
314
|
+
} else {
|
|
315
|
+
const daemonTxt = s.daemonRunning
|
|
316
|
+
? chalk.green('running')
|
|
317
|
+
: chalk.yellow('stopped');
|
|
318
|
+
const chromeTxt = s.chromeReady ? chalk.green('ready') : chalk.yellow('not installed');
|
|
319
|
+
console.log(` agent-browser ${chalk.cyan(s.version)} · daemon: ${daemonTxt} (port ${s.daemonPort}) · chrome: ${chromeTxt}`);
|
|
320
|
+
}
|
|
321
|
+
return s;
|
|
322
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/agent-browser-update.test.mjs
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for the v6.0.0 agent-browser install/update module.
|
|
5
|
+
* Uses node:test (no `expect` — uses `assert` instead).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
detectState,
|
|
13
|
+
printStatus,
|
|
14
|
+
install,
|
|
15
|
+
update,
|
|
16
|
+
ensureRunning,
|
|
17
|
+
} = await import("./agent-browser-update.mjs");
|
|
18
|
+
|
|
19
|
+
describe("agent-browser-update", () => {
|
|
20
|
+
it("exports the public API", () => {
|
|
21
|
+
assert.equal(typeof detectState, "function");
|
|
22
|
+
assert.equal(typeof printStatus, "function");
|
|
23
|
+
assert.equal(typeof install, "function");
|
|
24
|
+
assert.equal(typeof update, "function");
|
|
25
|
+
assert.equal(typeof ensureRunning, "function");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("detectState returns a structured state object", () => {
|
|
29
|
+
const s = detectState();
|
|
30
|
+
assert.ok("installed" in s);
|
|
31
|
+
assert.ok("version" in s);
|
|
32
|
+
assert.ok("chromeReady" in s);
|
|
33
|
+
assert.ok("daemonRunning" in s);
|
|
34
|
+
assert.ok("profileDir" in s);
|
|
35
|
+
assert.ok("daemonPort" in s);
|
|
36
|
+
assert.ok("bin" in s);
|
|
37
|
+
assert.equal(typeof s.installed, "boolean");
|
|
38
|
+
if (s.installed) {
|
|
39
|
+
assert.equal(typeof s.version, "string");
|
|
40
|
+
} else {
|
|
41
|
+
assert.equal(s.version, null);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("detectState profileDir points to ~/.agent-browser/profile by default", () => {
|
|
46
|
+
const s = detectState();
|
|
47
|
+
assert.match(s.profileDir, /\.agent-browser\/profile$/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("detectState default daemon port is 9223", () => {
|
|
51
|
+
delete process.env.AGENT_BROWSER_PORT;
|
|
52
|
+
const s = detectState();
|
|
53
|
+
assert.equal(s.daemonPort, 9223);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("printStatus does not throw (regardless of install state)", () => {
|
|
57
|
+
const origLog = console.log;
|
|
58
|
+
const captured = [];
|
|
59
|
+
console.log = (...args) => captured.push(args.join(" "));
|
|
60
|
+
try {
|
|
61
|
+
printStatus();
|
|
62
|
+
} finally {
|
|
63
|
+
console.log = origLog;
|
|
64
|
+
}
|
|
65
|
+
assert.ok(captured.length > 0);
|
|
66
|
+
assert.match(captured[0], /agent-browser/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("install with dryRun=true returns a state object", () => {
|
|
70
|
+
const s = install({ dryRun: true, silent: true });
|
|
71
|
+
assert.ok("installed" in s);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("update with dryRun=true returns a state object", () => {
|
|
75
|
+
const s = update({ dryRun: true, silent: true });
|
|
76
|
+
assert.ok("installed" in s);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("ensureRunning with dryRun=true returns a state object", () => {
|
|
80
|
+
const s = ensureRunning({ dryRun: true });
|
|
81
|
+
assert.ok("installed" in s);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// NOTE: AGENT_BROWSER_PORT env override is tested manually (node --test
|
|
85
|
+
// spawns a child process that doesn't inherit the parent's env).
|
|
86
|
+
});
|
package/cli/bin.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Commands:
|
|
14
14
|
* install, audit, init, export, artifact, update, test-gate, service, dash,
|
|
15
15
|
* memory, headroom, minimax, usage, mod, doctor, repair, dev-link, dev-unlink,
|
|
16
|
-
* heads-up, bg, agent-browser-up, providers, deploy, plugin, marketplace
|
|
16
|
+
* heads-up, bg, agent-browser, agent-browser-up, providers, deploy, plugin, marketplace
|
|
17
17
|
*/
|
|
18
18
|
import chalk from 'chalk';
|
|
19
19
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -115,7 +115,8 @@ function showHelp() {
|
|
|
115
115
|
deploy One-click deploy to Vercel, Cloudflare, Fly.io, or Docker
|
|
116
116
|
plugin <subcommand> Manage marketplace plugins (search/install/config/invoke)
|
|
117
117
|
marketplace <subcommand> Browse and install plugins from the public marketplace
|
|
118
|
-
agent-browser
|
|
118
|
+
agent-browser Install / update / verify the agent-browser CLI
|
|
119
|
+
agent-browser-up Start Chromium for agent-browser (start/stop/status)
|
|
119
120
|
providers detect Auto-detect provider API keys from env + cline.json
|
|
120
121
|
clip <subcommand> Manage web clipper saved clips (list/delete/configure)
|
|
121
122
|
ocr <subcommand> OCR operations on images (list/process/configure)
|
|
@@ -429,6 +430,7 @@ async function main() {
|
|
|
429
430
|
case 'heads-up':
|
|
430
431
|
case 'bg':
|
|
431
432
|
case 'digest':
|
|
433
|
+
case 'agent-browser':
|
|
432
434
|
case 'agent-browser-up':
|
|
433
435
|
case 'providers':
|
|
434
436
|
case 'plan': {
|
package/cli/commands/util.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Miscellaneous utility commands:
|
|
5
5
|
* audit, init, export, test-gate, dev-link, dev-unlink,
|
|
6
|
-
* doctor, repair, heads-up, bg, agent-browser-up, providers detect,
|
|
6
|
+
* doctor, repair, heads-up, bg, agent-browser, agent-browser-up, providers detect,
|
|
7
7
|
* backup, restore
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
@@ -441,6 +441,70 @@ export async function run(name, args, isHelpRequest) {
|
|
|
441
441
|
break;
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
case 'agent-browser': {
|
|
445
|
+
// v6.0.0 — install / update / verify the agent-browser CLI.
|
|
446
|
+
// (The 'agent-browser-up' sibling is the bash wrapper that just
|
|
447
|
+
// manages the daemon process; this is the rich installer + updater.)
|
|
448
|
+
const { install, update, detectState, ensureRunning, printStatus } =
|
|
449
|
+
await import('../agent-browser-update.mjs');
|
|
450
|
+
const sub = args[0] || 'status';
|
|
451
|
+
switch (sub) {
|
|
452
|
+
case 'status':
|
|
453
|
+
printStatus();
|
|
454
|
+
break;
|
|
455
|
+
case 'install': {
|
|
456
|
+
const s = install({ silent: false });
|
|
457
|
+
console.log(chalk.green('\n agent-browser ready.'));
|
|
458
|
+
console.log(` version: ${s.version}`);
|
|
459
|
+
console.log(` daemon: ${s.daemonRunning ? 'running' : 'stopped'}`);
|
|
460
|
+
console.log(` profile: ${s.profileDir}`);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case 'update': {
|
|
464
|
+
const s = update({ silent: false });
|
|
465
|
+
console.log(chalk.green('\n agent-browser up-to-date.'));
|
|
466
|
+
console.log(` version: ${s.version}`);
|
|
467
|
+
console.log(` daemon: ${s.daemonRunning ? 'running' : 'stopped'}`);
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
case 'detect': {
|
|
471
|
+
const s = detectState();
|
|
472
|
+
console.log(JSON.stringify(s, null, 2));
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case 'start':
|
|
476
|
+
ensureRunning({ silent: false });
|
|
477
|
+
break;
|
|
478
|
+
case 'stop': {
|
|
479
|
+
const { spawnSync } = await import('node:child_process');
|
|
480
|
+
const killed = spawnSync('pkill', ['-f', 'agent-browser serve'], { stdio: 'ignore' });
|
|
481
|
+
if (killed.status === 0) {
|
|
482
|
+
console.log(chalk.green(' ✓ daemon stopped'));
|
|
483
|
+
} else {
|
|
484
|
+
console.log(chalk.dim(' daemon was not running'));
|
|
485
|
+
}
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
default:
|
|
489
|
+
console.log(`bizar agent-browser <sub>
|
|
490
|
+
|
|
491
|
+
Subcommands:
|
|
492
|
+
status one-line status (default)
|
|
493
|
+
install install agent-browser + download Chrome
|
|
494
|
+
update upgrade to the latest version
|
|
495
|
+
detect JSON state (for scripts)
|
|
496
|
+
start start the daemon
|
|
497
|
+
stop stop the daemon
|
|
498
|
+
|
|
499
|
+
Examples:
|
|
500
|
+
bizar agent-browser status
|
|
501
|
+
bizar agent-browser install
|
|
502
|
+
bizar agent-browser update
|
|
503
|
+
bizar agent-browser start`);
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
|
|
444
508
|
case 'providers':
|
|
445
509
|
if (args[0] === 'detect') {
|
|
446
510
|
const { runProvidersDetect } = await import('../providers-detect.mjs');
|
package/cli/provision.mjs
CHANGED
|
@@ -835,6 +835,44 @@ export async function installLightragProvision({ dryRun = false } = {}) {
|
|
|
835
835
|
}
|
|
836
836
|
}
|
|
837
837
|
|
|
838
|
+
/**
|
|
839
|
+
* v6.0.0 — Ensure `agent-browser` (native Rust CLI from vercel-labs) is
|
|
840
|
+
* installed and the daemon is up. agent-browser replaces the v5.x
|
|
841
|
+
* `browser-harness` (Python CDP wrapper). The CLI is installed via
|
|
842
|
+
* `npm install -g agent-browser`; Chrome for Testing is downloaded by
|
|
843
|
+
* `agent-browser install`; the daemon is started by
|
|
844
|
+
* `agent-browser serve --headless --port <port> --profile <dir>`.
|
|
845
|
+
*
|
|
846
|
+
* Idempotent. Returns { ok, message, version?, daemonRunning? }.
|
|
847
|
+
*/
|
|
848
|
+
export async function ensureAgentBrowser({ dryRun = false, mode = 'install' } = {}) {
|
|
849
|
+
const ab = await import('./agent-browser-update.mjs');
|
|
850
|
+
const before = ab.detectState();
|
|
851
|
+
if (dryRun) {
|
|
852
|
+
return {
|
|
853
|
+
ok: true,
|
|
854
|
+
message: `[dry-run] agent-browser: ${before.installed ? 'installed' : 'not installed'}` +
|
|
855
|
+
(before.installed ? ` (v${before.version})` : ''),
|
|
856
|
+
version: before.version,
|
|
857
|
+
daemonRunning: before.daemonRunning,
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
const after = mode === 'update' && before.installed
|
|
862
|
+
? ab.update({ silent: true, startDaemon: true })
|
|
863
|
+
: ab.install({ silent: true, startDaemon: true });
|
|
864
|
+
return {
|
|
865
|
+
ok: true,
|
|
866
|
+
message: `agent-browser: ${after.installed ? `v${after.version}` : 'install attempted'}` +
|
|
867
|
+
` · daemon: ${after.daemonRunning ? 'running' : 'stopped'}`,
|
|
868
|
+
version: after.version,
|
|
869
|
+
daemonRunning: after.daemonRunning,
|
|
870
|
+
};
|
|
871
|
+
} catch (err) {
|
|
872
|
+
return { ok: false, message: `agent-browser install failed: ${err.message}` };
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
838
876
|
/**
|
|
839
877
|
* v4.4.11 — The mods step. By default, NEVER install or upgrade mods
|
|
840
878
|
* during `bizar install` or `bizar update`. The step just reports the
|
package/install.sh
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
# package manager:
|
|
15
15
|
#
|
|
16
16
|
# - Linux: ensure node is installed (apt/dnf/pacman/zypper), install uv,
|
|
17
|
-
# python3.12, jq, gh, agent-browser, Chrome
|
|
18
|
-
# - macOS: ensure homebrew is installed; everything else is via brew.
|
|
17
|
+
# python3.12, jq, gh, agent-browser (npm), Chrome for Testing.
|
|
18
|
+
# - macOS: ensure homebrew is installed; everything else is via brew/npm.
|
|
19
19
|
# - Windows: a stub that prints "use install.ps1".
|
|
20
20
|
#
|
|
21
21
|
# Agent files / plugin copy / cline.json patching / service registration /
|
|
@@ -189,6 +189,41 @@ check_deps() {
|
|
|
189
189
|
fi
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
install_agent_browser() {
|
|
193
|
+
# v6.0.0 — agent-browser (native Rust CLI from vercel-labs). Replaces
|
|
194
|
+
# the v5.x browser-harness (Python CDP wrapper). Installs via npm.
|
|
195
|
+
#
|
|
196
|
+
# If npm is not available, this is a soft-fail: agent-browser is
|
|
197
|
+
# optional, and `cli/provision.mjs:ensureAgentBrowser` will retry it
|
|
198
|
+
# once npm is available.
|
|
199
|
+
if have_cmd agent-browser; then
|
|
200
|
+
local ab_ver
|
|
201
|
+
ab_ver="$(agent-browser --version 2>/dev/null || echo 'unknown')"
|
|
202
|
+
note "agent-browser ${ab_ver} already installed"
|
|
203
|
+
return 0
|
|
204
|
+
fi
|
|
205
|
+
action "Installing agent-browser via npm..."
|
|
206
|
+
if ! have_cmd npm; then
|
|
207
|
+
warn "npm not available — skipping agent-browser install"
|
|
208
|
+
warn " Install later: npm install -g agent-browser"
|
|
209
|
+
return 0
|
|
210
|
+
fi
|
|
211
|
+
if dry npm install -g agent-browser 2>&1; then
|
|
212
|
+
local ab_ver
|
|
213
|
+
ab_ver="$(agent-browser --version 2>/dev/null || echo 'unknown')"
|
|
214
|
+
note "agent-browser ${ab_ver} installed"
|
|
215
|
+
# Download Chrome for Testing
|
|
216
|
+
if dry agent-browser install 2>&1; then
|
|
217
|
+
note "Chrome for Testing downloaded"
|
|
218
|
+
else
|
|
219
|
+
warn "Chrome download failed — retry later: agent-browser install"
|
|
220
|
+
fi
|
|
221
|
+
else
|
|
222
|
+
warn "agent-browser install failed — the browser tools will not be available"
|
|
223
|
+
warn " Install later: npm install -g agent-browser"
|
|
224
|
+
fi
|
|
225
|
+
}
|
|
226
|
+
|
|
192
227
|
install_lightrag() {
|
|
193
228
|
# LightRAG is optional but recommended. Install via uv tool.
|
|
194
229
|
# uv tools install to ~/.local/bin/ — ensure that's on PATH.
|
|
@@ -359,6 +394,7 @@ install_linux() {
|
|
|
359
394
|
ensure_node
|
|
360
395
|
check_deps
|
|
361
396
|
install_missing_deps_linux
|
|
397
|
+
install_agent_browser
|
|
362
398
|
install_service
|
|
363
399
|
}
|
|
364
400
|
|
|
@@ -366,6 +402,7 @@ install_macos() {
|
|
|
366
402
|
note "Detected macOS ($(uname -m))"
|
|
367
403
|
check_deps
|
|
368
404
|
install_missing_deps_macos
|
|
405
|
+
install_agent_browser
|
|
369
406
|
install_service
|
|
370
407
|
}
|
|
371
408
|
|
|
@@ -380,7 +417,7 @@ main() {
|
|
|
380
417
|
fi
|
|
381
418
|
|
|
382
419
|
echo ""
|
|
383
|
-
echo -e "${BOLD}${CYAN} ⚡ BizarHarness Installer
|
|
420
|
+
echo -e "${BOLD}${CYAN} ⚡ BizarHarness Installer v6.0.0${NC}"
|
|
384
421
|
if [ "$UPDATE_MODE" -eq 1 ]; then
|
|
385
422
|
echo -e " ${DIM}Update mode${NC}"
|
|
386
423
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "5.6.0-beta.
|
|
3
|
+
"version": "5.6.0-beta.8",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for cline — 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, cline plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|