@beeos-ai/cli 1.0.15 → 1.0.17
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/dist/index.js +475 -56
- package/package.json +1 -1
- package/scripts/_existing_install_actions.json +33 -4
- package/scripts/install.Tests.ps1 +33 -18
- package/scripts/install.ps1 +27 -57
- package/scripts/install.sh +36 -63
package/dist/index.js
CHANGED
|
@@ -1206,12 +1206,25 @@ function agentBinCommandAndArgs(bin) {
|
|
|
1206
1206
|
return { cmd: process.execPath, args: [bin.script] };
|
|
1207
1207
|
}
|
|
1208
1208
|
async function ensureDeviceAgent(progress) {
|
|
1209
|
-
const
|
|
1210
|
-
if (
|
|
1211
|
-
describeFound(
|
|
1212
|
-
return
|
|
1209
|
+
const found = await findExistingTs();
|
|
1210
|
+
if (found) {
|
|
1211
|
+
describeFound(found, progress);
|
|
1212
|
+
return found;
|
|
1213
1213
|
}
|
|
1214
|
-
|
|
1214
|
+
progress.onStatus("device-agent not found \u2014 installing @beeos-ai/device-agent + @beeos-ai/device-mcp-server (one-time, ~20MB)...");
|
|
1215
|
+
const outcome = await upgradeBeeosSuite({
|
|
1216
|
+
packages: [NPM_PKGS.DEVICE_AGENT, NPM_PKGS.DEVICE_MCP_SERVER],
|
|
1217
|
+
progress
|
|
1218
|
+
});
|
|
1219
|
+
if (outcome.anyFailed) {
|
|
1220
|
+
throw new Error(autoInstallFailedHint(outcome));
|
|
1221
|
+
}
|
|
1222
|
+
const after = await findExistingTs();
|
|
1223
|
+
if (!after) {
|
|
1224
|
+
throw new Error(tsNotFoundHint());
|
|
1225
|
+
}
|
|
1226
|
+
describeFound(after, progress);
|
|
1227
|
+
return after;
|
|
1215
1228
|
}
|
|
1216
1229
|
function describeFound(bin, progress) {
|
|
1217
1230
|
if (bin.type === "executable") {
|
|
@@ -1286,7 +1299,11 @@ async function pathDeviceAgent() {
|
|
|
1286
1299
|
return null;
|
|
1287
1300
|
}
|
|
1288
1301
|
function tsNotFoundHint() {
|
|
1289
|
-
return "device-agent (TS) not found.\n\nPick one:\n \u2022 dev (recommended): cd agents/device-agent && pnpm install && pnpm -r build\n \u2022 global:
|
|
1302
|
+
return "device-agent (TS) not found after auto-install.\n\nThis usually means npm install -g succeeded but the binary is on a\ndifferent PATH than this shell sees (common with nvm/fnm).\n\nPick one:\n \u2022 dev (recommended): cd agents/device-agent && pnpm install && pnpm -r build\n \u2022 global re-install: npm i -g @beeos-ai/device-agent @beeos-ai/device-mcp-server\n \u2022 explicit override: export BEEOS_DEVICE_AGENT_BIN=/abs/path/to/device-agent[.js]";
|
|
1303
|
+
}
|
|
1304
|
+
function autoInstallFailedHint(outcome) {
|
|
1305
|
+
const failed = outcome.packages.filter((p) => p.failed).map((p) => ` \u2022 ${p.pkg}: ${p.failed}`).join("\n");
|
|
1306
|
+
return "Auto-install of the device-agent suite failed.\n\nFailed packages:\n" + failed + "\n\nManual fix (run, then retry `beeos device attach`):\n npm i -g @beeos-ai/device-agent @beeos-ai/device-mcp-server\n\nPin a specific version by exporting before retry:\n export BEEOS_DEVICE_AGENT_VERSION=<semver>\n export BEEOS_MCP_SERVER_VERSION=<semver>";
|
|
1290
1307
|
}
|
|
1291
1308
|
var init_device_setup = __esm({
|
|
1292
1309
|
"../core/dist/device-setup.js"() {
|
|
@@ -1529,22 +1546,36 @@ var init_device = __esm({
|
|
|
1529
1546
|
const lines = result.stdout.split("\n").slice(1);
|
|
1530
1547
|
for (const line of lines) {
|
|
1531
1548
|
const parts = line.trim().split(/\s+/);
|
|
1532
|
-
if (parts.length
|
|
1533
|
-
|
|
1534
|
-
|
|
1549
|
+
if (parts.length < 2)
|
|
1550
|
+
continue;
|
|
1551
|
+
const serial = parts[0];
|
|
1552
|
+
const rawStatus = parts[1];
|
|
1553
|
+
let status2;
|
|
1554
|
+
if (rawStatus === "device")
|
|
1555
|
+
status2 = "device";
|
|
1556
|
+
else if (rawStatus === "unauthorized")
|
|
1557
|
+
status2 = "unauthorized";
|
|
1558
|
+
else
|
|
1559
|
+
status2 = "offline";
|
|
1560
|
+
devices.push({ serial, status: status2, rawStatus });
|
|
1535
1561
|
}
|
|
1536
1562
|
return devices;
|
|
1537
1563
|
},
|
|
1538
1564
|
async detectSingleDevice() {
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1565
|
+
const all = await this.listAdbDevices();
|
|
1566
|
+
const ready = all.filter((d) => d.status === "device");
|
|
1567
|
+
if (ready.length === 0) {
|
|
1568
|
+
if (all.length > 0) {
|
|
1569
|
+
const issues = all.map((d) => `${d.serial}=${d.rawStatus}`).join(", ");
|
|
1570
|
+
throw new Error(`No ADB devices ready (only: ${issues}). Accept the USB prompt on the phone or check the cable.`);
|
|
1571
|
+
}
|
|
1541
1572
|
throw new Error("No ADB devices found. Connect a device and try again.");
|
|
1542
1573
|
}
|
|
1543
|
-
if (
|
|
1544
|
-
const serials =
|
|
1574
|
+
if (ready.length > 1) {
|
|
1575
|
+
const serials = ready.map((d) => d.serial).join(", ");
|
|
1545
1576
|
throw new Error(`Multiple ADB devices found: ${serials}. Use --serial to specify one.`);
|
|
1546
1577
|
}
|
|
1547
|
-
return
|
|
1578
|
+
return ready[0].serial;
|
|
1548
1579
|
},
|
|
1549
1580
|
async spawnForDevice(serial, agentBin, bridgeUrl, httpEnabled, httpPort, agentGatewayUrl) {
|
|
1550
1581
|
const p = getPlatformAdapter();
|
|
@@ -3330,13 +3361,13 @@ function inferInitChoices(state) {
|
|
|
3330
3361
|
if (state.hasIdentity && state.binding) {
|
|
3331
3362
|
return {
|
|
3332
3363
|
defaultDecision: "upgrade",
|
|
3333
|
-
options: ["upgrade", "rebind-
|
|
3364
|
+
options: ["upgrade", "rebind", "reset-all", "skip"]
|
|
3334
3365
|
};
|
|
3335
3366
|
}
|
|
3336
3367
|
if (state.hasIdentity) {
|
|
3337
3368
|
return {
|
|
3338
3369
|
defaultDecision: "upgrade",
|
|
3339
|
-
options: ["upgrade", "
|
|
3370
|
+
options: ["upgrade", "reset-all", "skip"]
|
|
3340
3371
|
};
|
|
3341
3372
|
}
|
|
3342
3373
|
return { defaultDecision: "fresh", options: ["fresh", "skip"] };
|
|
@@ -3347,8 +3378,10 @@ function initDecisionLabel(decision) {
|
|
|
3347
3378
|
return "Install + bind";
|
|
3348
3379
|
case "upgrade":
|
|
3349
3380
|
return "Upgrade CLI & agents (keep binding & key)";
|
|
3350
|
-
case "rebind
|
|
3351
|
-
return "Re-bind (
|
|
3381
|
+
case "rebind":
|
|
3382
|
+
return "Re-bind (keep key, refresh binding \u2014 same instance)";
|
|
3383
|
+
case "reset-all":
|
|
3384
|
+
return "Reset everything (NEW key, NEW instance \u2014 for compromised key)";
|
|
3352
3385
|
case "skip":
|
|
3353
3386
|
return "Skip (do nothing)";
|
|
3354
3387
|
}
|
|
@@ -5092,6 +5125,20 @@ var init_fallback_banner = __esm({
|
|
|
5092
5125
|
}
|
|
5093
5126
|
});
|
|
5094
5127
|
|
|
5128
|
+
// src/json-envelope.ts
|
|
5129
|
+
function jsonOk(data) {
|
|
5130
|
+
return { ok: true, data };
|
|
5131
|
+
}
|
|
5132
|
+
function emitJsonEnvelope(env) {
|
|
5133
|
+
console.log(JSON.stringify(env, null, 2));
|
|
5134
|
+
}
|
|
5135
|
+
var init_json_envelope = __esm({
|
|
5136
|
+
"src/json-envelope.ts"() {
|
|
5137
|
+
"use strict";
|
|
5138
|
+
init_dist();
|
|
5139
|
+
}
|
|
5140
|
+
});
|
|
5141
|
+
|
|
5095
5142
|
// src/commands/device/fleet-notify.ts
|
|
5096
5143
|
async function notifyFleetReloadBestEffort(baseUrl = FLEET_STATUS_BASE_URL) {
|
|
5097
5144
|
try {
|
|
@@ -5228,6 +5275,207 @@ var init_upgrade2 = __esm({
|
|
|
5228
5275
|
}
|
|
5229
5276
|
});
|
|
5230
5277
|
|
|
5278
|
+
// src/lib/instance-picker.ts
|
|
5279
|
+
import readline from "readline";
|
|
5280
|
+
function resolveIO(ctx) {
|
|
5281
|
+
return {
|
|
5282
|
+
input: ctx.input ?? process.stdin,
|
|
5283
|
+
output: ctx.output ?? process.stdout
|
|
5284
|
+
};
|
|
5285
|
+
}
|
|
5286
|
+
function isTTY(ctx, io) {
|
|
5287
|
+
if (ctx.isTTY !== void 0) return ctx.isTTY;
|
|
5288
|
+
return Boolean(io.input.isTTY);
|
|
5289
|
+
}
|
|
5290
|
+
function ask(io, question) {
|
|
5291
|
+
const rl = readline.createInterface({ input: io.input, output: io.output });
|
|
5292
|
+
return new Promise((resolve) => {
|
|
5293
|
+
rl.question(question, (answer) => {
|
|
5294
|
+
rl.close();
|
|
5295
|
+
resolve(answer);
|
|
5296
|
+
});
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
function statusBlurb(status2) {
|
|
5300
|
+
switch (status2) {
|
|
5301
|
+
case "device":
|
|
5302
|
+
return "device";
|
|
5303
|
+
case "unauthorized":
|
|
5304
|
+
return "unauthorized \u2014 accept the USB prompt on the phone";
|
|
5305
|
+
case "offline":
|
|
5306
|
+
return "offline \u2014 check the USB cable / `adb kill-server`";
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
async function pickInstanceInteractive(candidates, ctx) {
|
|
5310
|
+
const io = resolveIO(ctx);
|
|
5311
|
+
if (!isTTY(ctx, io)) {
|
|
5312
|
+
return { kind: "skip" };
|
|
5313
|
+
}
|
|
5314
|
+
const frameworks = candidates.filter(
|
|
5315
|
+
(c) => c.kind === "framework"
|
|
5316
|
+
);
|
|
5317
|
+
const devices = candidates.filter(
|
|
5318
|
+
(c) => c.kind === "device"
|
|
5319
|
+
);
|
|
5320
|
+
if (ctx.caller === "init") {
|
|
5321
|
+
if (frameworks.length === 1 && devices.length === 0) {
|
|
5322
|
+
return { kind: "framework", id: frameworks[0].id };
|
|
5323
|
+
}
|
|
5324
|
+
return renderMixedMenu(frameworks, devices, io);
|
|
5325
|
+
}
|
|
5326
|
+
if (devices.length === 0) {
|
|
5327
|
+
if (ctx.caller === "discover") {
|
|
5328
|
+
io.output.write("No ADB devices found. Connect a device and try again.\n");
|
|
5329
|
+
}
|
|
5330
|
+
return { kind: "skip" };
|
|
5331
|
+
}
|
|
5332
|
+
if (devices.length === 1) {
|
|
5333
|
+
return askYesNoSingleDevice(devices[0], io);
|
|
5334
|
+
}
|
|
5335
|
+
return renderDeviceMenu(devices, io);
|
|
5336
|
+
}
|
|
5337
|
+
async function renderMixedMenu(frameworks, devices, io) {
|
|
5338
|
+
const items = [];
|
|
5339
|
+
let idx = 1;
|
|
5340
|
+
for (const fw of frameworks) {
|
|
5341
|
+
items.push({
|
|
5342
|
+
idx: idx++,
|
|
5343
|
+
kind: "framework",
|
|
5344
|
+
id: fw.id,
|
|
5345
|
+
label: fw.label,
|
|
5346
|
+
isDefault: fw.isDefault === true
|
|
5347
|
+
});
|
|
5348
|
+
}
|
|
5349
|
+
for (const d of devices) {
|
|
5350
|
+
items.push({
|
|
5351
|
+
idx: idx++,
|
|
5352
|
+
kind: "device",
|
|
5353
|
+
serial: d.serial,
|
|
5354
|
+
status: d.status,
|
|
5355
|
+
selectable: d.status === "device"
|
|
5356
|
+
});
|
|
5357
|
+
}
|
|
5358
|
+
const defaultItem = items.find((i) => i.kind === "framework" && i.isDefault) ?? items[0];
|
|
5359
|
+
io.output.write("\n");
|
|
5360
|
+
io.output.write("What would you like to bind on this machine?\n");
|
|
5361
|
+
io.output.write("\n");
|
|
5362
|
+
if (frameworks.length > 0) {
|
|
5363
|
+
io.output.write(" Local agent:\n");
|
|
5364
|
+
for (const it of items) {
|
|
5365
|
+
if (it.kind !== "framework") continue;
|
|
5366
|
+
const def = it.isDefault ? " (default)" : "";
|
|
5367
|
+
io.output.write(` [${it.idx}] ${it.label}${def}
|
|
5368
|
+
`);
|
|
5369
|
+
}
|
|
5370
|
+
io.output.write("\n");
|
|
5371
|
+
}
|
|
5372
|
+
if (devices.length > 0) {
|
|
5373
|
+
io.output.write(` ADB devices (${devices.length} found):
|
|
5374
|
+
`);
|
|
5375
|
+
for (const it of items) {
|
|
5376
|
+
if (it.kind !== "device") continue;
|
|
5377
|
+
const tag = statusBlurb(it.status);
|
|
5378
|
+
io.output.write(` [${it.idx}] ${it.serial.padEnd(20)} ${tag}
|
|
5379
|
+
`);
|
|
5380
|
+
}
|
|
5381
|
+
io.output.write("\n");
|
|
5382
|
+
}
|
|
5383
|
+
io.output.write(" [s] Skip\n");
|
|
5384
|
+
io.output.write("\n");
|
|
5385
|
+
return promptForChoice(items, defaultItem.idx, io);
|
|
5386
|
+
}
|
|
5387
|
+
async function renderDeviceMenu(devices, io) {
|
|
5388
|
+
const items = devices.map((d, i) => ({
|
|
5389
|
+
idx: i + 1,
|
|
5390
|
+
kind: "device",
|
|
5391
|
+
serial: d.serial,
|
|
5392
|
+
status: d.status,
|
|
5393
|
+
selectable: d.status === "device"
|
|
5394
|
+
}));
|
|
5395
|
+
io.output.write("\n");
|
|
5396
|
+
io.output.write(`Detected ${devices.length} ADB devices:
|
|
5397
|
+
`);
|
|
5398
|
+
for (const it of items) {
|
|
5399
|
+
io.output.write(` [${it.idx}] ${it.serial.padEnd(20)} ${statusBlurb(it.status)}
|
|
5400
|
+
`);
|
|
5401
|
+
}
|
|
5402
|
+
io.output.write("\n");
|
|
5403
|
+
return promptForChoice(
|
|
5404
|
+
items,
|
|
5405
|
+
/* defaultIdx */
|
|
5406
|
+
-1,
|
|
5407
|
+
io
|
|
5408
|
+
);
|
|
5409
|
+
}
|
|
5410
|
+
async function askYesNoSingleDevice(device, io) {
|
|
5411
|
+
if (device.status !== "device") {
|
|
5412
|
+
io.output.write(
|
|
5413
|
+
`Found 1 ADB device (serial=${device.serial}) but its status is ${statusBlurb(device.status)}.
|
|
5414
|
+
Skipping. Fix the device state and re-run.
|
|
5415
|
+
`
|
|
5416
|
+
);
|
|
5417
|
+
return { kind: "skip" };
|
|
5418
|
+
}
|
|
5419
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
5420
|
+
const answer = (await ask(io, `Found 1 ADB device (serial=${device.serial}). Attach now? [Y/n]: `)).trim().toLowerCase();
|
|
5421
|
+
if (answer === "" || answer === "y" || answer === "yes") {
|
|
5422
|
+
return { kind: "device", serial: device.serial };
|
|
5423
|
+
}
|
|
5424
|
+
if (answer === "n" || answer === "no" || answer === "s" || answer === "skip") {
|
|
5425
|
+
return { kind: "skip" };
|
|
5426
|
+
}
|
|
5427
|
+
io.output.write(` invalid input '${answer}'. Please answer Y or n.
|
|
5428
|
+
`);
|
|
5429
|
+
}
|
|
5430
|
+
io.output.write(" too many invalid inputs \u2014 skipping.\n");
|
|
5431
|
+
return { kind: "skip" };
|
|
5432
|
+
}
|
|
5433
|
+
async function promptForChoice(items, defaultIdx, io) {
|
|
5434
|
+
const range = `1-${items.length}`;
|
|
5435
|
+
const defaultHint = defaultIdx > 0 ? `default ${defaultIdx}` : "default skip";
|
|
5436
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
5437
|
+
const raw = await ask(io, `Choose [${range} / s] (${defaultHint}): `);
|
|
5438
|
+
const trimmed = raw.trim().toLowerCase();
|
|
5439
|
+
if (trimmed === "" && defaultIdx > 0) {
|
|
5440
|
+
return itemToSelection(items[defaultIdx - 1], io);
|
|
5441
|
+
}
|
|
5442
|
+
if (trimmed === "" || trimmed === "s" || trimmed === "skip") {
|
|
5443
|
+
return { kind: "skip" };
|
|
5444
|
+
}
|
|
5445
|
+
const n = Number.parseInt(trimmed, 10);
|
|
5446
|
+
if (!Number.isFinite(n) || n < 1 || n > items.length) {
|
|
5447
|
+
io.output.write(` invalid input '${raw.trim()}'. Please pick a number in ${range} or 's'.
|
|
5448
|
+
`);
|
|
5449
|
+
continue;
|
|
5450
|
+
}
|
|
5451
|
+
const picked = items[n - 1];
|
|
5452
|
+
if (picked.kind === "device" && !picked.selectable) {
|
|
5453
|
+
io.output.write(
|
|
5454
|
+
` device ${picked.serial} is ${statusBlurb(picked.status)} and cannot be attached yet.
|
|
5455
|
+
Fix the device state on the phone and re-run.
|
|
5456
|
+
`
|
|
5457
|
+
);
|
|
5458
|
+
continue;
|
|
5459
|
+
}
|
|
5460
|
+
return itemToSelection(picked, io);
|
|
5461
|
+
}
|
|
5462
|
+
io.output.write(" too many invalid inputs \u2014 skipping.\n");
|
|
5463
|
+
return { kind: "skip" };
|
|
5464
|
+
}
|
|
5465
|
+
function itemToSelection(item, _io) {
|
|
5466
|
+
if (item.kind === "framework") {
|
|
5467
|
+
return { kind: "framework", id: item.id };
|
|
5468
|
+
}
|
|
5469
|
+
return { kind: "device", serial: item.serial };
|
|
5470
|
+
}
|
|
5471
|
+
var MAX_RETRIES;
|
|
5472
|
+
var init_instance_picker = __esm({
|
|
5473
|
+
"src/lib/instance-picker.ts"() {
|
|
5474
|
+
"use strict";
|
|
5475
|
+
MAX_RETRIES = 3;
|
|
5476
|
+
}
|
|
5477
|
+
});
|
|
5478
|
+
|
|
5231
5479
|
// src/commands/device/state.ts
|
|
5232
5480
|
import lockfile from "proper-lockfile";
|
|
5233
5481
|
function deviceAgentTargetId(serial) {
|
|
@@ -5293,7 +5541,7 @@ var init_state2 = __esm({
|
|
|
5293
5541
|
|
|
5294
5542
|
// src/commands/device/attach.ts
|
|
5295
5543
|
import { spawn as spawn2 } from "child_process";
|
|
5296
|
-
import
|
|
5544
|
+
import readline2 from "readline";
|
|
5297
5545
|
function resolvePerDeviceAgentGatewayUrl(cfg, override) {
|
|
5298
5546
|
if (override === void 0) return resolveAgentGatewayUrl(cfg);
|
|
5299
5547
|
const trimmed = override.trim();
|
|
@@ -5325,6 +5573,24 @@ function resolvePerDeviceAgentGatewayUrl(cfg, override) {
|
|
|
5325
5573
|
}
|
|
5326
5574
|
return trimmed;
|
|
5327
5575
|
}
|
|
5576
|
+
async function pickAttachSerial() {
|
|
5577
|
+
const devices = await deviceRuntime.listAdbDevices();
|
|
5578
|
+
const ready = devices.filter((d) => d.status === "device");
|
|
5579
|
+
if (ready.length <= 1) {
|
|
5580
|
+
return deviceRuntime.detectSingleDevice();
|
|
5581
|
+
}
|
|
5582
|
+
if (!process.stdin.isTTY) {
|
|
5583
|
+
return deviceRuntime.detectSingleDevice();
|
|
5584
|
+
}
|
|
5585
|
+
const candidates = devices.map((d) => ({
|
|
5586
|
+
kind: "device",
|
|
5587
|
+
serial: d.serial,
|
|
5588
|
+
status: d.status
|
|
5589
|
+
}));
|
|
5590
|
+
const sel = await pickInstanceInteractive(candidates, { caller: "attach" });
|
|
5591
|
+
if (sel.kind === "device") return sel.serial;
|
|
5592
|
+
return deviceRuntime.detectSingleDevice();
|
|
5593
|
+
}
|
|
5328
5594
|
async function attach(options) {
|
|
5329
5595
|
const cfg = await loadOrCreateConfig();
|
|
5330
5596
|
const reporter = new CliReporter();
|
|
@@ -5336,7 +5602,7 @@ async function attach(options) {
|
|
|
5336
5602
|
if (options.all) {
|
|
5337
5603
|
return attachAll(cfg, reporter, withVideo, options);
|
|
5338
5604
|
}
|
|
5339
|
-
const serial = options.serial ?? await
|
|
5605
|
+
const serial = options.serial ?? await pickAttachSerial();
|
|
5340
5606
|
const name = options.name ?? serial;
|
|
5341
5607
|
await deviceRuntime.ensureAgent(reporter);
|
|
5342
5608
|
reporter.stop();
|
|
@@ -5451,11 +5717,26 @@ async function attach(options) {
|
|
|
5451
5717
|
await maybeNotifyFleetWithHint(cfg);
|
|
5452
5718
|
}
|
|
5453
5719
|
async function attachAll(cfg, reporter, withVideo, options) {
|
|
5454
|
-
const
|
|
5720
|
+
const all = await deviceRuntime.listAdbDevices();
|
|
5721
|
+
const devices = all.filter((d) => d.status === "device");
|
|
5722
|
+
const skipped = all.filter((d) => d.status !== "device");
|
|
5455
5723
|
if (devices.length === 0) {
|
|
5456
|
-
|
|
5724
|
+
if (skipped.length > 0) {
|
|
5725
|
+
console.log("No ADB devices ready to attach. Issues:");
|
|
5726
|
+
for (const d of skipped) {
|
|
5727
|
+
console.log(` ${d.serial}: ${d.rawStatus}`);
|
|
5728
|
+
}
|
|
5729
|
+
} else {
|
|
5730
|
+
console.log("No ADB devices detected");
|
|
5731
|
+
}
|
|
5457
5732
|
return;
|
|
5458
5733
|
}
|
|
5734
|
+
if (skipped.length > 0) {
|
|
5735
|
+
console.log(`Skipping ${skipped.length} device(s) not in 'device' state:`);
|
|
5736
|
+
for (const d of skipped) {
|
|
5737
|
+
console.log(` ${d.serial}: ${d.rawStatus}`);
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5459
5740
|
console.log(`Discovered ${devices.length} device(s) via adb.`);
|
|
5460
5741
|
await deviceRuntime.ensureAgent(reporter);
|
|
5461
5742
|
reporter.stop();
|
|
@@ -5695,7 +5976,7 @@ async function maybeNotifyFleetWithHint(cfg) {
|
|
|
5695
5976
|
console.log("it, the device is recorded in ~/.beeos/devices.json but");
|
|
5696
5977
|
console.log("no process is supervising the per-device agent.");
|
|
5697
5978
|
console.log("");
|
|
5698
|
-
const rl =
|
|
5979
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5699
5980
|
const answer = await new Promise((resolve) => {
|
|
5700
5981
|
rl.question("Enable fleet now (registers a launchd service)? [Y/n]: ", (a) => {
|
|
5701
5982
|
rl.close();
|
|
@@ -5869,7 +6150,7 @@ Android SDK Platform-Tools (adb) is licensed under the Android SDK License Agree
|
|
|
5869
6150
|
Set BEEOS_ACCEPT_ADB_LICENSE=1 in your environment to skip this prompt next time.
|
|
5870
6151
|
`
|
|
5871
6152
|
);
|
|
5872
|
-
const rl =
|
|
6153
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5873
6154
|
const ans = await new Promise((resolve) => {
|
|
5874
6155
|
rl.question("Agree to the Android SDK license and download platform-tools? [Y/n]: ", (a) => {
|
|
5875
6156
|
rl.close();
|
|
@@ -5883,6 +6164,7 @@ var init_attach = __esm({
|
|
|
5883
6164
|
"use strict";
|
|
5884
6165
|
init_dist();
|
|
5885
6166
|
init_progress2();
|
|
6167
|
+
init_instance_picker();
|
|
5886
6168
|
init_fallback_banner();
|
|
5887
6169
|
init_fleet_notify();
|
|
5888
6170
|
init_state2();
|
|
@@ -6119,11 +6401,65 @@ var init_refresh_config = __esm({
|
|
|
6119
6401
|
}
|
|
6120
6402
|
});
|
|
6121
6403
|
|
|
6404
|
+
// src/commands/device/discover.ts
|
|
6405
|
+
async function run6(options = {}) {
|
|
6406
|
+
const reporter = new CliReporter();
|
|
6407
|
+
await ensureAdb(reporter, { autoInstall: false });
|
|
6408
|
+
reporter.stop();
|
|
6409
|
+
let devices;
|
|
6410
|
+
try {
|
|
6411
|
+
devices = await deviceRuntime.listAdbDevices();
|
|
6412
|
+
} catch (e) {
|
|
6413
|
+
if (options.json) {
|
|
6414
|
+
emitJsonEnvelope(jsonOk({ devices: [], selected: null, action: "skip" }));
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
console.log("ADB is not available \u2014 install Android platform-tools or run `beeos device attach` once to auto-install.");
|
|
6418
|
+
console.log(` (${e instanceof Error ? e.message : String(e)})`);
|
|
6419
|
+
return;
|
|
6420
|
+
}
|
|
6421
|
+
if (options.json) {
|
|
6422
|
+
emitJsonEnvelope(
|
|
6423
|
+
jsonOk({
|
|
6424
|
+
devices: devices.map((d) => ({
|
|
6425
|
+
serial: d.serial,
|
|
6426
|
+
status: d.status,
|
|
6427
|
+
rawStatus: d.rawStatus
|
|
6428
|
+
})),
|
|
6429
|
+
selected: null,
|
|
6430
|
+
action: "list"
|
|
6431
|
+
})
|
|
6432
|
+
);
|
|
6433
|
+
return;
|
|
6434
|
+
}
|
|
6435
|
+
const candidates = devices.map((d) => ({
|
|
6436
|
+
kind: "device",
|
|
6437
|
+
serial: d.serial,
|
|
6438
|
+
status: d.status
|
|
6439
|
+
}));
|
|
6440
|
+
const sel = await pickInstanceInteractive(candidates, { caller: "discover" });
|
|
6441
|
+
if (sel.kind === "skip") return;
|
|
6442
|
+
if (sel.kind === "device") {
|
|
6443
|
+
await attach({ serial: sel.serial });
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
var init_discover = __esm({
|
|
6447
|
+
"src/commands/device/discover.ts"() {
|
|
6448
|
+
"use strict";
|
|
6449
|
+
init_dist();
|
|
6450
|
+
init_progress2();
|
|
6451
|
+
init_json_envelope();
|
|
6452
|
+
init_instance_picker();
|
|
6453
|
+
init_attach();
|
|
6454
|
+
}
|
|
6455
|
+
});
|
|
6456
|
+
|
|
6122
6457
|
// src/commands/device/index.ts
|
|
6123
6458
|
var device_exports = {};
|
|
6124
6459
|
__export(device_exports, {
|
|
6125
6460
|
attach: () => attach,
|
|
6126
6461
|
detach: () => detach,
|
|
6462
|
+
discover: () => run6,
|
|
6127
6463
|
exec: () => exec,
|
|
6128
6464
|
list: () => list,
|
|
6129
6465
|
nextHttpPort: () => nextHttpPort,
|
|
@@ -6139,6 +6475,7 @@ var init_device2 = __esm({
|
|
|
6139
6475
|
init_exec();
|
|
6140
6476
|
init_upgrade2();
|
|
6141
6477
|
init_refresh_config();
|
|
6478
|
+
init_discover();
|
|
6142
6479
|
init_state2();
|
|
6143
6480
|
}
|
|
6144
6481
|
});
|
|
@@ -6362,18 +6699,8 @@ var NodePlatformAdapter = class {
|
|
|
6362
6699
|
init_dist();
|
|
6363
6700
|
init_progress2();
|
|
6364
6701
|
init_fallback_banner();
|
|
6702
|
+
init_json_envelope();
|
|
6365
6703
|
import os6 from "os";
|
|
6366
|
-
|
|
6367
|
-
// src/json-envelope.ts
|
|
6368
|
-
init_dist();
|
|
6369
|
-
function jsonOk(data) {
|
|
6370
|
-
return { ok: true, data };
|
|
6371
|
-
}
|
|
6372
|
-
function emitJsonEnvelope(env) {
|
|
6373
|
-
console.log(JSON.stringify(env, null, 2));
|
|
6374
|
-
}
|
|
6375
|
-
|
|
6376
|
-
// src/commands/start.ts
|
|
6377
6704
|
async function run(agentFramework, options) {
|
|
6378
6705
|
const p = getPlatformAdapter();
|
|
6379
6706
|
await ensureDirs();
|
|
@@ -6564,6 +6891,7 @@ async function run2(agentFramework) {
|
|
|
6564
6891
|
|
|
6565
6892
|
// src/commands/status.ts
|
|
6566
6893
|
init_dist();
|
|
6894
|
+
init_json_envelope();
|
|
6567
6895
|
async function run3(options = {}) {
|
|
6568
6896
|
const home = beeoHome();
|
|
6569
6897
|
const cfg = await loadOrCreateConfig();
|
|
@@ -6629,6 +6957,7 @@ function formatService(s) {
|
|
|
6629
6957
|
|
|
6630
6958
|
// src/commands/update.ts
|
|
6631
6959
|
init_dist();
|
|
6960
|
+
init_json_envelope();
|
|
6632
6961
|
init_upgrade2();
|
|
6633
6962
|
async function run4(agentFramework, options = {}) {
|
|
6634
6963
|
if (agentFramework === "device") {
|
|
@@ -6669,6 +6998,7 @@ async function run4(agentFramework, options = {}) {
|
|
|
6669
6998
|
|
|
6670
6999
|
// src/commands/bind-status.ts
|
|
6671
7000
|
init_dist();
|
|
7001
|
+
init_json_envelope();
|
|
6672
7002
|
async function run5(bindId, options) {
|
|
6673
7003
|
const cfg = await loadOrCreateConfig();
|
|
6674
7004
|
const resp = await getBindStatus(cfg.platform.api_url, bindId);
|
|
@@ -6732,14 +7062,15 @@ init_device2();
|
|
|
6732
7062
|
|
|
6733
7063
|
// src/commands/init.ts
|
|
6734
7064
|
init_dist();
|
|
6735
|
-
import
|
|
7065
|
+
import readline3 from "readline";
|
|
6736
7066
|
|
|
6737
7067
|
// src/commands/doctor.ts
|
|
6738
7068
|
init_dist();
|
|
7069
|
+
init_json_envelope();
|
|
6739
7070
|
import fs9 from "fs/promises";
|
|
6740
7071
|
import path9 from "path";
|
|
6741
7072
|
var LOG_SIZE_WARN_BYTES = 50 * 1024 * 1024;
|
|
6742
|
-
async function
|
|
7073
|
+
async function run7(options) {
|
|
6743
7074
|
const state = await detectExistingInstall();
|
|
6744
7075
|
const cfg = await loadOrCreateConfig();
|
|
6745
7076
|
const p = getPlatformAdapter();
|
|
@@ -6754,7 +7085,8 @@ async function run6(options) {
|
|
|
6754
7085
|
const resolvedAgentGatewayUrl = resolveAgentGatewayUrl(cfg);
|
|
6755
7086
|
const agentGatewayHealth = await probeAgentGateway(resolvedAgentGatewayUrl);
|
|
6756
7087
|
const tools = await collectToolStatus();
|
|
6757
|
-
const
|
|
7088
|
+
const hasBoundDevices = state.devices.entries.length > 0;
|
|
7089
|
+
const shimReport = await collectShimReport(hasBoundDevices);
|
|
6758
7090
|
const warnings = [];
|
|
6759
7091
|
const hints = [];
|
|
6760
7092
|
if (!state.hasIdentity) {
|
|
@@ -6820,7 +7152,7 @@ async function run6(options) {
|
|
|
6820
7152
|
);
|
|
6821
7153
|
} else if (e.outcome === "missing") {
|
|
6822
7154
|
warnings.push(`${e.pkg}: not installed globally \u2014 run 'beeos init' to install.`);
|
|
6823
|
-
} else if (e.outcome === "error") {
|
|
7155
|
+
} else if (e.outcome === "error" || e.outcome === "not_installed_yet") {
|
|
6824
7156
|
}
|
|
6825
7157
|
}
|
|
6826
7158
|
if (shimReport.entries.some((e) => e.outcome === "outdated" || e.outcome === "missing")) {
|
|
@@ -6921,8 +7253,12 @@ async function collectToolStatus() {
|
|
|
6921
7253
|
vncBridge: { path: vncPath }
|
|
6922
7254
|
};
|
|
6923
7255
|
}
|
|
6924
|
-
async function collectShimReport() {
|
|
7256
|
+
async function collectShimReport(hasBoundDevices) {
|
|
6925
7257
|
const sources = readPinSourcesFromEnv();
|
|
7258
|
+
const deviceSuite = /* @__PURE__ */ new Set([
|
|
7259
|
+
NPM_PKGS.DEVICE_AGENT,
|
|
7260
|
+
NPM_PKGS.DEVICE_MCP_SERVER
|
|
7261
|
+
]);
|
|
6926
7262
|
const entries = await Promise.all(
|
|
6927
7263
|
ALL_BEEOS_PKGS.map(async (pkg) => {
|
|
6928
7264
|
const spec = resolveInstallSpec(pkg, sources);
|
|
@@ -6932,8 +7268,9 @@ async function collectShimReport() {
|
|
|
6932
7268
|
]);
|
|
6933
7269
|
let outcome;
|
|
6934
7270
|
if (latest === null) outcome = "error";
|
|
6935
|
-
else if (installed === null)
|
|
6936
|
-
|
|
7271
|
+
else if (installed === null) {
|
|
7272
|
+
outcome = deviceSuite.has(pkg) && !hasBoundDevices ? "not_installed_yet" : "missing";
|
|
7273
|
+
} else if (installed === latest) outcome = "ok";
|
|
6937
7274
|
else outcome = "outdated";
|
|
6938
7275
|
if (pkg === NPM_PKGS.CLI && installed === null) {
|
|
6939
7276
|
outcome = "missing";
|
|
@@ -6951,6 +7288,8 @@ function shimMarker(outcome) {
|
|
|
6951
7288
|
return "\u2191";
|
|
6952
7289
|
case "missing":
|
|
6953
7290
|
return "\u2717";
|
|
7291
|
+
case "not_installed_yet":
|
|
7292
|
+
return "\xB7";
|
|
6954
7293
|
case "error":
|
|
6955
7294
|
return "?";
|
|
6956
7295
|
}
|
|
@@ -7037,9 +7376,11 @@ function formatAgentGatewayStatus(h) {
|
|
|
7037
7376
|
}
|
|
7038
7377
|
|
|
7039
7378
|
// src/commands/init.ts
|
|
7379
|
+
init_attach();
|
|
7040
7380
|
init_progress2();
|
|
7041
7381
|
init_fallback_banner();
|
|
7042
|
-
|
|
7382
|
+
init_instance_picker();
|
|
7383
|
+
async function run8(options) {
|
|
7043
7384
|
await ensureDirs();
|
|
7044
7385
|
if (options.device) {
|
|
7045
7386
|
const { attach: attach2 } = await Promise.resolve().then(() => (init_device2(), device_exports));
|
|
@@ -7048,7 +7389,7 @@ async function run7(options) {
|
|
|
7048
7389
|
}
|
|
7049
7390
|
const state = await detectExistingInstall();
|
|
7050
7391
|
if (options.json) {
|
|
7051
|
-
await
|
|
7392
|
+
await run7({ json: true });
|
|
7052
7393
|
} else {
|
|
7053
7394
|
printBanner();
|
|
7054
7395
|
for (const line of summarizeExistingInstall(state)) {
|
|
@@ -7074,10 +7415,45 @@ async function run7(options) {
|
|
|
7074
7415
|
}
|
|
7075
7416
|
console.log("Ensuring OpenClaw is running...\n");
|
|
7076
7417
|
}
|
|
7077
|
-
if (decision === "rebind
|
|
7418
|
+
if (decision === "rebind") {
|
|
7419
|
+
await removeBindingInfo();
|
|
7420
|
+
}
|
|
7421
|
+
if (decision === "reset-all") {
|
|
7078
7422
|
await rotateIdentity();
|
|
7079
7423
|
await removeBindingInfo();
|
|
7080
7424
|
}
|
|
7425
|
+
if (decision === "fresh" && !options.framework && !options.yes && !options.json && process.stdin.isTTY) {
|
|
7426
|
+
const sel = await pickFreshInstance(options);
|
|
7427
|
+
if (sel.kind === "skip") {
|
|
7428
|
+
console.log("Skipped. Re-run `beeos init` or `beeos device attach` whenever ready.");
|
|
7429
|
+
return;
|
|
7430
|
+
}
|
|
7431
|
+
if (sel.kind === "device") {
|
|
7432
|
+
await attach({ serial: sel.serial });
|
|
7433
|
+
return;
|
|
7434
|
+
}
|
|
7435
|
+
const descriptor2 = frameworkById(sel.id);
|
|
7436
|
+
if (!descriptor2 || descriptor2.status !== "available") {
|
|
7437
|
+
throw new BeeosError({
|
|
7438
|
+
code: "framework_unavailable",
|
|
7439
|
+
message: `Agent framework '${sel.id}' is not available.`,
|
|
7440
|
+
hint: "Pick a different framework or reinstall the CLI.",
|
|
7441
|
+
details: { requested: sel.id, available: availableFrameworks().map((f) => f.id) }
|
|
7442
|
+
});
|
|
7443
|
+
}
|
|
7444
|
+
await run(descriptor2.id, {
|
|
7445
|
+
force: true,
|
|
7446
|
+
json: options.json,
|
|
7447
|
+
browser: options.browser !== false
|
|
7448
|
+
});
|
|
7449
|
+
if (!options.json && !options.skipServicePrompt) {
|
|
7450
|
+
await maybePromptServiceInstall();
|
|
7451
|
+
}
|
|
7452
|
+
if (!options.json) {
|
|
7453
|
+
printNextSteps();
|
|
7454
|
+
}
|
|
7455
|
+
return;
|
|
7456
|
+
}
|
|
7081
7457
|
const frameworkId = await decideFramework(state, decision, options);
|
|
7082
7458
|
const descriptor = frameworkById(frameworkId);
|
|
7083
7459
|
if (!descriptor || descriptor.status !== "available") {
|
|
@@ -7176,6 +7552,32 @@ async function decideFramework(state, decision, options) {
|
|
|
7176
7552
|
}
|
|
7177
7553
|
return picked.id;
|
|
7178
7554
|
}
|
|
7555
|
+
async function safeListAdbDevices() {
|
|
7556
|
+
const adbPath = await findAdb().catch(() => null);
|
|
7557
|
+
if (!adbPath) return [];
|
|
7558
|
+
try {
|
|
7559
|
+
return await deviceRuntime.listAdbDevices();
|
|
7560
|
+
} catch {
|
|
7561
|
+
return [];
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
async function pickFreshInstance(_opts) {
|
|
7565
|
+
const frameworks = availableFrameworks().map((f) => ({
|
|
7566
|
+
kind: "framework",
|
|
7567
|
+
id: f.id,
|
|
7568
|
+
label: f.displayName,
|
|
7569
|
+
description: f.description,
|
|
7570
|
+
isDefault: f.id === defaultFrameworkId()
|
|
7571
|
+
}));
|
|
7572
|
+
const devices = (await safeListAdbDevices()).map((d) => ({
|
|
7573
|
+
kind: "device",
|
|
7574
|
+
serial: d.serial,
|
|
7575
|
+
status: d.status
|
|
7576
|
+
}));
|
|
7577
|
+
return pickInstanceInteractive([...frameworks, ...devices], {
|
|
7578
|
+
caller: "init"
|
|
7579
|
+
});
|
|
7580
|
+
}
|
|
7179
7581
|
async function maybePromptServiceInstall() {
|
|
7180
7582
|
try {
|
|
7181
7583
|
const mgr = await getServiceManager();
|
|
@@ -7227,7 +7629,7 @@ function printNextSteps() {
|
|
|
7227
7629
|
console.log("");
|
|
7228
7630
|
}
|
|
7229
7631
|
function prompt(question) {
|
|
7230
|
-
const rl =
|
|
7632
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
7231
7633
|
return new Promise((resolve) => {
|
|
7232
7634
|
rl.question(question, (answer) => {
|
|
7233
7635
|
rl.close();
|
|
@@ -7244,19 +7646,22 @@ async function upgradeAndMaybeExit(options) {
|
|
|
7244
7646
|
if (options.json) {
|
|
7245
7647
|
return false;
|
|
7246
7648
|
}
|
|
7247
|
-
|
|
7649
|
+
const packages = await pickInstalledPackages();
|
|
7650
|
+
console.log("Checking for newer @beeos-ai/cli on npm...");
|
|
7248
7651
|
const reporter = new CliReporter();
|
|
7249
7652
|
let outcome;
|
|
7250
7653
|
try {
|
|
7251
7654
|
outcome = await upgradeBeeosSuite({
|
|
7252
|
-
packages
|
|
7655
|
+
packages,
|
|
7253
7656
|
progress: reporter
|
|
7254
7657
|
});
|
|
7255
7658
|
} catch (e) {
|
|
7256
7659
|
reporter.stop();
|
|
7257
7660
|
console.log(
|
|
7258
7661
|
` (upgrade skipped \u2014 ${e instanceof Error ? e.message : String(e)})
|
|
7259
|
-
Tip: re-run \`npm install -g @beeos-ai/cli
|
|
7662
|
+
Tip: re-run \`npm install -g @beeos-ai/cli\` manually. The device
|
|
7663
|
+
suite (device-agent + device-mcp-server) will be auto-installed
|
|
7664
|
+
on the next \`beeos device attach\`.`
|
|
7260
7665
|
);
|
|
7261
7666
|
return false;
|
|
7262
7667
|
}
|
|
@@ -7265,7 +7670,7 @@ async function upgradeAndMaybeExit(options) {
|
|
|
7265
7670
|
const failed = outcome.packages.find((p) => p.failed)?.failed ?? "unknown error";
|
|
7266
7671
|
console.log(` \u26A0 npm install failed: ${failed}`);
|
|
7267
7672
|
console.log(
|
|
7268
|
-
" Manual fix:\n npm i -g @beeos-ai/cli
|
|
7673
|
+
" Manual fix:\n npm i -g @beeos-ai/cli\n (the device suite is auto-installed on `beeos device attach`)\n"
|
|
7269
7674
|
);
|
|
7270
7675
|
return false;
|
|
7271
7676
|
}
|
|
@@ -7289,9 +7694,18 @@ async function upgradeAndMaybeExit(options) {
|
|
|
7289
7694
|
console.log(" Agents updated. Continuing init...\n");
|
|
7290
7695
|
return false;
|
|
7291
7696
|
}
|
|
7697
|
+
async function pickInstalledPackages() {
|
|
7698
|
+
const packages = [NPM_PKGS.CLI];
|
|
7699
|
+
for (const pkg of [NPM_PKGS.DEVICE_AGENT, NPM_PKGS.DEVICE_MCP_SERVER]) {
|
|
7700
|
+
const installed = await npmGlobalVersion(pkg).catch(() => null);
|
|
7701
|
+
if (installed !== null) packages.push(pkg);
|
|
7702
|
+
}
|
|
7703
|
+
return packages;
|
|
7704
|
+
}
|
|
7292
7705
|
|
|
7293
7706
|
// src/commands/service.ts
|
|
7294
7707
|
init_dist();
|
|
7708
|
+
init_json_envelope();
|
|
7295
7709
|
async function install(options) {
|
|
7296
7710
|
const mgr = await getServiceManager();
|
|
7297
7711
|
const check = await mgr.selfCheck();
|
|
@@ -7376,7 +7790,8 @@ async function status(options) {
|
|
|
7376
7790
|
|
|
7377
7791
|
// src/commands/migrate.ts
|
|
7378
7792
|
init_dist();
|
|
7379
|
-
|
|
7793
|
+
init_json_envelope();
|
|
7794
|
+
async function run9(options) {
|
|
7380
7795
|
await ensureDirs();
|
|
7381
7796
|
const mgr = await getServiceManager();
|
|
7382
7797
|
const mig = await migrateLegacySupervisor(mgr);
|
|
@@ -7413,6 +7828,7 @@ async function run8(options) {
|
|
|
7413
7828
|
|
|
7414
7829
|
// src/commands/logs.ts
|
|
7415
7830
|
init_dist();
|
|
7831
|
+
init_json_envelope();
|
|
7416
7832
|
function resolveLogTarget(input, services) {
|
|
7417
7833
|
const exact = services.find((s) => s.id === input);
|
|
7418
7834
|
if (exact) {
|
|
@@ -7434,7 +7850,7 @@ function resolveLogTarget(input, services) {
|
|
|
7434
7850
|
matchedFrom: "fallback"
|
|
7435
7851
|
};
|
|
7436
7852
|
}
|
|
7437
|
-
async function
|
|
7853
|
+
async function run10(idArg, options) {
|
|
7438
7854
|
const mgr = await getServiceManager();
|
|
7439
7855
|
let services = [];
|
|
7440
7856
|
try {
|
|
@@ -7508,12 +7924,12 @@ setPlatformAdapter(new NodePlatformAdapter());
|
|
|
7508
7924
|
var program = new Command();
|
|
7509
7925
|
var cliVersion = getCliVersion(import.meta.url, resolveActiveDistTag());
|
|
7510
7926
|
program.name("beeos").version(cliVersion.display).description("BeeOS \u2014 run AI agents from your desktop");
|
|
7511
|
-
program.command("init").description("One-shot install + bind flow (default entry from the curl installer)").option("--framework <name>", "Agent framework to install (default: openclaw)").option("--yes", "Non-interactive \u2014 accept the default action", false).option("--json", "Output machine-readable JSON (implies --yes)", false).option("--no-browser", "Don't auto-open a browser for bind confirmation").option("--headless", "Never open a browser (use terminal QR only)", false).option("--skip-service-prompt", "Skip the system-service install prompt", false).option("--device", "Jump straight into `beeos device attach` instead", false).action((opts) =>
|
|
7512
|
-
program.command("doctor").description("Inspect local BeeOS state (install, binding, services, warnings)").option("--json", "Output machine-readable JSON", false).action((opts) =>
|
|
7513
|
-
program.command("migrate").description("Migrate legacy Node supervisor state to OS-native services (one-shot)").option("--json", "Output machine-readable JSON", false).action((opts) =>
|
|
7927
|
+
program.command("init").description("One-shot install + bind flow (default entry from the curl installer)").option("--framework <name>", "Agent framework to install (default: openclaw)").option("--yes", "Non-interactive \u2014 accept the default action", false).option("--json", "Output machine-readable JSON (implies --yes)", false).option("--no-browser", "Don't auto-open a browser for bind confirmation").option("--headless", "Never open a browser (use terminal QR only)", false).option("--skip-service-prompt", "Skip the system-service install prompt", false).option("--device", "Jump straight into `beeos device attach` instead", false).action((opts) => run8(opts));
|
|
7928
|
+
program.command("doctor").description("Inspect local BeeOS state (install, binding, services, warnings)").option("--json", "Output machine-readable JSON", false).action((opts) => run7(opts));
|
|
7929
|
+
program.command("migrate").description("Migrate legacy Node supervisor state to OS-native services (one-shot)").option("--json", "Output machine-readable JSON", false).action((opts) => run9(opts));
|
|
7514
7930
|
program.command("logs").description(
|
|
7515
7931
|
"Tail logs for a BeeOS service. Pass an ADB serial or service id; omit to list every registered service's log file path."
|
|
7516
|
-
).argument("[id]", "Service id (e.g. `device-agent-<serial>`) or raw ADB serial").option("-f, --follow", "Stream new log lines (Ctrl-C to exit)", false).option("-n, --lines <n>", "Initial trailing lines to print (default 50)", (v) => Number(v)).option("--json", "Output machine-readable JSON (resolves the path; does not stream)", false).action((id, opts) =>
|
|
7932
|
+
).argument("[id]", "Service id (e.g. `device-agent-<serial>`) or raw ADB serial").option("-f, --follow", "Stream new log lines (Ctrl-C to exit)", false).option("-n, --lines <n>", "Initial trailing lines to print (default 50)", (v) => Number(v)).option("--json", "Output machine-readable JSON (resolves the path; does not stream)", false).action((id, opts) => run10(id, opts));
|
|
7517
7933
|
var serviceCmd = program.command("service").description("Inspect and manage the OS-native service manager (launchd / systemd --user / Task Scheduler)");
|
|
7518
7934
|
serviceCmd.command("install").description("Verify the OS service manager is available (per-target services install automatically)").option("--json", "Output machine-readable JSON", false).action((opts) => install(opts));
|
|
7519
7935
|
serviceCmd.command("uninstall").description("Uninstall every BeeOS-managed OS service").option("--json", "Output machine-readable JSON", false).action((opts) => uninstall(opts));
|
|
@@ -7541,6 +7957,9 @@ deviceCmd.command("upgrade").description("Upgrade the device-agent (and scrcpy/v
|
|
|
7541
7957
|
deviceCmd.command("refresh-config").description(
|
|
7542
7958
|
"Re-fetch this device's VLM/ARouter config from Agent Gateway and rewrite ~/.beeos/<instance_id>.config.json (use after dashboard changes)."
|
|
7543
7959
|
).option("--serial <serial>", "ADB device serial number").option("--all", "Refresh every attached device", false).action(refreshConfig);
|
|
7960
|
+
deviceCmd.command("discover").description(
|
|
7961
|
+
"List ADB devices and pick one to attach (interactive). Use this when you've plugged in a phone after `beeos init` and want to attach it without remembering --serial."
|
|
7962
|
+
).option("--json", "Output machine-readable JSON envelope ({ok,data})", false).action((opts) => run6(opts));
|
|
7544
7963
|
program.parseAsync(process.argv).catch((err) => {
|
|
7545
7964
|
const wantsJson = process.argv.includes("--json");
|
|
7546
7965
|
if (isBeeosError(err)) {
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"",
|
|
8
8
|
" 1. web/packages/cli/scripts/install.sh (bash, pre-Node)",
|
|
9
9
|
" 2. web/packages/cli/scripts/install.ps1 (PowerShell, pre-Node)",
|
|
10
|
-
" 3. web/packages/
|
|
10
|
+
" 3. web/packages/core/src/detect.ts (TS, post-Node, used by init.ts)",
|
|
11
11
|
"",
|
|
12
12
|
"Pre-Node sites can't import TypeScript constants, so we anchor the",
|
|
13
13
|
"wording with this manifest plus a vitest grep lint",
|
|
@@ -15,7 +15,23 @@
|
|
|
15
15
|
"If you change a label in any of the three files, update the",
|
|
16
16
|
"matching `keywords` here so the lint stays representative; if you",
|
|
17
17
|
"add a new action altogether, add a new entry below + render it",
|
|
18
|
-
"in all three sites."
|
|
18
|
+
"in all three sites.",
|
|
19
|
+
"",
|
|
20
|
+
"## Note on the 1.0.17 rebind split (P2 of the install-link review)",
|
|
21
|
+
"",
|
|
22
|
+
"detect.ts now exposes TWO rebind variants in addition to the three",
|
|
23
|
+
"actions enforced here:",
|
|
24
|
+
"",
|
|
25
|
+
" - 'rebind' — soft rebind, keeps Ed25519 key, refreshes binding;",
|
|
26
|
+
" same instance_id (platform Bind handler returns",
|
|
27
|
+
" the existing instance for a known pubkey).",
|
|
28
|
+
" - 'reset-all' — rotates the key AND wipes binding; NEW instance.",
|
|
29
|
+
"",
|
|
30
|
+
"These are NOT enforced across all 3 call sites because the",
|
|
31
|
+
"pre-Node bash/PowerShell installers have only [Reinstall][Re-run][Skip]",
|
|
32
|
+
"buttons — the rebind variants live exclusively inside `beeos init`'s",
|
|
33
|
+
"interactive menu. The 'rerun' keyword 'Re-bind' below incidentally",
|
|
34
|
+
"covers detect.ts's soft-rebind label, which keeps this lint passing."
|
|
19
35
|
],
|
|
20
36
|
"actions": {
|
|
21
37
|
"upgrade": {
|
|
@@ -23,7 +39,7 @@
|
|
|
23
39
|
"keywords": ["Reinstall", "Upgrade"]
|
|
24
40
|
},
|
|
25
41
|
"rerun": {
|
|
26
|
-
"$comment": "Reuse the existing install (no npm i -g). install.sh/install.ps1 say 'Re-run beeos init'
|
|
42
|
+
"$comment": "Reuse the existing install (no npm i -g). install.sh/install.ps1 say 'Re-run beeos init'. detect.ts's soft-rebind label includes 'Re-bind' which is intentionally captured here too — see the 1.0.17 split note in $comment above.",
|
|
27
43
|
"keywords": ["Re-run", "Re-bind", "Reusing"]
|
|
28
44
|
},
|
|
29
45
|
"skip": {
|
|
@@ -36,5 +52,18 @@
|
|
|
36
52
|
"packages/cli/scripts/install.sh",
|
|
37
53
|
"packages/cli/scripts/install.ps1",
|
|
38
54
|
"packages/core/src/detect.ts"
|
|
39
|
-
]
|
|
55
|
+
],
|
|
56
|
+
"$advisory_comment": "Strings below are advisory — not enforced by the lint, but checked by `existing-install-actions.test.ts` to make sure detect.ts's two rebind variants stay distinguishable from each other.",
|
|
57
|
+
"advisory": {
|
|
58
|
+
"rebind_soft": {
|
|
59
|
+
"$comment": "1.0.17+ : detect.ts must include this exact phrase so users see the soft-rebind keeps the same instance_id.",
|
|
60
|
+
"must_appear_in": ["packages/core/src/detect.ts"],
|
|
61
|
+
"keywords": ["keep key, refresh binding", "same instance"]
|
|
62
|
+
},
|
|
63
|
+
"rebind_destructive": {
|
|
64
|
+
"$comment": "1.0.17+ : detect.ts must include this phrase so the destructive rebind is unmistakable.",
|
|
65
|
+
"must_appear_in": ["packages/core/src/detect.ts"],
|
|
66
|
+
"keywords": ["NEW key", "NEW instance"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
40
69
|
}
|
|
@@ -23,18 +23,24 @@
|
|
|
23
23
|
|
|
24
24
|
# ── Parse-time checks ────────────────────────────────────────
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# Pester 5 runs file-level code in the discovery phase but does NOT
|
|
27
|
+
# propagate vars defined there into It blocks at run phase. Compute
|
|
28
|
+
# the path inside each test (or via BeforeAll) so $scriptPath is
|
|
29
|
+
# always non-null when the assertions execute.
|
|
30
|
+
BeforeAll {
|
|
31
|
+
$script:ScriptPath = Join-Path $PSScriptRoot "install.ps1"
|
|
32
|
+
}
|
|
27
33
|
|
|
28
34
|
Describe "install.ps1 parse-time integrity" {
|
|
29
35
|
It "exists at the expected path" {
|
|
30
|
-
Test-Path $
|
|
36
|
+
Test-Path $script:ScriptPath | Should -Be $true
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
It "parses without syntax errors (AST round-trip)" {
|
|
34
40
|
$tokens = $null
|
|
35
41
|
$errors = $null
|
|
36
42
|
[System.Management.Automation.Language.Parser]::ParseFile(
|
|
37
|
-
$
|
|
43
|
+
$script:ScriptPath,
|
|
38
44
|
[ref]$tokens,
|
|
39
45
|
[ref]$errors
|
|
40
46
|
) | Out-Null
|
|
@@ -45,7 +51,7 @@ Describe "install.ps1 parse-time integrity" {
|
|
|
45
51
|
$tokens = $null
|
|
46
52
|
$errors = $null
|
|
47
53
|
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
48
|
-
$
|
|
54
|
+
$script:ScriptPath,
|
|
49
55
|
[ref]$tokens,
|
|
50
56
|
[ref]$errors
|
|
51
57
|
)
|
|
@@ -55,6 +61,9 @@ Describe "install.ps1 parse-time integrity" {
|
|
|
55
61
|
$node -is [System.Management.Automation.Language.FunctionDefinitionAst]
|
|
56
62
|
}, $true) | ForEach-Object { $_.Name }
|
|
57
63
|
|
|
64
|
+
# CLI >= 1.0.16: Install-DeviceAgentSuite has been removed —
|
|
65
|
+
# the device suite installs lazily on the first
|
|
66
|
+
# `beeos device attach` (see core/device-setup.ts).
|
|
58
67
|
$expected = @(
|
|
59
68
|
"Write-BeeInfo",
|
|
60
69
|
"Write-BeeOk",
|
|
@@ -66,7 +75,6 @@ Describe "install.ps1 parse-time integrity" {
|
|
|
66
75
|
"Show-InstallHints",
|
|
67
76
|
"Send-Telemetry",
|
|
68
77
|
"Read-ExistingInstallAction",
|
|
69
|
-
"Install-DeviceAgentSuite",
|
|
70
78
|
"Invoke-BeeosCli",
|
|
71
79
|
# P2-8: PSCore + RunAs auto-elevation helpers.
|
|
72
80
|
"Test-IsAdministrator",
|
|
@@ -83,7 +91,7 @@ Describe "install.ps1 parse-time integrity" {
|
|
|
83
91
|
|
|
84
92
|
Describe "install.ps1 platform guard (P2-8)" {
|
|
85
93
|
It "contains the non-Windows early-exit branch" {
|
|
86
|
-
$content = Get-Content -Raw -Path $
|
|
94
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
87
95
|
# Look for the marker comment AND the exit branch so a
|
|
88
96
|
# superficial rename of the variable still trips the test.
|
|
89
97
|
$content | Should -Match "This installer is Windows-only"
|
|
@@ -91,12 +99,12 @@ Describe "install.ps1 platform guard (P2-8)" {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
It "contains a UAC auto-elevation branch" {
|
|
94
|
-
$content = Get-Content -Raw -Path $
|
|
102
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
95
103
|
$content | Should -Match "Start-Process .* -Verb RunAs"
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
It "honours BEEOS_NO_AUTO_ELEVATE=1 as an opt-out" {
|
|
99
|
-
$content = Get-Content -Raw -Path $
|
|
107
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
100
108
|
$content | Should -Match "BEEOS_NO_AUTO_ELEVATE"
|
|
101
109
|
}
|
|
102
110
|
}
|
|
@@ -106,13 +114,16 @@ Describe "install.ps1 stdin / TTY handover (P1-J)" {
|
|
|
106
114
|
# prompts read from the exhausted pipe and silently take defaults.
|
|
107
115
|
# Mirrors install.sh's `</dev/tty` reattachment.
|
|
108
116
|
It "detects [Console]::IsInputRedirected" {
|
|
109
|
-
$content = Get-Content -Raw -Path $
|
|
117
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
110
118
|
$content | Should -Match "\[Console\]::IsInputRedirected"
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
It "uses Start-Process with -NoNewWindow when stdin is redirected" {
|
|
114
|
-
$content = Get-Content -Raw -Path $
|
|
115
|
-
|
|
122
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
123
|
+
# The Start-Process invocation in install.ps1 uses backtick line
|
|
124
|
+
# continuation, so `-NoNewWindow` lives on a separate line from
|
|
125
|
+
# `Start-Process`. `(?s)` enables dotall so `.` matches newlines.
|
|
126
|
+
$content | Should -Match "(?s)Start-Process .*?-NoNewWindow"
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
129
|
|
|
@@ -121,25 +132,29 @@ Describe "install.ps1 stderr log file (P1-I)" {
|
|
|
121
132
|
# MUST land in a forensic file instead of `2>$null`. The lint
|
|
122
133
|
# below is structural — assert the variable + the redirect appear.
|
|
123
134
|
It "declares a `$BeeosInstallLog` variable" {
|
|
124
|
-
$content = Get-Content -Raw -Path $
|
|
135
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
125
136
|
$content | Should -Match "BeeosInstallLog"
|
|
126
137
|
}
|
|
127
138
|
|
|
128
139
|
It "redirects winget stderr to the install log" {
|
|
129
|
-
$content = Get-Content -Raw -Path $
|
|
140
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
130
141
|
# `2>>$BeeosInstallLog` is the canonical append-redirect syntax;
|
|
131
142
|
# `2>$null` here would be a regression to the silent
|
|
132
143
|
# behaviour the review called out.
|
|
133
|
-
|
|
144
|
+
# Use a single-quoted string so `$BeeosInstallLog` is NOT
|
|
145
|
+
# subject to PowerShell variable interpolation; the .NET regex
|
|
146
|
+
# engine still treats `\r` / `\n` as CR / LF and `\$` as a
|
|
147
|
+
# literal `$`.
|
|
148
|
+
$content | Should -Match 'winget install [^\r\n]+2>>\$BeeosInstallLog'
|
|
134
149
|
}
|
|
135
150
|
|
|
136
151
|
It "redirects choco stderr to the install log" {
|
|
137
|
-
$content = Get-Content -Raw -Path $
|
|
138
|
-
$content | Should -Match
|
|
152
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
153
|
+
$content | Should -Match 'choco install [^\r\n]+2>>\$BeeosInstallLog'
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
It "Show-InstallHints surfaces the log path on failure" {
|
|
142
|
-
$content = Get-Content -Raw -Path $
|
|
157
|
+
$content = Get-Content -Raw -Path $script:ScriptPath
|
|
143
158
|
$content | Should -Match "Installer log:"
|
|
144
159
|
}
|
|
145
160
|
}
|
|
@@ -164,6 +179,6 @@ Describe "install.ps1 dry-run hook" {
|
|
|
164
179
|
# Dot-source so we exercise the param-block + helper definitions
|
|
165
180
|
# without invoking the main install body. The dry-run hook
|
|
166
181
|
# `return`s before any state-changing call.
|
|
167
|
-
{ . $
|
|
182
|
+
{ . $script:ScriptPath } | Should -Not -Throw
|
|
168
183
|
}
|
|
169
184
|
}
|
package/scripts/install.ps1
CHANGED
|
@@ -23,16 +23,24 @@
|
|
|
23
23
|
bootstrap requests).
|
|
24
24
|
$env:BEEOS_DASHBOARD_URL Dashboard base (OAuth + bind redirect).
|
|
25
25
|
$env:BEEOS_DEVICE_AGENT_VERSION Pin @beeos-ai/device-agent semver.
|
|
26
|
-
|
|
26
|
+
NOT consumed by this script
|
|
27
|
+
(CLI >= 1.0.16 lazy-installs the
|
|
28
|
+
device suite on the first
|
|
29
|
+
`beeos device attach`); export it
|
|
30
|
+
in the shell that runs `beeos
|
|
31
|
+
device attach` instead.
|
|
32
|
+
$env:BEEOS_MCP_SERVER_VERSION Same, for @beeos-ai/device-mcp-server.
|
|
27
33
|
$env:BEEOS_USE_NPX = "1" Power-user escape hatch: use `npx`
|
|
28
34
|
for a throwaway install. `beeos`
|
|
29
35
|
will NOT persist on PATH after
|
|
30
36
|
the script exits. Default
|
|
31
37
|
behaviour (since CLI 1.0.11) is
|
|
32
38
|
always `npm install -g` so the
|
|
33
|
-
CLI
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
CLI is globally available for
|
|
40
|
+
follow-up commands. The device
|
|
41
|
+
suite is NOT eagerly installed
|
|
42
|
+
(CLI >= 1.0.16); see
|
|
43
|
+
web/packages/core/src/device-setup.ts.
|
|
36
44
|
|
|
37
45
|
.EXAMPLE
|
|
38
46
|
# One-liner (PowerShell 5+):
|
|
@@ -47,12 +55,10 @@
|
|
|
47
55
|
.\install.ps1 -Device
|
|
48
56
|
|
|
49
57
|
.EXAMPLE
|
|
50
|
-
# Bare-metal one-shot staging install (CLI >= 1.0.
|
|
58
|
+
# Bare-metal one-shot staging install (CLI >= 1.0.16):
|
|
51
59
|
$env:BEEOS_API_URL = "https://public-api-staging.beeos.ai"
|
|
52
60
|
$env:BEEOS_AGENT_GATEWAY_URL = "https://agent-gw-staging.beeos.ai"
|
|
53
61
|
$env:BEEOS_DASHBOARD_URL = "https://beeos-staging-web.vercel.app"
|
|
54
|
-
$env:BEEOS_DEVICE_AGENT_VERSION = "0.4.2"
|
|
55
|
-
$env:BEEOS_MCP_SERVER_VERSION = "0.2.3"
|
|
56
62
|
irm https://beeos.ai/install.ps1 | iex
|
|
57
63
|
#>
|
|
58
64
|
param(
|
|
@@ -102,18 +108,11 @@ if (-not $script:IsWindowsHost) {
|
|
|
102
108
|
|
|
103
109
|
$CliPackage = if ($Version -eq "latest") { "@beeos-ai/cli@latest" } else { "@beeos-ai/cli@$Version" }
|
|
104
110
|
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# pins the CLI) — both packages follow their own semver. Override via
|
|
111
|
-
# $env:BEEOS_DEVICE_AGENT_VERSION / $env:BEEOS_MCP_SERVER_VERSION when you
|
|
112
|
-
# need to pin a specific build (e.g. for a staging smoke test).
|
|
113
|
-
$DeviceAgentVersion = if ($env:BEEOS_DEVICE_AGENT_VERSION) { $env:BEEOS_DEVICE_AGENT_VERSION } else { "latest" }
|
|
114
|
-
$McpServerVersion = if ($env:BEEOS_MCP_SERVER_VERSION) { $env:BEEOS_MCP_SERVER_VERSION } else { "latest" }
|
|
115
|
-
$DeviceAgentPackage = "@beeos-ai/device-agent@$DeviceAgentVersion"
|
|
116
|
-
$McpServerPackage = "@beeos-ai/device-mcp-server@$McpServerVersion"
|
|
111
|
+
# `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` are NOT
|
|
112
|
+
# installed here. CLI >= 1.0.16 installs them lazily on the first
|
|
113
|
+
# `beeos device attach` via `ensureDeviceAgent` in
|
|
114
|
+
# `web/packages/core/src/device-setup.ts`. OpenClaw-only users never
|
|
115
|
+
# download the device suite at all.
|
|
117
116
|
|
|
118
117
|
# Telemetry endpoint (override with $env:BEEOS_API_URL for staging).
|
|
119
118
|
if (-not $env:BEEOS_API_URL -or $env:BEEOS_API_URL.Length -eq 0) {
|
|
@@ -479,39 +478,6 @@ function Read-ExistingInstallAction {
|
|
|
479
478
|
}
|
|
480
479
|
}
|
|
481
480
|
|
|
482
|
-
# ── Device-agent sibling suite installer ─────────────────────
|
|
483
|
-
#
|
|
484
|
-
# Installs `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` globally
|
|
485
|
-
# so the very first `beeos device attach` on a clean machine does not need
|
|
486
|
-
# to fetch them separately. `@beeos-ai/device-common` is pulled
|
|
487
|
-
# transitively as a `dependencies` entry of both — no separate install
|
|
488
|
-
# needed.
|
|
489
|
-
#
|
|
490
|
-
# Failure here is NEVER fatal: the CLI itself was already installed
|
|
491
|
-
# successfully, and `beeos device attach` will prompt to install missing
|
|
492
|
-
# pieces via ensureDeviceAgent on first use. We just emit a WARN with the
|
|
493
|
-
# manual recovery command.
|
|
494
|
-
function Install-DeviceAgentSuite {
|
|
495
|
-
$npmPath = Get-Command npm -ErrorAction SilentlyContinue
|
|
496
|
-
if (-not $npmPath) {
|
|
497
|
-
Write-BeeWarn "npm not available — skipping device-agent suite install."
|
|
498
|
-
Write-BeeWarn "Manual fix: install Node + run ``npm i -g $DeviceAgentPackage $McpServerPackage``."
|
|
499
|
-
return
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
Write-BeeInfo "Installing device-agent suite ($DeviceAgentPackage, $McpServerPackage)..."
|
|
503
|
-
& npm install -g $DeviceAgentPackage $McpServerPackage
|
|
504
|
-
if ($LASTEXITCODE -eq 0) {
|
|
505
|
-
Write-BeeOk "device-agent suite installed."
|
|
506
|
-
} else {
|
|
507
|
-
Write-BeeWarn "Failed to install device-agent suite — ``beeos device attach`` will prompt for it on first use."
|
|
508
|
-
Write-BeeWarn "Manual fix: npm i -g $DeviceAgentPackage $McpServerPackage"
|
|
509
|
-
# Reset $LASTEXITCODE so subsequent `if ($LASTEXITCODE -ne 0)` checks
|
|
510
|
-
# in callers don't trip on this non-fatal failure.
|
|
511
|
-
$global:LASTEXITCODE = 0
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
481
|
# ── Run CLI ──────────────────────────────────────────────────
|
|
516
482
|
|
|
517
483
|
function Invoke-BeeosCli {
|
|
@@ -547,9 +513,7 @@ function Invoke-BeeosCli {
|
|
|
547
513
|
if ($LASTEXITCODE -ne 0) {
|
|
548
514
|
# P0-A of the install-link review: fail-after-bootstrap signal.
|
|
549
515
|
# The `install.bootstrap.cli_installed` event is only emitted
|
|
550
|
-
# AFTER npm
|
|
551
|
-
# failures are recorded as a distinct outcome (not as silent
|
|
552
|
-
# post-success).
|
|
516
|
+
# AFTER npm reports success.
|
|
553
517
|
Send-Telemetry -Event "install.bootstrap.npm_fail" -ErrorCode "npm_install_cli_failed" -Success $false
|
|
554
518
|
Write-BeeError "npm install -g $CliPackage failed."
|
|
555
519
|
Write-BeeError ""
|
|
@@ -559,7 +523,10 @@ function Invoke-BeeosCli {
|
|
|
559
523
|
Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
|
|
560
524
|
exit 1
|
|
561
525
|
}
|
|
562
|
-
|
|
526
|
+
# Device-agent suite is intentionally NOT installed here; `beeos
|
|
527
|
+
# device attach` will auto-install it on first use via
|
|
528
|
+
# `ensureDeviceAgent` (see `web/packages/core/src/device-setup.ts`).
|
|
529
|
+
# OpenClaw-only users never download those ~20MB.
|
|
563
530
|
Send-Telemetry -Event "install.bootstrap.cli_installed"
|
|
564
531
|
|
|
565
532
|
# P1-J of the install-link review: when the user invokes the
|
|
@@ -662,7 +629,10 @@ if ($beeosCmd) {
|
|
|
662
629
|
Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
|
|
663
630
|
exit 1
|
|
664
631
|
}
|
|
665
|
-
|
|
632
|
+
# Device suite is NOT upgraded here. `beeos device upgrade`
|
|
633
|
+
# is the canonical refresh path (it also restarts the
|
|
634
|
+
# running fleet); the install-script upgrade prompt only
|
|
635
|
+
# refreshes the CLI itself.
|
|
666
636
|
}
|
|
667
637
|
"rerun" {
|
|
668
638
|
Write-BeeInfo "Reusing existing install."
|
package/scripts/install.sh
CHANGED
|
@@ -29,24 +29,30 @@
|
|
|
29
29
|
# bootstrap requests).
|
|
30
30
|
# BEEOS_DASHBOARD_URL Dashboard base (OAuth + bind redirect).
|
|
31
31
|
# BEEOS_DEVICE_AGENT_VERSION Pin @beeos-ai/device-agent semver.
|
|
32
|
-
#
|
|
32
|
+
# NOT consumed by this script (CLI ≥ 1.0.16
|
|
33
|
+
# installs the device suite lazily on the
|
|
34
|
+
# first `beeos device attach`); export it
|
|
35
|
+
# in the shell that runs `beeos device
|
|
36
|
+
# attach` instead.
|
|
37
|
+
# BEEOS_MCP_SERVER_VERSION Same as above, for @beeos-ai/device-mcp-server.
|
|
33
38
|
# BEEOS_USE_NPX=1 (Power-user escape hatch) Use `npx` for a
|
|
34
39
|
# throwaway install. `beeos` will NOT
|
|
35
40
|
# persist on PATH after the script exits.
|
|
36
41
|
# Default behaviour (since CLI 1.0.11) is
|
|
37
|
-
# always `npm install -g` so the CLI
|
|
38
|
-
#
|
|
39
|
-
#
|
|
42
|
+
# always `npm install -g` so the CLI is
|
|
43
|
+
# globally available for follow-up commands.
|
|
44
|
+
# The device-agent suite is no longer
|
|
45
|
+
# eagerly installed (CLI ≥ 1.0.16); see
|
|
46
|
+
# `web/packages/core/src/device-setup.ts`.
|
|
40
47
|
#
|
|
41
|
-
# Example: bare-metal one-shot staging install
|
|
42
|
-
#
|
|
48
|
+
# Example: bare-metal one-shot staging install (no device-agent pin —
|
|
49
|
+
# add `BEEOS_DEVICE_AGENT_VERSION=...` to the shell when you later run
|
|
50
|
+
# `beeos device attach` if you need to pin a specific build):
|
|
43
51
|
#
|
|
44
52
|
# BEEOS_API_URL=https://public-api-staging.beeos.ai \
|
|
45
53
|
# BEEOS_AGENT_GATEWAY_URL=https://agent-gw-staging.beeos.ai \
|
|
46
54
|
# BEEOS_DASHBOARD_URL=https://beeos-staging-web.vercel.app \
|
|
47
|
-
#
|
|
48
|
-
# BEEOS_MCP_SERVER_VERSION=0.2.3 \
|
|
49
|
-
# curl -fsSL https://beeos.ai/install | bash -s -- --version 1.0.11
|
|
55
|
+
# curl -fsSL https://beeos.ai/install | bash -s -- --version 1.0.16
|
|
50
56
|
|
|
51
57
|
set -euo pipefail
|
|
52
58
|
|
|
@@ -59,16 +65,11 @@ RED="\033[31m"
|
|
|
59
65
|
CYAN="\033[36m"
|
|
60
66
|
|
|
61
67
|
CLI_PACKAGE="@beeos-ai/cli@latest"
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# only pins the CLI) — both packages follow their own semver. Override via
|
|
68
|
-
# BEEOS_DEVICE_AGENT_VERSION / BEEOS_MCP_SERVER_VERSION when you need to
|
|
69
|
-
# pin a specific build (e.g. for a staging smoke test).
|
|
70
|
-
DEVICE_AGENT_PACKAGE="@beeos-ai/device-agent@${BEEOS_DEVICE_AGENT_VERSION:-latest}"
|
|
71
|
-
MCP_SERVER_PACKAGE="@beeos-ai/device-mcp-server@${BEEOS_MCP_SERVER_VERSION:-latest}"
|
|
68
|
+
# `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` are NOT
|
|
69
|
+
# installed here. CLI ≥ 1.0.16 installs them lazily on the first
|
|
70
|
+
# `beeos device attach` via `ensureDeviceAgent` in
|
|
71
|
+
# `web/packages/core/src/device-setup.ts`. OpenClaw-only users never
|
|
72
|
+
# download the device suite at all.
|
|
72
73
|
MIN_NODE_VERSION=18
|
|
73
74
|
NVM_VERSION="v0.40.1"
|
|
74
75
|
FNM_INSTALL_URL="https://fnm.vercel.app/install"
|
|
@@ -479,35 +480,6 @@ prompt_existing_install_action() {
|
|
|
479
480
|
esac
|
|
480
481
|
}
|
|
481
482
|
|
|
482
|
-
# ── Device-agent sibling suite installer ─────────────────────
|
|
483
|
-
#
|
|
484
|
-
# Installs `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` globally
|
|
485
|
-
# so the very first `beeos device attach` on a clean machine does not need
|
|
486
|
-
# to fetch them separately. `@beeos-ai/device-common` is pulled
|
|
487
|
-
# transitively as a `dependencies` entry of both — no separate install
|
|
488
|
-
# needed.
|
|
489
|
-
#
|
|
490
|
-
# Failure here is NEVER fatal: the CLI itself was already installed
|
|
491
|
-
# successfully, and `beeos device attach` will prompt to install missing
|
|
492
|
-
# pieces via ensureDeviceAgent on first use. We just emit a WARN with the
|
|
493
|
-
# manual recovery command.
|
|
494
|
-
install_device_agent_suite() {
|
|
495
|
-
if ! command -v npm &>/dev/null; then
|
|
496
|
-
warn "npm not available — skipping device-agent suite install."
|
|
497
|
-
warn "Manual fix: install Node + run \`npm i -g ${DEVICE_AGENT_PACKAGE} ${MCP_SERVER_PACKAGE}\`."
|
|
498
|
-
return 0
|
|
499
|
-
fi
|
|
500
|
-
|
|
501
|
-
info "Installing device-agent suite (${DEVICE_AGENT_PACKAGE}, ${MCP_SERVER_PACKAGE})..."
|
|
502
|
-
if npm install -g "$DEVICE_AGENT_PACKAGE" "$MCP_SERVER_PACKAGE"; then
|
|
503
|
-
success "device-agent suite installed."
|
|
504
|
-
else
|
|
505
|
-
warn "Failed to install device-agent suite — \`beeos device attach\` will prompt for it on first use."
|
|
506
|
-
warn "Manual fix: npm i -g ${DEVICE_AGENT_PACKAGE} ${MCP_SERVER_PACKAGE}"
|
|
507
|
-
fi
|
|
508
|
-
return 0
|
|
509
|
-
}
|
|
510
|
-
|
|
511
483
|
# ── Main ─────────────────────────────────────────────────────
|
|
512
484
|
|
|
513
485
|
run_cli() {
|
|
@@ -528,16 +500,11 @@ run_cli() {
|
|
|
528
500
|
|
|
529
501
|
# ALWAYS install globally. We previously branched to `npx --yes` when
|
|
530
502
|
# available so curl|bash users got a throwaway install, but that
|
|
531
|
-
# silently broke the post-install UX:
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
#
|
|
536
|
-
# persisted, leaving the user stuck.
|
|
537
|
-
# - The device-agent suite was deliberately skipped on the npx
|
|
538
|
-
# branch, so even if the user re-ran via `npm i -g`, the very
|
|
539
|
-
# next `beeos device attach` had to bootstrap device-agent
|
|
540
|
-
# itself.
|
|
503
|
+
# silently broke the post-install UX: `beeos` was not on PATH after
|
|
504
|
+
# the script exited, so the user's next step (`beeos device attach`)
|
|
505
|
+
# failed with `command not found` and the obvious recovery (re-run
|
|
506
|
+
# the one-liner) hit the "existing install" prompt because some
|
|
507
|
+
# state in ~/.beeos persisted, leaving the user stuck.
|
|
541
508
|
# Power users who explicitly want the old throwaway behaviour can set
|
|
542
509
|
# `BEEOS_USE_NPX=1`. Default is global install.
|
|
543
510
|
if [[ "${BEEOS_USE_NPX:-}" == "1" ]] && command -v npx &>/dev/null; then
|
|
@@ -567,10 +534,13 @@ run_cli() {
|
|
|
567
534
|
error " use a node version manager (nvm / fnm) or sudo"
|
|
568
535
|
exit 1
|
|
569
536
|
fi
|
|
570
|
-
|
|
571
|
-
#
|
|
572
|
-
#
|
|
573
|
-
#
|
|
537
|
+
# NPM succeeded — CLI is now on PATH. The device-agent suite is
|
|
538
|
+
# deliberately NOT installed here; `beeos device attach` will
|
|
539
|
+
# auto-install it on first use via `ensureDeviceAgent` (see
|
|
540
|
+
# `web/packages/core/src/device-setup.ts`). OpenClaw-only users
|
|
541
|
+
# never download those ~20MB. Fire the "CLI is reachable on PATH"
|
|
542
|
+
# event before exec — exec replaces our process so this is the
|
|
543
|
+
# last chance.
|
|
574
544
|
send_telemetry "install.bootstrap.cli_installed"
|
|
575
545
|
exec beeos "$subcmd" "$@" <"$stdin_src"
|
|
576
546
|
}
|
|
@@ -634,7 +604,10 @@ main() {
|
|
|
634
604
|
error " use a node version manager (nvm / fnm) or sudo"
|
|
635
605
|
exit 1
|
|
636
606
|
fi
|
|
637
|
-
|
|
607
|
+
# Device-agent suite is intentionally NOT upgraded here.
|
|
608
|
+
# `beeos device upgrade` is the canonical refresh path (it
|
|
609
|
+
# also restarts the running fleet); the install-script
|
|
610
|
+
# upgrade prompt only refreshes the CLI itself.
|
|
638
611
|
fi
|
|
639
612
|
;;
|
|
640
613
|
rerun)
|