@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 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
- let resp;
1157
- try {
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 === "not_running") printFleetNotRunningHint();
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 vncBridgeRuntime.ensureInstalled(reporter);
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 scrcpyBridgeRuntime.ensureInstalled(reporter);
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
- Install from https://github.com/beeos-ai/scrcpy-bridge or set BEEOS_SCRCPY_BRIDGE_BIN.`
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 readline from "readline";
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 = readline.createInterface({ input: process.stdin, output: process.stdout });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beeos-ai/cli",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "type": "module",
5
5
  "description": "BeeOS CLI — run AI agents from your desktop",
6
6
  "bin": {
@@ -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.10):
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.1"
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
- if ($npxPath) {
341
- # Temporary / npx path: deliberately do NOT install the device-agent
342
- # suite globally. The user opted into the throwaway-install flow, so
343
- # we honor that if they later run `beeos device attach`,
344
- # ensureDeviceAgent will guide them through a global install at that
345
- # point.
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
- } else {
348
- Write-BeeWarn "npx not found, installing globally..."
349
- & npm install -g $CliPackage
350
- if ($LASTEXITCODE -ne 0) {
351
- Write-BeeError "npm install -g $CliPackage failed."
352
- Write-BeeError ""
353
- Write-BeeError "Common fixes:"
354
- Write-BeeError " - EEXIST on beeos from another package:"
355
- Write-BeeError " npm uninstall -g beeos # then re-run installer"
356
- Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
357
- exit 1
358
- }
359
- Install-DeviceAgentSuite
360
- & beeos @args
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 ─────────────────────────────────────────────────────
@@ -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.1 \
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.10
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
- if command -v npx &>/dev/null; then
499
- # Temporary / npx path: deliberately do NOT install the device-agent
500
- # suite globally. The user opted into the throwaway-install flow, so
501
- # we honor that if they later run `beeos device attach`,
502
- # ensureDeviceAgent will guide them through a global install at that
503
- # point.
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() {