@beeos-ai/cli 1.0.10 → 1.0.12
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 +137 -23
- package/package.json +1 -1
- package/scripts/install.ps1 +41 -23
- package/scripts/install.sh +45 -22
package/dist/index.js
CHANGED
|
@@ -1148,26 +1148,56 @@ function archiveName(cfg, target) {
|
|
|
1148
1148
|
const suffix = target.includes("windows") ? "zip" : "tar.gz";
|
|
1149
1149
|
return `${cfg.name}-${target}.${suffix}`;
|
|
1150
1150
|
}
|
|
1151
|
+
async function fetchArchiveBuffer(cfg, url, progress, maxAttempts = 3) {
|
|
1152
|
+
const p = getPlatformAdapter();
|
|
1153
|
+
let lastError = null;
|
|
1154
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1155
|
+
let resp;
|
|
1156
|
+
try {
|
|
1157
|
+
resp = await p.fetch(url);
|
|
1158
|
+
} catch (e) {
|
|
1159
|
+
lastError = String(e);
|
|
1160
|
+
progress.onStatus(`${cfg.name} download attempt ${attempt}/${maxAttempts} failed: ${lastError}`);
|
|
1161
|
+
if (attempt < maxAttempts) {
|
|
1162
|
+
await new Promise((r) => setTimeout(r, 1e3 * 2 ** (attempt - 1)));
|
|
1163
|
+
}
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
if (!resp.ok) {
|
|
1167
|
+
progress.onStatus(`${cfg.name} download: ${resp.status} ${resp.statusText} (${url})`);
|
|
1168
|
+
if (resp.status === 404) {
|
|
1169
|
+
progress.onStatus(`Hint: the current release may not include the requested target. Try setting $${cfg.binaryEnv} to a locally built binary, ` + (cfg.releaseUrlEnv ? `or $${cfg.releaseUrlEnv} to a different release URL.` : `or check for a newer release.`));
|
|
1170
|
+
}
|
|
1171
|
+
if (resp.status === 404 || resp.status === 403 || resp.status === 401) {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
lastError = `${resp.status} ${resp.statusText}`;
|
|
1175
|
+
if (attempt < maxAttempts) {
|
|
1176
|
+
await new Promise((r) => setTimeout(r, 1e3 * 2 ** (attempt - 1)));
|
|
1177
|
+
}
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
try {
|
|
1181
|
+
return new Uint8Array(await resp.arrayBuffer());
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
lastError = String(e);
|
|
1184
|
+
progress.onStatus(`${cfg.name} stream interrupted on attempt ${attempt}/${maxAttempts}: ${lastError}`);
|
|
1185
|
+
if (attempt < maxAttempts) {
|
|
1186
|
+
await new Promise((r) => setTimeout(r, 1e3 * 2 ** (attempt - 1)));
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
progress.onStatus(`${cfg.name} download gave up after ${maxAttempts} attempts (last: ${lastError ?? "unknown"}).`);
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1151
1193
|
async function downloadManagedBinary(cfg, target, progress) {
|
|
1152
1194
|
const p = getPlatformAdapter();
|
|
1153
1195
|
const base = releaseBaseUrl(cfg);
|
|
1154
1196
|
const url = `${base}/${archiveName(cfg, target)}`;
|
|
1155
1197
|
progress.onStatus(`Downloading ${cfg.name} (${target})...`);
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
resp = await p.fetch(url);
|
|
1159
|
-
} catch (e) {
|
|
1160
|
-
progress.onStatus(`${cfg.name} download failed: ${String(e)}`);
|
|
1198
|
+
const data = await fetchArchiveBuffer(cfg, url, progress);
|
|
1199
|
+
if (!data)
|
|
1161
1200
|
return null;
|
|
1162
|
-
}
|
|
1163
|
-
if (!resp.ok) {
|
|
1164
|
-
progress.onStatus(`${cfg.name} download: ${resp.status} ${resp.statusText} (${url})`);
|
|
1165
|
-
if (resp.status === 404) {
|
|
1166
|
-
progress.onStatus(`Hint: the current release may not include ${target}. Try setting $${cfg.binaryEnv} to a locally built binary, ` + (cfg.releaseUrlEnv ? `or $${cfg.releaseUrlEnv} to a different release URL.` : `or check for a newer release.`));
|
|
1167
|
-
}
|
|
1168
|
-
return null;
|
|
1169
|
-
}
|
|
1170
|
-
const data = new Uint8Array(await resp.arrayBuffer());
|
|
1171
1201
|
const digestUrl = `${url}.sha256`;
|
|
1172
1202
|
let expected = null;
|
|
1173
1203
|
try {
|
|
@@ -4335,6 +4365,8 @@ var init_state = __esm({
|
|
|
4335
4365
|
});
|
|
4336
4366
|
|
|
4337
4367
|
// src/commands/device/attach.ts
|
|
4368
|
+
import { spawn as spawn2 } from "child_process";
|
|
4369
|
+
import readline from "readline";
|
|
4338
4370
|
async function attach(options) {
|
|
4339
4371
|
const cfg = await loadOrCreateConfig();
|
|
4340
4372
|
const reporter = new CliReporter();
|
|
@@ -4418,7 +4450,7 @@ async function attach(options) {
|
|
|
4418
4450
|
throw e;
|
|
4419
4451
|
}
|
|
4420
4452
|
});
|
|
4421
|
-
await maybeNotifyFleetWithHint();
|
|
4453
|
+
await maybeNotifyFleetWithHint(cfg);
|
|
4422
4454
|
}
|
|
4423
4455
|
async function attachAll(cfg, reporter, withVideo, options) {
|
|
4424
4456
|
const devices = await deviceRuntime.listAdbDevices();
|
|
@@ -4493,7 +4525,7 @@ async function attachAll(cfg, reporter, withVideo, options) {
|
|
|
4493
4525
|
await saveDeviceState(state);
|
|
4494
4526
|
console.log(`Attached ${committed.length} device(s); supervision via device-agent fleet.`);
|
|
4495
4527
|
});
|
|
4496
|
-
await maybeNotifyFleetWithHint();
|
|
4528
|
+
await maybeNotifyFleetWithHint(cfg);
|
|
4497
4529
|
}
|
|
4498
4530
|
function commitAttachResults(state, commits) {
|
|
4499
4531
|
const committed = [];
|
|
@@ -4587,15 +4619,80 @@ async function installService(mgr, spec) {
|
|
|
4587
4619
|
maybePrintFallbackBanner(mgr.kind);
|
|
4588
4620
|
await mgr.install(spec);
|
|
4589
4621
|
}
|
|
4590
|
-
async function maybeNotifyFleetWithHint() {
|
|
4622
|
+
async function maybeNotifyFleetWithHint(cfg) {
|
|
4591
4623
|
const outcome = await notifyFleetReloadBestEffort();
|
|
4592
|
-
if (outcome
|
|
4624
|
+
if (outcome !== "not_running") return;
|
|
4625
|
+
if (!shouldOfferFleetEnable()) {
|
|
4626
|
+
printFleetNotRunningHint();
|
|
4627
|
+
return;
|
|
4628
|
+
}
|
|
4629
|
+
console.log("");
|
|
4630
|
+
console.log("device-agent fleet is not running yet. Until you start");
|
|
4631
|
+
console.log("it, the device is recorded in ~/.beeos/devices.json but");
|
|
4632
|
+
console.log("no process is supervising the per-device agent.");
|
|
4633
|
+
console.log("");
|
|
4634
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
4635
|
+
const answer = await new Promise((resolve) => {
|
|
4636
|
+
rl.question("Enable fleet now (registers a launchd service)? [Y/n]: ", (a) => {
|
|
4637
|
+
rl.close();
|
|
4638
|
+
resolve(a);
|
|
4639
|
+
});
|
|
4640
|
+
});
|
|
4641
|
+
const yes = answer.trim() === "" || /^y(es)?$/i.test(answer.trim());
|
|
4642
|
+
if (!yes) {
|
|
4643
|
+
printFleetNotRunningHint();
|
|
4644
|
+
return;
|
|
4645
|
+
}
|
|
4646
|
+
const ok = await runDeviceAgentFleetEnable(cfg);
|
|
4647
|
+
if (!ok) {
|
|
4648
|
+
console.error("");
|
|
4649
|
+
console.error("fleet enable did not complete cleanly. Re-run manually:");
|
|
4650
|
+
console.error(" device-agent fleet enable --agent-gateway-url <URL>");
|
|
4651
|
+
console.error("");
|
|
4652
|
+
printFleetNotRunningHint();
|
|
4653
|
+
return;
|
|
4654
|
+
}
|
|
4655
|
+
console.log("");
|
|
4656
|
+
console.log("fleet enabled \u2014 supervised processes should come up within a few seconds.");
|
|
4657
|
+
console.log("Check status with: device-agent fleet devices");
|
|
4658
|
+
}
|
|
4659
|
+
function shouldOfferFleetEnable(inputs = {
|
|
4660
|
+
platform: process.platform,
|
|
4661
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
4662
|
+
stdoutIsTTY: process.stdout.isTTY,
|
|
4663
|
+
env: process.env
|
|
4664
|
+
}) {
|
|
4665
|
+
if (inputs.platform !== "darwin") return false;
|
|
4666
|
+
if (inputs.env.BEEOS_NO_FLEET_AUTOSTART === "1") return false;
|
|
4667
|
+
if (!inputs.stdinIsTTY) return false;
|
|
4668
|
+
if (!inputs.stdoutIsTTY) return false;
|
|
4669
|
+
return true;
|
|
4670
|
+
}
|
|
4671
|
+
async function runDeviceAgentFleetEnable(cfg) {
|
|
4672
|
+
const agentGatewayUrl = resolveAgentGatewayUrl(cfg);
|
|
4673
|
+
return new Promise((resolve) => {
|
|
4674
|
+
try {
|
|
4675
|
+
const child = spawn2(
|
|
4676
|
+
"device-agent",
|
|
4677
|
+
["fleet", "enable", "--agent-gateway-url", agentGatewayUrl],
|
|
4678
|
+
{ stdio: "inherit" }
|
|
4679
|
+
);
|
|
4680
|
+
child.once("close", (code) => resolve(code === 0));
|
|
4681
|
+
child.once("error", () => resolve(false));
|
|
4682
|
+
} catch {
|
|
4683
|
+
resolve(false);
|
|
4684
|
+
}
|
|
4685
|
+
});
|
|
4593
4686
|
}
|
|
4594
4687
|
async function registerVideoBridge(_mgr, reporter, params) {
|
|
4595
4688
|
if (!params.withVideo) return { mode: "none" };
|
|
4596
4689
|
const agentGatewayUrl = resolveAgentGatewayUrl(params.cfg);
|
|
4597
4690
|
if (params.vncHost) {
|
|
4598
|
-
const binary2 = await
|
|
4691
|
+
const binary2 = await ensureBridgeBinaryDegraded(
|
|
4692
|
+
vncBridgeRuntime.ensureInstalled.bind(vncBridgeRuntime),
|
|
4693
|
+
reporter,
|
|
4694
|
+
"vnc-bridge"
|
|
4695
|
+
);
|
|
4599
4696
|
if (!binary2) {
|
|
4600
4697
|
console.error(
|
|
4601
4698
|
` vnc-bridge binary unavailable \u2014 ${params.serial} will run without VNC streaming.`
|
|
@@ -4615,16 +4712,33 @@ async function registerVideoBridge(_mgr, reporter, params) {
|
|
|
4615
4712
|
await installService(_mgr, spec);
|
|
4616
4713
|
return { mode: "vnc" };
|
|
4617
4714
|
}
|
|
4618
|
-
const binary = await
|
|
4715
|
+
const binary = await ensureBridgeBinaryDegraded(
|
|
4716
|
+
scrcpyBridgeRuntime.ensureInstalled.bind(scrcpyBridgeRuntime),
|
|
4717
|
+
reporter,
|
|
4718
|
+
"scrcpy-bridge"
|
|
4719
|
+
);
|
|
4619
4720
|
if (!binary) {
|
|
4620
4721
|
console.error(
|
|
4621
4722
|
` scrcpy-bridge binary unavailable \u2014 ${params.serial} will run without WebRTC video.
|
|
4622
|
-
|
|
4723
|
+
ACP control + screenshots still work. To recover live video later:
|
|
4724
|
+
\u2022 re-run: beeos device attach --serial ${params.serial} (will retry the download)
|
|
4725
|
+
\u2022 or download from https://github.com/beeos-ai/scrcpy-bridge/releases
|
|
4726
|
+
and set BEEOS_SCRCPY_BRIDGE_BIN to its absolute path.`
|
|
4623
4727
|
);
|
|
4624
4728
|
return { mode: "none" };
|
|
4625
4729
|
}
|
|
4626
4730
|
return { mode: "scrcpy" };
|
|
4627
4731
|
}
|
|
4732
|
+
async function ensureBridgeBinaryDegraded(fn, reporter, label) {
|
|
4733
|
+
try {
|
|
4734
|
+
return await fn(reporter);
|
|
4735
|
+
} catch (e) {
|
|
4736
|
+
console.error(
|
|
4737
|
+
` ${label} install threw unexpectedly (${String(e)}) \u2014 degrading to no-video mode.`
|
|
4738
|
+
);
|
|
4739
|
+
return null;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4628
4742
|
async function ensureAdbAvailable(reporter) {
|
|
4629
4743
|
const existing = await findAdb();
|
|
4630
4744
|
if (existing) return;
|
|
@@ -5388,7 +5502,7 @@ init_device2();
|
|
|
5388
5502
|
|
|
5389
5503
|
// src/commands/init.ts
|
|
5390
5504
|
init_dist();
|
|
5391
|
-
import
|
|
5505
|
+
import readline2 from "readline";
|
|
5392
5506
|
|
|
5393
5507
|
// src/commands/doctor.ts
|
|
5394
5508
|
init_dist();
|
|
@@ -5833,7 +5947,7 @@ function printNextSteps() {
|
|
|
5833
5947
|
console.log("");
|
|
5834
5948
|
}
|
|
5835
5949
|
function prompt(question) {
|
|
5836
|
-
const rl =
|
|
5950
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5837
5951
|
return new Promise((resolve) => {
|
|
5838
5952
|
rl.question(question, (answer) => {
|
|
5839
5953
|
rl.close();
|
package/package.json
CHANGED
package/scripts/install.ps1
CHANGED
|
@@ -24,6 +24,15 @@
|
|
|
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
|
$env:BEEOS_MCP_SERVER_VERSION Pin @beeos-ai/device-mcp-server semver.
|
|
27
|
+
$env:BEEOS_USE_NPX = "1" Power-user escape hatch: use `npx`
|
|
28
|
+
for a throwaway install. `beeos`
|
|
29
|
+
will NOT persist on PATH after
|
|
30
|
+
the script exits. Default
|
|
31
|
+
behaviour (since CLI 1.0.11) is
|
|
32
|
+
always `npm install -g` so the
|
|
33
|
+
CLI plus the device-agent suite
|
|
34
|
+
are both globally available for
|
|
35
|
+
follow-up commands.
|
|
27
36
|
|
|
28
37
|
.EXAMPLE
|
|
29
38
|
# One-liner (PowerShell 5+):
|
|
@@ -38,11 +47,11 @@
|
|
|
38
47
|
.\install.ps1 -Device
|
|
39
48
|
|
|
40
49
|
.EXAMPLE
|
|
41
|
-
# Bare-metal one-shot staging install (CLI >= 1.0.
|
|
50
|
+
# Bare-metal one-shot staging install (CLI >= 1.0.11):
|
|
42
51
|
$env:BEEOS_API_URL = "https://public-api-staging.beeos.ai"
|
|
43
52
|
$env:BEEOS_AGENT_GATEWAY_URL = "https://agent-gw-staging.beeos.ai"
|
|
44
53
|
$env:BEEOS_DASHBOARD_URL = "https://beeos-staging-web.vercel.app"
|
|
45
|
-
$env:BEEOS_DEVICE_AGENT_VERSION = "0.4.
|
|
54
|
+
$env:BEEOS_DEVICE_AGENT_VERSION = "0.4.2"
|
|
46
55
|
$env:BEEOS_MCP_SERVER_VERSION = "0.2.3"
|
|
47
56
|
irm https://beeos.ai/install.ps1 | iex
|
|
48
57
|
#>
|
|
@@ -333,32 +342,41 @@ function Invoke-BeeosCli {
|
|
|
333
342
|
[string[]]$ExtraArgs
|
|
334
343
|
)
|
|
335
344
|
|
|
336
|
-
$npxPath = Get-Command npx -ErrorAction SilentlyContinue
|
|
337
345
|
$args = @($Subcommand)
|
|
338
346
|
if ($ExtraArgs) { $args += $ExtraArgs }
|
|
339
347
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
348
|
+
# ALWAYS install globally. Pre-1.0.11 we branched into `npx --yes`
|
|
349
|
+
# when available, which gave irm|iex users a throwaway install but
|
|
350
|
+
# silently broke their next step (`beeos device attach`) because
|
|
351
|
+
# `beeos` was not on PATH after the script exited. Power users who
|
|
352
|
+
# explicitly want the old throwaway behaviour can set
|
|
353
|
+
# `$env:BEEOS_USE_NPX = "1"`.
|
|
354
|
+
$npxPath = Get-Command npx -ErrorAction SilentlyContinue
|
|
355
|
+
if ($env:BEEOS_USE_NPX -eq "1" -and $npxPath) {
|
|
356
|
+
Write-BeeWarn "BEEOS_USE_NPX=1 — using throwaway npx install (beeos won't persist on PATH)."
|
|
346
357
|
& npx --yes $CliPackage @args
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
$npmPath = Get-Command npm -ErrorAction SilentlyContinue
|
|
362
|
+
if (-not $npmPath) {
|
|
363
|
+
Write-BeeError "npm not found — install Node.js first (this script tries that for you)."
|
|
364
|
+
exit 1
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
Write-BeeInfo "Installing $CliPackage globally..."
|
|
368
|
+
& npm install -g $CliPackage
|
|
369
|
+
if ($LASTEXITCODE -ne 0) {
|
|
370
|
+
Write-BeeError "npm install -g $CliPackage failed."
|
|
371
|
+
Write-BeeError ""
|
|
372
|
+
Write-BeeError "Common fixes:"
|
|
373
|
+
Write-BeeError " - EEXIST on beeos from another package:"
|
|
374
|
+
Write-BeeError " npm uninstall -g beeos # then re-run installer"
|
|
375
|
+
Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
|
|
376
|
+
exit 1
|
|
361
377
|
}
|
|
378
|
+
Install-DeviceAgentSuite
|
|
379
|
+
& beeos @args
|
|
362
380
|
}
|
|
363
381
|
|
|
364
382
|
# ── Main ─────────────────────────────────────────────────────
|
package/scripts/install.sh
CHANGED
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
# BEEOS_DASHBOARD_URL Dashboard base (OAuth + bind redirect).
|
|
31
31
|
# BEEOS_DEVICE_AGENT_VERSION Pin @beeos-ai/device-agent semver.
|
|
32
32
|
# BEEOS_MCP_SERVER_VERSION Pin @beeos-ai/device-mcp-server semver.
|
|
33
|
+
# BEEOS_USE_NPX=1 (Power-user escape hatch) Use `npx` for a
|
|
34
|
+
# throwaway install. `beeos` will NOT
|
|
35
|
+
# persist on PATH after the script exits.
|
|
36
|
+
# Default behaviour (since CLI 1.0.11) is
|
|
37
|
+
# always `npm install -g` so the CLI plus
|
|
38
|
+
# the device-agent suite are both globally
|
|
39
|
+
# available for follow-up commands.
|
|
33
40
|
#
|
|
34
41
|
# Example: bare-metal one-shot staging install, including device-agent
|
|
35
42
|
# version pin and OAuth landing on the staging dashboard:
|
|
@@ -37,9 +44,9 @@
|
|
|
37
44
|
# BEEOS_API_URL=https://public-api-staging.beeos.ai \
|
|
38
45
|
# BEEOS_AGENT_GATEWAY_URL=https://agent-gw-staging.beeos.ai \
|
|
39
46
|
# BEEOS_DASHBOARD_URL=https://beeos-staging-web.vercel.app \
|
|
40
|
-
# BEEOS_DEVICE_AGENT_VERSION=0.4.
|
|
47
|
+
# BEEOS_DEVICE_AGENT_VERSION=0.4.2 \
|
|
41
48
|
# BEEOS_MCP_SERVER_VERSION=0.2.3 \
|
|
42
|
-
# curl -fsSL https://beeos.ai/install | bash -s -- --version 1.0.
|
|
49
|
+
# curl -fsSL https://beeos.ai/install | bash -s -- --version 1.0.11
|
|
43
50
|
|
|
44
51
|
set -euo pipefail
|
|
45
52
|
|
|
@@ -495,28 +502,44 @@ run_cli() {
|
|
|
495
502
|
stdin_src="/dev/tty"
|
|
496
503
|
fi
|
|
497
504
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
505
|
+
# ALWAYS install globally. We previously branched to `npx --yes` when
|
|
506
|
+
# available so curl|bash users got a throwaway install, but that
|
|
507
|
+
# silently broke the post-install UX:
|
|
508
|
+
# - `beeos` was not on PATH after the script exited, so the user's
|
|
509
|
+
# next step (`beeos device attach`) failed with `command not found`
|
|
510
|
+
# and the obvious recovery (re-run the one-liner) hit the
|
|
511
|
+
# "existing install" prompt because some state in ~/.beeos
|
|
512
|
+
# persisted, leaving the user stuck.
|
|
513
|
+
# - The device-agent suite was deliberately skipped on the npx
|
|
514
|
+
# branch, so even if the user re-ran via `npm i -g`, the very
|
|
515
|
+
# next `beeos device attach` had to bootstrap device-agent
|
|
516
|
+
# itself.
|
|
517
|
+
# Power users who explicitly want the old throwaway behaviour can set
|
|
518
|
+
# `BEEOS_USE_NPX=1`. Default is global install.
|
|
519
|
+
if [[ "${BEEOS_USE_NPX:-}" == "1" ]] && command -v npx &>/dev/null; then
|
|
520
|
+
warn "BEEOS_USE_NPX=1 — using throwaway npx install (beeos won't persist on PATH)."
|
|
504
521
|
exec npx --yes "$CLI_PACKAGE" "$subcmd" "$@" <"$stdin_src"
|
|
505
|
-
else
|
|
506
|
-
warn "npx not found, installing globally..."
|
|
507
|
-
if ! npm install -g "$CLI_PACKAGE"; then
|
|
508
|
-
error "npm install -g ${CLI_PACKAGE} failed."
|
|
509
|
-
error ""
|
|
510
|
-
error "Common fixes:"
|
|
511
|
-
error " - EEXIST on /bin/beeos from another package:"
|
|
512
|
-
error " npm uninstall -g beeos # then re-run installer"
|
|
513
|
-
error " - EACCES permission error:"
|
|
514
|
-
error " use a node version manager (nvm / fnm) or sudo"
|
|
515
|
-
exit 1
|
|
516
|
-
fi
|
|
517
|
-
install_device_agent_suite
|
|
518
|
-
exec beeos "$subcmd" "$@" <"$stdin_src"
|
|
519
522
|
fi
|
|
523
|
+
|
|
524
|
+
if ! command -v npm &>/dev/null; then
|
|
525
|
+
error "npm not found — install Node.js first (this script tries that for you;"
|
|
526
|
+
error "see ${BEEOS_INSTALL_LOG} for why auto-install bailed out)."
|
|
527
|
+
exit 1
|
|
528
|
+
fi
|
|
529
|
+
|
|
530
|
+
info "Installing ${CLI_PACKAGE} globally..."
|
|
531
|
+
if ! npm install -g "$CLI_PACKAGE"; then
|
|
532
|
+
error "npm install -g ${CLI_PACKAGE} failed."
|
|
533
|
+
error ""
|
|
534
|
+
error "Common fixes:"
|
|
535
|
+
error " - EEXIST on /bin/beeos from another package:"
|
|
536
|
+
error " npm uninstall -g beeos # then re-run installer"
|
|
537
|
+
error " - EACCES permission error:"
|
|
538
|
+
error " use a node version manager (nvm / fnm) or sudo"
|
|
539
|
+
exit 1
|
|
540
|
+
fi
|
|
541
|
+
install_device_agent_suite
|
|
542
|
+
exec beeos "$subcmd" "$@" <"$stdin_src"
|
|
520
543
|
}
|
|
521
544
|
|
|
522
545
|
main() {
|