@beeos-ai/cli 1.0.11 → 1.0.13

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.
Files changed (2) hide show
  1. package/dist/index.js +73 -26
  2. package/package.json +1 -1
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)}`);
1161
- 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
- }
1198
+ const data = await fetchArchiveBuffer(cfg, url, progress);
1199
+ if (!data)
1168
1200
  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 {
@@ -2651,13 +2681,13 @@ function inferInitChoices(state) {
2651
2681
  if (state.hasIdentity && state.binding) {
2652
2682
  return {
2653
2683
  defaultDecision: "upgrade",
2654
- options: ["upgrade", "rebind-keep-key", "rebind-new-key", "skip"]
2684
+ options: ["upgrade", "rebind-new-key", "skip"]
2655
2685
  };
2656
2686
  }
2657
2687
  if (state.hasIdentity) {
2658
2688
  return {
2659
- defaultDecision: "rebind-keep-key",
2660
- options: ["upgrade", "rebind-keep-key", "rebind-new-key", "skip"]
2689
+ defaultDecision: "upgrade",
2690
+ options: ["upgrade", "rebind-new-key", "skip"]
2661
2691
  };
2662
2692
  }
2663
2693
  return { defaultDecision: "fresh", options: ["fresh", "skip"] };
@@ -2667,9 +2697,7 @@ function initDecisionLabel(decision) {
2667
2697
  case "fresh":
2668
2698
  return "Install + bind";
2669
2699
  case "upgrade":
2670
- return "Upgrade CLI & agents (keep binding)";
2671
- case "rebind-keep-key":
2672
- return "Re-bind (keep existing Ed25519 key)";
2700
+ return "Upgrade CLI & agents (keep binding & key)";
2673
2701
  case "rebind-new-key":
2674
2702
  return "Re-bind (rotate Ed25519 key)";
2675
2703
  case "skip":
@@ -4658,7 +4686,11 @@ async function registerVideoBridge(_mgr, reporter, params) {
4658
4686
  if (!params.withVideo) return { mode: "none" };
4659
4687
  const agentGatewayUrl = resolveAgentGatewayUrl(params.cfg);
4660
4688
  if (params.vncHost) {
4661
- const binary2 = await vncBridgeRuntime.ensureInstalled(reporter);
4689
+ const binary2 = await ensureBridgeBinaryDegraded(
4690
+ vncBridgeRuntime.ensureInstalled.bind(vncBridgeRuntime),
4691
+ reporter,
4692
+ "vnc-bridge"
4693
+ );
4662
4694
  if (!binary2) {
4663
4695
  console.error(
4664
4696
  ` vnc-bridge binary unavailable \u2014 ${params.serial} will run without VNC streaming.`
@@ -4678,16 +4710,33 @@ async function registerVideoBridge(_mgr, reporter, params) {
4678
4710
  await installService(_mgr, spec);
4679
4711
  return { mode: "vnc" };
4680
4712
  }
4681
- const binary = await scrcpyBridgeRuntime.ensureInstalled(reporter);
4713
+ const binary = await ensureBridgeBinaryDegraded(
4714
+ scrcpyBridgeRuntime.ensureInstalled.bind(scrcpyBridgeRuntime),
4715
+ reporter,
4716
+ "scrcpy-bridge"
4717
+ );
4682
4718
  if (!binary) {
4683
4719
  console.error(
4684
4720
  ` scrcpy-bridge binary unavailable \u2014 ${params.serial} will run without WebRTC video.
4685
- Install from https://github.com/beeos-ai/scrcpy-bridge or set BEEOS_SCRCPY_BRIDGE_BIN.`
4721
+ ACP control + screenshots still work. To recover live video later:
4722
+ \u2022 re-run: beeos device attach --serial ${params.serial} (will retry the download)
4723
+ \u2022 or download from https://github.com/beeos-ai/scrcpy-bridge/releases
4724
+ and set BEEOS_SCRCPY_BRIDGE_BIN to its absolute path.`
4686
4725
  );
4687
4726
  return { mode: "none" };
4688
4727
  }
4689
4728
  return { mode: "scrcpy" };
4690
4729
  }
4730
+ async function ensureBridgeBinaryDegraded(fn, reporter, label) {
4731
+ try {
4732
+ return await fn(reporter);
4733
+ } catch (e) {
4734
+ console.error(
4735
+ ` ${label} install threw unexpectedly (${String(e)}) \u2014 degrading to no-video mode.`
4736
+ );
4737
+ return null;
4738
+ }
4739
+ }
4691
4740
  async function ensureAdbAvailable(reporter) {
4692
4741
  const existing = await findAdb();
4693
4742
  if (existing) return;
@@ -5746,8 +5795,6 @@ async function run7(options) {
5746
5795
  }
5747
5796
  if (decision === "rebind-new-key") {
5748
5797
  await rotateIdentity();
5749
- }
5750
- if (decision === "rebind-keep-key" || decision === "rebind-new-key") {
5751
5798
  await removeBindingInfo();
5752
5799
  }
5753
5800
  const frameworkId = await decideFramework(state, decision, options);
@@ -5800,7 +5847,7 @@ async function decideFramework(state, decision, options) {
5800
5847
  if (options.framework && options.framework.trim()) {
5801
5848
  return options.framework.trim();
5802
5849
  }
5803
- if (state.binding && (decision === "upgrade" || decision === "rebind-keep-key")) {
5850
+ if (state.binding && decision === "upgrade") {
5804
5851
  const persisted = state.binding.framework?.trim();
5805
5852
  if (persisted) return persisted;
5806
5853
  return defaultFrameworkId();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beeos-ai/cli",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "type": "module",
5
5
  "description": "BeeOS CLI — run AI agents from your desktop",
6
6
  "bin": {