@beeos-ai/cli 1.0.11 → 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.
Files changed (2) hide show
  1. package/dist/index.js +68 -17
  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)}`);
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 {
@@ -4658,7 +4688,11 @@ async function registerVideoBridge(_mgr, reporter, params) {
4658
4688
  if (!params.withVideo) return { mode: "none" };
4659
4689
  const agentGatewayUrl = resolveAgentGatewayUrl(params.cfg);
4660
4690
  if (params.vncHost) {
4661
- const binary2 = await vncBridgeRuntime.ensureInstalled(reporter);
4691
+ const binary2 = await ensureBridgeBinaryDegraded(
4692
+ vncBridgeRuntime.ensureInstalled.bind(vncBridgeRuntime),
4693
+ reporter,
4694
+ "vnc-bridge"
4695
+ );
4662
4696
  if (!binary2) {
4663
4697
  console.error(
4664
4698
  ` vnc-bridge binary unavailable \u2014 ${params.serial} will run without VNC streaming.`
@@ -4678,16 +4712,33 @@ async function registerVideoBridge(_mgr, reporter, params) {
4678
4712
  await installService(_mgr, spec);
4679
4713
  return { mode: "vnc" };
4680
4714
  }
4681
- const binary = await scrcpyBridgeRuntime.ensureInstalled(reporter);
4715
+ const binary = await ensureBridgeBinaryDegraded(
4716
+ scrcpyBridgeRuntime.ensureInstalled.bind(scrcpyBridgeRuntime),
4717
+ reporter,
4718
+ "scrcpy-bridge"
4719
+ );
4682
4720
  if (!binary) {
4683
4721
  console.error(
4684
4722
  ` 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.`
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.`
4686
4727
  );
4687
4728
  return { mode: "none" };
4688
4729
  }
4689
4730
  return { mode: "scrcpy" };
4690
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
+ }
4691
4742
  async function ensureAdbAvailable(reporter) {
4692
4743
  const existing = await findAdb();
4693
4744
  if (existing) return;
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.12",
4
4
  "type": "module",
5
5
  "description": "BeeOS CLI — run AI agents from your desktop",
6
6
  "bin": {