@beeos-ai/cli 1.0.2 → 1.0.4

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 +238 -131
  2. package/package.json +15 -15
package/dist/index.js CHANGED
@@ -67,17 +67,17 @@ function bindingPath() {
67
67
  const p = getPlatformAdapter();
68
68
  return p.joinPath(beeoHome(), "binding.json");
69
69
  }
70
- function agentDir(agentType) {
70
+ function agentDir(agentFramework) {
71
71
  const p = getPlatformAdapter();
72
- return p.joinPath(beeoHome(), "agents", agentType);
72
+ return p.joinPath(beeoHome(), "agents", agentFramework);
73
73
  }
74
- function agentHome(agentType) {
74
+ function agentHome(agentFramework) {
75
75
  const p = getPlatformAdapter();
76
- return p.joinPath(agentDir(agentType), "home");
76
+ return p.joinPath(agentDir(agentFramework), "home");
77
77
  }
78
- function pidFile(agentType) {
78
+ function pidFile(agentFramework) {
79
79
  const p = getPlatformAdapter();
80
- return p.joinPath(beeoHome(), `${agentType}.pid`);
80
+ return p.joinPath(beeoHome(), `${agentFramework}.pid`);
81
81
  }
82
82
  async function loadOrCreateConfig() {
83
83
  const p = getPlatformAdapter();
@@ -188,41 +188,54 @@ async function loadOrCreateGatewayToken() {
188
188
  }
189
189
  return token;
190
190
  }
191
- async function readPid(agentType) {
191
+ async function readPid(agentFramework) {
192
192
  const p = getPlatformAdapter();
193
- const path2 = pidFile(agentType);
193
+ const path2 = pidFile(agentFramework);
194
194
  if (!await p.exists(path2))
195
195
  return null;
196
196
  const raw = (await p.readFile(path2)).trim();
197
197
  const pid = parseInt(raw, 10);
198
198
  return isNaN(pid) ? null : pid;
199
199
  }
200
- async function writePid(agentType, pid) {
200
+ async function writePid(agentFramework, pid) {
201
201
  const p = getPlatformAdapter();
202
- await p.writeFile(pidFile(agentType), String(pid));
202
+ await p.writeFile(pidFile(agentFramework), String(pid));
203
203
  }
204
- async function removePid(agentType) {
204
+ async function removePid(agentFramework) {
205
205
  const p = getPlatformAdapter();
206
- const path2 = pidFile(agentType);
206
+ const path2 = pidFile(agentFramework);
207
207
  if (await p.exists(path2)) {
208
208
  await p.rm(path2);
209
209
  }
210
210
  }
211
211
 
212
212
  // ../core/dist/identity/keypair.js
213
- import { createHash } from "crypto";
214
213
  import * as ed from "@noble/ed25519";
215
214
  import { sha512 } from "@noble/hashes/sha512";
215
+ import { sha256 } from "@noble/hashes/sha256";
216
+ import { bytesToHex } from "@noble/hashes/utils";
216
217
  ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
218
+ function toBase64(bytes) {
219
+ let binary = "";
220
+ for (let i = 0; i < bytes.length; i++)
221
+ binary += String.fromCharCode(bytes[i]);
222
+ return btoa(binary);
223
+ }
224
+ function fromBase64(b64) {
225
+ const binary = atob(b64);
226
+ const bytes = new Uint8Array(binary.length);
227
+ for (let i = 0; i < binary.length; i++)
228
+ bytes[i] = binary.charCodeAt(i);
229
+ return bytes;
230
+ }
217
231
  function publicKeyB64(id) {
218
- return Buffer.from(id.publicKey).toString("base64");
232
+ return toBase64(id.publicKey);
219
233
  }
220
234
  function fingerprint(id) {
221
- return createHash("sha256").update(id.publicKey).digest("hex");
235
+ return bytesToHex(sha256(id.publicKey));
222
236
  }
223
237
  function fingerprintFromB64(pubkeyB64) {
224
- const raw = Buffer.from(pubkeyB64, "base64");
225
- return createHash("sha256").update(raw).digest("hex");
238
+ return bytesToHex(sha256(fromBase64(pubkeyB64)));
226
239
  }
227
240
  async function loadOrCreateIdentity() {
228
241
  const p = getPlatformAdapter();
@@ -240,8 +253,8 @@ async function ensureDeviceKey(keyPath) {
240
253
  return;
241
254
  const id = generate();
242
255
  const stored = {
243
- publicKey: Buffer.from(id.publicKey).toString("base64"),
244
- privateKey: Buffer.from(id.privateKey).toString("base64")
256
+ publicKey: toBase64(id.publicKey),
257
+ privateKey: toBase64(id.privateKey)
245
258
  };
246
259
  await p.mkdir(p.dirname(keyPath));
247
260
  await p.writeFile(keyPath, JSON.stringify(stored, null, 2));
@@ -269,7 +282,7 @@ async function loadFromFile(path2) {
269
282
  const p = getPlatformAdapter();
270
283
  const raw = await p.readFile(path2);
271
284
  const stored = JSON.parse(raw);
272
- const privateKey = new Uint8Array(Buffer.from(stored.privateKey, "base64"));
285
+ const privateKey = fromBase64(stored.privateKey);
273
286
  if (privateKey.length !== 32) {
274
287
  throw new Error(`private key seed must be 32 bytes, got ${privateKey.length}`);
275
288
  }
@@ -284,8 +297,8 @@ function generate() {
284
297
  async function save(id) {
285
298
  const p = getPlatformAdapter();
286
299
  const stored = {
287
- publicKey: Buffer.from(id.publicKey).toString("base64"),
288
- privateKey: Buffer.from(id.privateKey).toString("base64")
300
+ publicKey: toBase64(id.publicKey),
301
+ privateKey: toBase64(id.privateKey)
289
302
  };
290
303
  const kpPath = keypairPath();
291
304
  await p.mkdir(p.dirname(kpPath));
@@ -353,7 +366,7 @@ function buildBindUrl(dashboardBaseUrl, bindId) {
353
366
  const base = dashboardBaseUrl.replace(/\/+$/, "");
354
367
  return `${base}/bind/${bindId}`;
355
368
  }
356
- async function agentBind(apiUrl, publicKey, fingerprint2, agentType, hostname) {
369
+ async function agentBind(apiUrl, publicKey, fingerprint2, agentFramework, hostname) {
357
370
  const p = getPlatformAdapter();
358
371
  const url = `${apiUrl}/api/v1/agent/bind`;
359
372
  const resp = await p.fetch(url, {
@@ -362,7 +375,7 @@ async function agentBind(apiUrl, publicKey, fingerprint2, agentType, hostname) {
362
375
  body: JSON.stringify({
363
376
  public_key: publicKey,
364
377
  fingerprint: fingerprint2,
365
- agent_type: agentType,
378
+ agent_framework: agentFramework,
366
379
  hostname
367
380
  })
368
381
  });
@@ -560,22 +573,14 @@ async function upgradeDeviceAgent(progress) {
560
573
  }
561
574
  async function findExisting() {
562
575
  const p = getPlatformAdapter();
576
+ const whichCmd = p.platform() === "win32" ? "where" : "which";
563
577
  try {
564
- const result = await p.exec("which", ["device-agent"]);
578
+ const result = await p.exec(whichCmd, ["device-agent"]);
565
579
  if (result.code === 0 && result.stdout.trim()) {
566
- return { type: "executable", path: result.stdout.trim() };
580
+ return { type: "executable", path: result.stdout.trim().split("\n")[0].trim() };
567
581
  }
568
582
  } catch {
569
583
  }
570
- if (p.platform() === "win32") {
571
- try {
572
- const result = await p.exec("where", ["device-agent"]);
573
- if (result.code === 0 && result.stdout.trim()) {
574
- return { type: "executable", path: result.stdout.trim().split("\n")[0].trim() };
575
- }
576
- } catch {
577
- }
578
- }
579
584
  const managed = managedBinary();
580
585
  if (await p.exists(managed)) {
581
586
  return { type: "executable", path: managed };
@@ -665,7 +670,7 @@ async function installWithPython(progress, venv, source, python) {
665
670
  async function installSource() {
666
671
  const p = getPlatformAdapter();
667
672
  try {
668
- const cwd = p.env("PWD") || p.homeDir();
673
+ const cwd = p.cwd();
669
674
  let dir = cwd;
670
675
  for (let i = 0; i < 10; i++) {
671
676
  const da = p.joinPath(dir, "agents", "device-agent", "pyproject.toml");
@@ -769,7 +774,7 @@ ${stderr}`);
769
774
 
770
775
  // ../core/dist/runtime/device.js
771
776
  var deviceRuntime = {
772
- agentType() {
777
+ agentFramework() {
773
778
  return "device";
774
779
  },
775
780
  displayName() {
@@ -880,9 +885,9 @@ var GATEWAY_PORT = 18789;
880
885
  async function isGatewayRunning() {
881
886
  return getPlatformAdapter().tcpProbe("127.0.0.1", GATEWAY_PORT, 500);
882
887
  }
883
- async function managedBinary2(agentType) {
888
+ async function managedBinary2(agentFramework) {
884
889
  const p = getPlatformAdapter();
885
- const base = agentDir(agentType);
890
+ const base = agentDir(agentFramework);
886
891
  const currentLink = p.joinPath(base, "versions", "current");
887
892
  if (!await p.exists(currentLink))
888
893
  return null;
@@ -893,20 +898,45 @@ async function managedBinary2(agentType) {
893
898
  resolved = currentLink;
894
899
  }
895
900
  const binShim = p.joinPath(resolved, "node_modules", ".bin", "openclaw");
901
+ if (p.platform() === "win32") {
902
+ const cmdShim = binShim + ".cmd";
903
+ if (await p.exists(cmdShim))
904
+ return cmdShim;
905
+ }
896
906
  if (await p.exists(binShim))
897
907
  return binShim;
898
908
  const mjs = p.joinPath(resolved, "node_modules", "openclaw", "openclaw.mjs");
899
909
  if (await p.exists(mjs))
900
910
  return mjs;
911
+ const rootMjs = p.joinPath(resolved, "openclaw.mjs");
912
+ if (await p.exists(rootMjs))
913
+ return rootMjs;
914
+ return null;
915
+ }
916
+ async function readLocalPluginVersion(agentHome2) {
917
+ const p = getPlatformAdapter();
918
+ for (const rel of [
919
+ p.joinPath("extensions", "beeos-claw", "package.json"),
920
+ p.joinPath("extensions", "beeos-claw", "node_modules", "beeos-claw", "package.json")
921
+ ]) {
922
+ const fullPath = p.joinPath(agentHome2, rel);
923
+ try {
924
+ const raw = await p.readFile(fullPath);
925
+ const parsed = JSON.parse(raw);
926
+ if (typeof parsed.version === "string")
927
+ return parsed.version;
928
+ } catch {
929
+ }
930
+ }
901
931
  return null;
902
932
  }
903
933
  async function findAgent(driver) {
904
- const managed = await managedBinary2(driver.agentType());
934
+ const managed = await managedBinary2(driver.agentFramework());
905
935
  if (managed) {
906
936
  return {
907
937
  type: "managed",
908
938
  binary: managed,
909
- home: agentHome(driver.agentType())
939
+ home: agentHome(driver.agentFramework())
910
940
  };
911
941
  }
912
942
  const local = await driver.detectLocal();
@@ -922,7 +952,7 @@ async function findAgent(driver) {
922
952
  }
923
953
 
924
954
  // ../core/dist/agent/downloader.js
925
- async function downloadAgent(npmPackage, requestedVersion, agentType, progress) {
955
+ async function downloadAgent(npmPackage, requestedVersion, agentFramework, progress) {
926
956
  const p = getPlatformAdapter();
927
957
  const info = await fetchNpmPackageInfo(npmPackage);
928
958
  const version = requestedVersion ?? info["dist-tags"]["latest"];
@@ -931,7 +961,7 @@ async function downloadAgent(npmPackage, requestedVersion, agentType, progress)
931
961
  const verInfo = info.versions[version];
932
962
  if (!verInfo)
933
963
  throw new Error(`version ${version} not found in npm registry`);
934
- const versionsDir = p.joinPath(agentDir(agentType), "versions");
964
+ const versionsDir = p.joinPath(agentDir(agentFramework), "versions");
935
965
  const dest = p.joinPath(versionsDir, version);
936
966
  if (await p.exists(dest)) {
937
967
  progress.onStatus(`${npmPackage} v${version} already downloaded`);
@@ -957,19 +987,28 @@ async function downloadAgent(npmPackage, requestedVersion, agentType, progress)
957
987
  }
958
988
  return dest;
959
989
  }
960
- async function downloadPlugin(pluginPackage, agentType, progress) {
990
+ async function downloadPlugin(pluginPackage, agentFramework, progress, installedVersion) {
961
991
  const p = getPlatformAdapter();
962
992
  const info = await fetchNpmPackageInfo(pluginPackage);
963
993
  const version = info["dist-tags"]["latest"];
964
994
  if (!version)
965
995
  throw new Error("no 'latest' tag for plugin");
996
+ if (installedVersion && semverGte(installedVersion, version)) {
997
+ progress.onStatus(`${pluginPackage} v${installedVersion} is up to date`);
998
+ const cacheDir2 = p.joinPath(agentDir(agentFramework), "beeos-claw");
999
+ const existing = p.joinPath(cacheDir2, version);
1000
+ if (await p.exists(existing))
1001
+ return existing;
1002
+ }
966
1003
  const verInfo = info.versions[version];
967
1004
  if (!verInfo)
968
1005
  throw new Error("plugin version not found");
969
- const cacheDir = p.joinPath(agentDir(agentType), "beeos-claw");
1006
+ const cacheDir = p.joinPath(agentDir(agentFramework), "beeos-claw");
970
1007
  const dest = p.joinPath(cacheDir, version);
971
- if (await p.exists(dest))
1008
+ if (await p.exists(dest)) {
1009
+ progress.onStatus(`${pluginPackage} v${version} already cached`);
972
1010
  return dest;
1011
+ }
973
1012
  progress.onStatus(`Downloading ${pluginPackage} v${version} ...`);
974
1013
  const data = await downloadTarball(verInfo.dist.tarball);
975
1014
  await extractTarball(data, dest);
@@ -977,6 +1016,7 @@ async function downloadPlugin(pluginPackage, agentType, progress) {
977
1016
  if (await p.exists(pkgJson)) {
978
1017
  await runNpmInstall(dest);
979
1018
  }
1019
+ progress.onComplete(`${pluginPackage} v${version} ready`);
980
1020
  return dest;
981
1021
  }
982
1022
  async function extractTarball(data, dest) {
@@ -985,7 +1025,12 @@ async function extractTarball(data, dest) {
985
1025
  const tmpFile = p.joinPath(dest, "__tmp.tgz");
986
1026
  await p.writeFileBytes(tmpFile, data);
987
1027
  try {
988
- await p.exec("tar", ["-xzf", tmpFile, "--strip-components=1", "-C", dest]);
1028
+ try {
1029
+ const tar = await import("tar");
1030
+ await tar.extract({ file: tmpFile, cwd: dest, strip: 1 });
1031
+ } catch {
1032
+ await p.exec("tar", ["-xzf", tmpFile, "--strip-components=1", "-C", dest]);
1033
+ }
989
1034
  } finally {
990
1035
  try {
991
1036
  await p.rm(tmpFile);
@@ -1002,9 +1047,22 @@ async function runNpmInstall(dir) {
1002
1047
  throw new Error(`npm install failed with exit code ${result.code}`);
1003
1048
  }
1004
1049
  }
1050
+ function semverGte(version, minVersion) {
1051
+ const parse2 = (s) => s.split(".").map((p) => parseInt(p, 10) || 0);
1052
+ const a = parse2(version);
1053
+ const b = parse2(minVersion);
1054
+ for (let i = 0; i < 3; i++) {
1055
+ if ((a[i] ?? 0) > (b[i] ?? 0))
1056
+ return true;
1057
+ if ((a[i] ?? 0) < (b[i] ?? 0))
1058
+ return false;
1059
+ }
1060
+ return true;
1061
+ }
1005
1062
 
1006
1063
  // ../core/dist/agent/launcher.js
1007
- import { createHash as createHash2 } from "crypto";
1064
+ import { sha256 as sha2562 } from "@noble/hashes/sha256";
1065
+ import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
1008
1066
  var MAX_BACKUPS = 5;
1009
1067
  async function backupOpenclawConfig(agentHome2) {
1010
1068
  const p = getPlatformAdapter();
@@ -1150,6 +1208,28 @@ async function spawnOpenclaw(ctx) {
1150
1208
  });
1151
1209
  return result.pid;
1152
1210
  }
1211
+ async function installPluginToHome(agentHome2, pluginDir) {
1212
+ const p = getPlatformAdapter();
1213
+ const dest = p.joinPath(agentHome2, "extensions", "beeos-claw");
1214
+ if (await p.exists(dest)) {
1215
+ await p.rmdir(dest);
1216
+ }
1217
+ await copyDirRecursive(pluginDir, dest);
1218
+ }
1219
+ async function copyDirRecursive(src, dst) {
1220
+ const p = getPlatformAdapter();
1221
+ await p.mkdir(dst);
1222
+ const entries = await p.readdir(src);
1223
+ for (const name of entries) {
1224
+ const srcPath = p.joinPath(src, name);
1225
+ const dstPath = p.joinPath(dst, name);
1226
+ if (await p.isDirectory(srcPath)) {
1227
+ await copyDirRecursive(srcPath, dstPath);
1228
+ } else {
1229
+ await p.copyFile(srcPath, dstPath);
1230
+ }
1231
+ }
1232
+ }
1153
1233
  async function writePairedJson(home, keyFile) {
1154
1234
  const p = getPlatformAdapter();
1155
1235
  const devicesDir = p.joinPath(home, "devices");
@@ -1165,8 +1245,11 @@ async function writePairedJson(home, keyFile) {
1165
1245
  }
1166
1246
  let deviceId = "beeos-claw-device";
1167
1247
  if (pubKeyB64) {
1168
- const decoded = Buffer.from(pubKeyB64, "base64");
1169
- const hash = createHash2("sha256").update(decoded).digest("hex");
1248
+ const binary = atob(pubKeyB64);
1249
+ const decoded = new Uint8Array(binary.length);
1250
+ for (let i = 0; i < binary.length; i++)
1251
+ decoded[i] = binary.charCodeAt(i);
1252
+ const hash = bytesToHex2(sha2562(decoded));
1170
1253
  deviceId = `beeos-claw-${hash.slice(0, 12)}`;
1171
1254
  }
1172
1255
  const paired = {
@@ -1186,7 +1269,7 @@ async function writePairedJson(home, keyFile) {
1186
1269
 
1187
1270
  // ../core/dist/agent/registry.js
1188
1271
  var openClawDriver = {
1189
- agentType() {
1272
+ agentFramework() {
1190
1273
  return "openclaw";
1191
1274
  },
1192
1275
  npmPackage() {
@@ -1221,7 +1304,7 @@ var openClawDriver = {
1221
1304
  // ../core/dist/runtime/openclaw.js
1222
1305
  var GATEWAY_PORT2 = 18789;
1223
1306
  var openclawRuntime = {
1224
- agentType() {
1307
+ agentFramework() {
1225
1308
  return "openclaw";
1226
1309
  },
1227
1310
  displayName() {
@@ -1257,11 +1340,11 @@ var openclawRuntime = {
1257
1340
  async isHealthy() {
1258
1341
  return getPlatformAdapter().tcpProbe("127.0.0.1", GATEWAY_PORT2, 500);
1259
1342
  },
1260
- async stop(agentType) {
1261
- const pid = await readPid(agentType);
1343
+ async stop(agentFramework) {
1344
+ const pid = await readPid(agentFramework);
1262
1345
  if (pid != null) {
1263
1346
  killProcess(pid);
1264
- await removePid(agentType);
1347
+ await removePid(agentFramework);
1265
1348
  }
1266
1349
  },
1267
1350
  async update(progress) {
@@ -1270,6 +1353,48 @@ var openclawRuntime = {
1270
1353
  }
1271
1354
  };
1272
1355
 
1356
+ // ../core/dist/service/local-agent.js
1357
+ async function ensurePlugin(agentFramework, agentHomeDir, progress) {
1358
+ const currentVersion = await readLocalPluginVersion(agentHomeDir) ?? void 0;
1359
+ const pluginDir = await downloadPlugin(openClawDriver.pluginPackage(), agentFramework, progress, currentVersion);
1360
+ await installPluginToHome(agentHomeDir, pluginDir);
1361
+ }
1362
+ async function bind(options) {
1363
+ const resp = await agentBind(options.apiUrl, options.publicKey, options.fingerprint, options.agentFramework, options.hostname);
1364
+ if (resp.status === "bound") {
1365
+ const instanceId = resp.instance_id ?? "";
1366
+ await saveBindingInfo({
1367
+ fingerprint: options.fingerprint,
1368
+ instance_id: instanceId,
1369
+ bound_at: Math.floor(Date.now() / 1e3)
1370
+ });
1371
+ return { status: "bound", instanceId };
1372
+ }
1373
+ if (resp.status === "pending") {
1374
+ const bindId = resp.bind_id ?? "";
1375
+ const bindUrl = buildBindUrl(options.dashboardBaseUrl, bindId);
1376
+ await removeBindingInfo();
1377
+ if (options.confirmFn) {
1378
+ const instanceId2 = await options.confirmFn(bindId);
1379
+ await saveBindingInfo({
1380
+ fingerprint: options.fingerprint,
1381
+ instance_id: instanceId2,
1382
+ bound_at: Math.floor(Date.now() / 1e3)
1383
+ });
1384
+ return { status: "bound", instanceId: instanceId2 };
1385
+ }
1386
+ await presentBindUrl(bindUrl, options.headless ?? false);
1387
+ const instanceId = await pollUntilBound(options.apiUrl, bindId, 6e5);
1388
+ await saveBindingInfo({
1389
+ fingerprint: options.fingerprint,
1390
+ instance_id: instanceId,
1391
+ bound_at: Math.floor(Date.now() / 1e3)
1392
+ });
1393
+ return { status: "bound", instanceId };
1394
+ }
1395
+ throw new Error(`unexpected bind status: ${resp.status}`);
1396
+ }
1397
+
1273
1398
  // src/node-adapter.ts
1274
1399
  import {
1275
1400
  execFile,
@@ -1361,7 +1486,9 @@ var NodePlatformAdapter = class {
1361
1486
  env,
1362
1487
  timeout: options?.timeout,
1363
1488
  maxBuffer: 10 * 1024 * 1024,
1364
- encoding: "utf-8"
1489
+ encoding: "utf-8",
1490
+ shell: process.platform === "win32",
1491
+ windowsHide: true
1365
1492
  },
1366
1493
  (error, stdout, stderr) => {
1367
1494
  const code = error && "code" in error && typeof error.code === "number" ? error.code : error ? 1 : 0;
@@ -1390,6 +1517,8 @@ var NodePlatformAdapter = class {
1390
1517
  cwd: options?.cwd,
1391
1518
  env,
1392
1519
  detached: options?.detached ?? false,
1520
+ shell: process.platform === "win32",
1521
+ windowsHide: true,
1393
1522
  stdio: [
1394
1523
  "ignore",
1395
1524
  stdoutFd != null ? stdoutFd : "ignore",
@@ -1457,6 +1586,9 @@ var NodePlatformAdapter = class {
1457
1586
  env(key) {
1458
1587
  return process.env[key];
1459
1588
  }
1589
+ cwd() {
1590
+ return process.cwd();
1591
+ }
1460
1592
  // ── UI ──────────────────────────────────────────────────
1461
1593
  async openUrl(url) {
1462
1594
  const open = await import("open");
@@ -1506,13 +1638,13 @@ var CliReporter = class {
1506
1638
  };
1507
1639
 
1508
1640
  // src/commands/start.ts
1509
- async function run(agentType, options) {
1641
+ async function run(agentFramework, options) {
1510
1642
  const p = getPlatformAdapter();
1511
1643
  await ensureDirs();
1512
1644
  const cfg = await loadOrCreateConfig();
1513
1645
  const reporter = new CliReporter();
1514
- if (agentType !== "openclaw") {
1515
- throw new Error(`Agent type '${agentType}' is not yet supported. Use 'openclaw'.`);
1646
+ if (agentFramework !== "openclaw") {
1647
+ throw new Error(`Agent framework '${agentFramework}' is not yet supported. Use 'openclaw'.`);
1516
1648
  }
1517
1649
  const identity = await loadOrCreateIdentity();
1518
1650
  const fp = fingerprint(identity);
@@ -1527,13 +1659,16 @@ async function run(agentType, options) {
1527
1659
  if (location.type === "managed") {
1528
1660
  binary = location.binary;
1529
1661
  home = location.home;
1662
+ try {
1663
+ await ensurePlugin(agentFramework, home, reporter);
1664
+ } catch {
1665
+ }
1530
1666
  } else if (location.type === "system") {
1531
1667
  binary = location.binary;
1532
1668
  home = location.home;
1533
1669
  isSystemHome = true;
1534
1670
  try {
1535
- const pluginDir = await downloadPlugin(openClawDriver.pluginPackage(), agentType, reporter);
1536
- await installPluginToHome(home, pluginDir, binary);
1671
+ await ensurePlugin(agentFramework, home, reporter);
1537
1672
  } catch {
1538
1673
  }
1539
1674
  const systemToken = await readExistingGatewayToken(home);
@@ -1560,11 +1695,11 @@ async function run(agentType, options) {
1560
1695
  }
1561
1696
  } else {
1562
1697
  reporter.onStatus(`Downloading ${openClawDriver.npmPackage()}...`);
1563
- await downloadAgent(openClawDriver.npmPackage(), options.version ?? void 0, agentType, reporter);
1564
- home = agentHome(agentType);
1698
+ await downloadAgent(openClawDriver.npmPackage(), options.version ?? void 0, agentFramework, reporter);
1699
+ home = agentHome(agentFramework);
1565
1700
  await p.mkdir(home);
1566
- await downloadPlugin(openClawDriver.pluginPackage(), agentType, reporter);
1567
- const managedBin = await managedBinary2(agentType);
1701
+ await ensurePlugin(agentFramework, home, reporter);
1702
+ const managedBin = await managedBinary2(agentFramework);
1568
1703
  if (!managedBin) throw new Error("agent binary not found after download");
1569
1704
  binary = managedBin;
1570
1705
  }
@@ -1586,7 +1721,7 @@ async function run(agentType, options) {
1586
1721
  await generateOpenclawConfig(ctx);
1587
1722
  }
1588
1723
  const pid = await spawnOpenclaw(ctx);
1589
- await writePid(agentType, pid);
1724
+ await writePid(agentFramework, pid);
1590
1725
  gatewayPid = pid;
1591
1726
  let alive = false;
1592
1727
  for (let i = 0; i < 10; i++) {
@@ -1600,55 +1735,52 @@ async function run(agentType, options) {
1600
1735
  throw new Error("Agent gateway did not start within 5s");
1601
1736
  }
1602
1737
  } else {
1603
- gatewayPid = await readPid(agentType) ?? void 0;
1738
+ gatewayPid = await readPid(agentFramework) ?? void 0;
1604
1739
  }
1605
1740
  const hostname = buildHostname();
1606
1741
  try {
1607
- const resp = await agentBind(cfg.platform.api_url, pubkey, fp, agentType, hostname);
1608
- if (resp.status === "bound") {
1609
- const instanceId = resp.instance_id ?? "";
1610
- await saveBindingInfo({ fingerprint: fp, instance_id: instanceId, bound_at: nowUnix() });
1611
- const result = {
1742
+ const result = await bind({
1743
+ apiUrl: cfg.platform.api_url,
1744
+ dashboardBaseUrl: cfg.platform.dashboard_base_url,
1745
+ publicKey: pubkey,
1746
+ fingerprint: fp,
1747
+ agentFramework,
1748
+ hostname,
1749
+ headless: options.browser === false
1750
+ });
1751
+ if (result.status === "bound") {
1752
+ const out = {
1612
1753
  status: "bound",
1613
1754
  public_key: pubkey,
1614
1755
  fingerprint: fp,
1615
1756
  gateway_url: "http://127.0.0.1:18789",
1616
1757
  gateway_pid: gatewayPid,
1617
1758
  bridge_url: cfg.platform.bridge_url,
1618
- instance_id: instanceId
1759
+ instance_id: result.instanceId
1619
1760
  };
1620
1761
  if (options.json) {
1621
- console.log(JSON.stringify(result, null, 2));
1762
+ console.log(JSON.stringify(out, null, 2));
1622
1763
  } else {
1623
- console.log(`Agent bound to instance: ${instanceId}`);
1764
+ console.log(`Agent bound to instance: ${result.instanceId}`);
1624
1765
  }
1625
1766
  return;
1626
1767
  }
1627
- if (resp.status === "pending") {
1628
- const bindId = resp.bind_id ?? "";
1629
- const bindUrl = buildBindUrl(cfg.platform.dashboard_base_url, bindId);
1630
- await removeBindingInfo();
1768
+ if (result.status === "pending") {
1631
1769
  if (options.json) {
1632
- const result = {
1770
+ const out = {
1633
1771
  status: "pending",
1634
1772
  public_key: pubkey,
1635
1773
  fingerprint: fp,
1636
- bind_id: bindId,
1637
- bind_url: bindUrl,
1638
- qr_data: bindUrl,
1774
+ bind_id: result.bindId,
1775
+ bind_url: result.bindUrl,
1776
+ qr_data: result.bindUrl,
1639
1777
  gateway_url: "http://127.0.0.1:18789",
1640
1778
  gateway_pid: gatewayPid,
1641
1779
  bridge_url: cfg.platform.bridge_url
1642
1780
  };
1643
- console.log(JSON.stringify(result, null, 2));
1781
+ console.log(JSON.stringify(out, null, 2));
1644
1782
  return;
1645
1783
  }
1646
- await presentBindUrl(bindUrl, options.browser === false);
1647
- console.log("");
1648
- console.log(" Waiting for bind approval (timeout: 10min)...");
1649
- const instanceId = await pollUntilBound(cfg.platform.api_url, bindId, 6e5);
1650
- await saveBindingInfo({ fingerprint: fp, instance_id: instanceId, bound_at: nowUnix() });
1651
- console.log(` Bind confirmed! Instance: ${instanceId}`);
1652
1784
  }
1653
1785
  } catch (e) {
1654
1786
  const cached = await loadBindingInfo();
@@ -1671,50 +1803,25 @@ async function run(agentType, options) {
1671
1803
  throw new Error(`Cannot reach platform to bind agent: ${e}`);
1672
1804
  }
1673
1805
  }
1674
- async function installPluginToHome(home, pluginDir, binary) {
1675
- const p = getPlatformAdapter();
1676
- const dest = p.joinPath(home, "extensions", "beeos-claw");
1677
- if (await p.exists(dest)) {
1678
- await p.rmdir(dest);
1679
- }
1680
- await copyDirRecursive(pluginDir, dest);
1681
- }
1682
- async function copyDirRecursive(src, dst) {
1683
- const p = getPlatformAdapter();
1684
- await p.mkdir(dst);
1685
- const entries = await p.readdir(src);
1686
- for (const name of entries) {
1687
- const srcPath = p.joinPath(src, name);
1688
- const dstPath = p.joinPath(dst, name);
1689
- if (await p.isDirectory(srcPath)) {
1690
- await copyDirRecursive(srcPath, dstPath);
1691
- } else {
1692
- await p.copyFile(srcPath, dstPath);
1693
- }
1694
- }
1695
- }
1696
1806
  function buildHostname() {
1697
1807
  const machine = os2.hostname();
1698
1808
  const user = process.env.USER || process.env.USERNAME || "";
1699
1809
  return user ? `${user}@${machine}` : machine;
1700
1810
  }
1701
- function nowUnix() {
1702
- return Math.floor(Date.now() / 1e3);
1703
- }
1704
1811
  function sleep2(ms) {
1705
1812
  return new Promise((resolve) => setTimeout(resolve, ms));
1706
1813
  }
1707
1814
 
1708
1815
  // src/commands/stop.ts
1709
- async function run2(agentType) {
1710
- const pid = await readPid(agentType);
1816
+ async function run2(agentFramework) {
1817
+ const pid = await readPid(agentFramework);
1711
1818
  if (pid == null) {
1712
- console.log(`No running ${agentType} agent found`);
1819
+ console.log(`No running ${agentFramework} agent found`);
1713
1820
  return;
1714
1821
  }
1715
1822
  killProcess(pid);
1716
- await removePid(agentType);
1717
- console.log(`Stopped ${agentType} agent (pid ${pid})`);
1823
+ await removePid(agentFramework);
1824
+ console.log(`Stopped ${agentFramework} agent (pid ${pid})`);
1718
1825
  }
1719
1826
 
1720
1827
  // src/commands/status.ts
@@ -1748,25 +1855,25 @@ async function run3() {
1748
1855
  }
1749
1856
 
1750
1857
  // src/commands/update.ts
1751
- async function run4(agentType) {
1858
+ async function run4(agentFramework) {
1752
1859
  const reporter = new CliReporter();
1753
- const pid = await readPid(agentType);
1860
+ const pid = await readPid(agentFramework);
1754
1861
  const wasRunning = pid != null && isProcessAlive(pid);
1755
1862
  if (wasRunning) {
1756
- await run2(agentType);
1863
+ await run2(agentFramework);
1757
1864
  }
1758
- if (agentType === "openclaw") {
1865
+ if (agentFramework === "openclaw") {
1759
1866
  await openclawRuntime.update(reporter);
1760
- } else if (agentType === "device") {
1867
+ } else if (agentFramework === "device") {
1761
1868
  await deviceRuntime.update(reporter);
1762
1869
  } else {
1763
- throw new Error(`Unknown agent type: ${agentType}`);
1870
+ throw new Error(`Unknown agent framework: ${agentFramework}`);
1764
1871
  }
1765
1872
  reporter.stop();
1766
- console.log(`${agentType} updated`);
1873
+ console.log(`${agentFramework} updated`);
1767
1874
  if (wasRunning) {
1768
1875
  console.log("Restarting agent...");
1769
- await run(agentType, { force: true });
1876
+ await run(agentFramework, { force: true });
1770
1877
  }
1771
1878
  }
1772
1879
 
@@ -2107,10 +2214,10 @@ function sleep3(ms) {
2107
2214
  setPlatformAdapter(new NodePlatformAdapter());
2108
2215
  var program = new Command();
2109
2216
  program.name("beeos").version("1.0.0").description("BeeOS \u2014 run AI agents from your desktop");
2110
- program.command("start").description("Install and start an agent").argument("<agent_type>", "Agent type to start (e.g. openclaw)").option("--version <version>", "Pin to a specific version instead of latest").option("--force", "Skip confirmation prompts", false).option("--json", "Output machine-readable JSON to stdout (implies --force)", false).option("--no-browser", "Don't auto-open browser for bind confirmation").action(run);
2111
- program.command("stop").description("Stop a running agent").argument("<agent_type>", "Agent type to stop").action(run2);
2217
+ program.command("start").description("Install and start an agent").argument("<agent_type>", "Agent type to start (e.g. openclaw)").option("--version <version>", "Pin to a specific version instead of latest").option("--force", "Skip confirmation prompts", false).option("--json", "Output machine-readable JSON to stdout (implies --force)", false).option("--no-browser", "Don't auto-open browser for bind confirmation").action((agentFramework, options) => run(agentFramework, options));
2218
+ program.command("stop").description("Stop a running agent").argument("<agent_type>", "Agent type to stop").action((agentFramework) => run2(agentFramework));
2112
2219
  program.command("status").description("Show status of managed agents").action(run3);
2113
- program.command("update").description("Update an agent to the latest version").argument("<agent_type>", "Agent type to update").action(run4);
2220
+ program.command("update").description("Update an agent to the latest version").argument("<agent_type>", "Agent type to update").action((agentFramework) => run4(agentFramework));
2114
2221
  program.command("bind-status").description("Check agent binding status").argument("<bind_id>", "Bind session ID returned by `beeos start --json`").option("--json", "Output machine-readable JSON", false).action(run5);
2115
2222
  var deviceCmd = program.command("device").description("Manage device agents (Android/Desktop/ChromeOS)");
2116
2223
  deviceCmd.command("attach").description("Attach an ADB-connected device as an AI agent").option("--serial <serial>", "ADB device serial number").option("--name <name>", "Human-readable device name").option("--all", "Attach all connected ADB devices", false).action(attach);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beeos-ai/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "type": "module",
5
5
  "description": "BeeOS CLI — run AI agents from your desktop",
6
6
  "bin": {
@@ -13,38 +13,38 @@
13
13
  "dist/",
14
14
  "README.md"
15
15
  ],
16
- "scripts": {
17
- "build": "tsup",
18
- "type-check": "tsc --noEmit",
19
- "lint": "eslint src/",
20
- "test": "vitest run --passWithNoTests",
21
- "prepublishOnly": "pnpm build"
22
- },
23
16
  "dependencies": {
17
+ "@noble/ed25519": "^2.2.0",
18
+ "@noble/hashes": "^1.7.0",
24
19
  "commander": "^13.0.0",
25
20
  "open": "^10.1.0",
26
21
  "ora": "^8.1.0",
27
22
  "proper-lockfile": "^4.1.2",
28
- "which": "^5.0.0",
29
- "@noble/ed25519": "^2.2.0",
30
- "@noble/hashes": "^1.7.0",
23
+ "qrcode": "^1.5.4",
31
24
  "smol-toml": "^1.3.0",
32
- "qrcode": "^1.5.4"
25
+ "tar": "^7.5.13",
26
+ "which": "^5.0.0"
33
27
  },
34
28
  "devDependencies": {
35
- "@beeos/core": "workspace:*",
36
29
  "@types/node": "^22.0.0",
37
30
  "@types/proper-lockfile": "^4.1.4",
38
31
  "@types/qrcode": "^1.5.5",
39
32
  "@types/which": "^3.0.4",
40
33
  "tsup": "^8.3.0",
41
34
  "typescript": "^5.7.0",
42
- "vitest": "^3.0.0"
35
+ "vitest": "^3.0.0",
36
+ "@beeos/core": "0.1.0"
43
37
  },
44
38
  "engines": {
45
39
  "node": ">=18"
46
40
  },
47
41
  "publishConfig": {
48
42
  "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "type-check": "tsc --noEmit",
47
+ "lint": "eslint src/",
48
+ "test": "vitest run --passWithNoTests"
49
49
  }
50
- }
50
+ }