@beeos-ai/cli 1.0.18 → 1.0.20
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 +801 -591
- package/package.json +1 -1
- package/scripts/install.ps1 +29 -7
- package/scripts/install.sh +71 -7
package/dist/index.js
CHANGED
|
@@ -746,26 +746,40 @@ var init_keypair = __esm({
|
|
|
746
746
|
});
|
|
747
747
|
|
|
748
748
|
// ../core/dist/platform/client.js
|
|
749
|
+
async function bindHttpError(resp, op) {
|
|
750
|
+
const body = await resp.text().catch(() => "");
|
|
751
|
+
const code = resp.status === 401 || resp.status === 403 ? "platform_unauthorized" : resp.status === 429 ? "platform_rate_limited" : resp.status >= 500 ? "platform_unavailable" : "platform_bind_failed";
|
|
752
|
+
const hint = code === "platform_unauthorized" ? "Re-run `beeos init` and complete the OAuth flow again." : code === "platform_rate_limited" ? "Wait a minute and try again." : code === "platform_unavailable" ? "The platform is having trouble. Retry in a moment; if it persists, check status.beeos.ai." : "Re-run the command. If it still fails, contact support with the http_status.";
|
|
753
|
+
return new BeeosError({
|
|
754
|
+
code,
|
|
755
|
+
message: `Platform ${op} request failed: HTTP ${resp.status}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`,
|
|
756
|
+
hint,
|
|
757
|
+
details: { http_status: resp.status, op, body: body.slice(0, 1e3) }
|
|
758
|
+
});
|
|
759
|
+
}
|
|
749
760
|
function buildBindUrl(dashboardBaseUrl, bindId) {
|
|
750
761
|
const base = dashboardBaseUrl.replace(/\/+$/, "");
|
|
751
762
|
return `${base}/bind/${bindId}`;
|
|
752
763
|
}
|
|
753
|
-
async function agentBind(apiUrl, publicKey, fingerprint2, agentFramework, hostname) {
|
|
764
|
+
async function agentBind(apiUrl, publicKey, fingerprint2, agentFramework, hostname, osType) {
|
|
754
765
|
const p = getPlatformAdapter();
|
|
755
766
|
const url = `${apiUrl}/api/v1/agent/bind`;
|
|
767
|
+
const body = {
|
|
768
|
+
public_key: publicKey,
|
|
769
|
+
fingerprint: fingerprint2,
|
|
770
|
+
agent_framework: agentFramework,
|
|
771
|
+
hostname
|
|
772
|
+
};
|
|
773
|
+
if (osType && osType.length > 0) {
|
|
774
|
+
body.os_type = osType;
|
|
775
|
+
}
|
|
756
776
|
const resp = await p.fetch(url, {
|
|
757
777
|
method: "POST",
|
|
758
778
|
headers: { "Content-Type": "application/json" },
|
|
759
|
-
body: JSON.stringify(
|
|
760
|
-
public_key: publicKey,
|
|
761
|
-
fingerprint: fingerprint2,
|
|
762
|
-
agent_framework: agentFramework,
|
|
763
|
-
hostname
|
|
764
|
-
})
|
|
779
|
+
body: JSON.stringify(body)
|
|
765
780
|
});
|
|
766
781
|
if (!resp.ok) {
|
|
767
|
-
|
|
768
|
-
throw new Error(`agent bind failed (${resp.status}): ${text}`);
|
|
782
|
+
throw await bindHttpError(resp, "bind");
|
|
769
783
|
}
|
|
770
784
|
return await resp.json();
|
|
771
785
|
}
|
|
@@ -776,7 +790,7 @@ async function pollBind(apiUrl, bindId) {
|
|
|
776
790
|
signal: AbortSignal.timeout(35e3)
|
|
777
791
|
});
|
|
778
792
|
if (!resp.ok) {
|
|
779
|
-
throw
|
|
793
|
+
throw await bindHttpError(resp, "poll");
|
|
780
794
|
}
|
|
781
795
|
return await resp.json();
|
|
782
796
|
}
|
|
@@ -811,7 +825,7 @@ async function getBindStatus(apiUrl, bindId) {
|
|
|
811
825
|
signal: AbortSignal.timeout(5e3)
|
|
812
826
|
});
|
|
813
827
|
if (!resp.ok) {
|
|
814
|
-
throw
|
|
828
|
+
throw await bindHttpError(resp, "status");
|
|
815
829
|
}
|
|
816
830
|
return await resp.json();
|
|
817
831
|
}
|
|
@@ -820,6 +834,7 @@ var init_client = __esm({
|
|
|
820
834
|
"use strict";
|
|
821
835
|
init_platform_adapter();
|
|
822
836
|
init_keypair();
|
|
837
|
+
init_errors();
|
|
823
838
|
}
|
|
824
839
|
});
|
|
825
840
|
|
|
@@ -914,7 +929,7 @@ async function bindAgent(opts) {
|
|
|
914
929
|
const pollTimeoutMs = opts.pollTimeoutMs ?? 6e5;
|
|
915
930
|
let resp;
|
|
916
931
|
try {
|
|
917
|
-
resp = await agentBind(opts.apiUrl, opts.publicKey, opts.fingerprint, opts.agentFramework, opts.hostname);
|
|
932
|
+
resp = await agentBind(opts.apiUrl, opts.publicKey, opts.fingerprint, opts.agentFramework, opts.hostname, opts.osType);
|
|
918
933
|
} catch (e) {
|
|
919
934
|
if (isNetworkError(e) && opts.cachedBinding && opts.cachedBinding.fingerprint === opts.fingerprint) {
|
|
920
935
|
return {
|
|
@@ -925,9 +940,20 @@ async function bindAgent(opts) {
|
|
|
925
940
|
throw e;
|
|
926
941
|
}
|
|
927
942
|
if (resp.status === "bound") {
|
|
943
|
+
if (!resp.instance_id) {
|
|
944
|
+
throw new BeeosError({
|
|
945
|
+
code: "platform_protocol_violation",
|
|
946
|
+
message: "Platform returned status='bound' but no instance_id.",
|
|
947
|
+
hint: "This indicates a platform bug. Re-run `beeos init`; if it persists, open a support ticket with the fingerprint below.",
|
|
948
|
+
details: {
|
|
949
|
+
fingerprint: opts.fingerprint,
|
|
950
|
+
response: resp
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
}
|
|
928
954
|
return {
|
|
929
955
|
status: "bound",
|
|
930
|
-
instanceId: resp.instance_id
|
|
956
|
+
instanceId: resp.instance_id,
|
|
931
957
|
source: "already_bound"
|
|
932
958
|
};
|
|
933
959
|
}
|
|
@@ -939,8 +965,10 @@ async function bindAgent(opts) {
|
|
|
939
965
|
return { status: "bound", instanceId, source: "fresh" };
|
|
940
966
|
}
|
|
941
967
|
await presentBindUrl(bindUrl, opts.headless ?? false, log);
|
|
968
|
+
const expiryMinutes = Math.round(pollTimeoutMs / 6e4);
|
|
969
|
+
log(` Press Ctrl+C to cancel; the bind session expires in ${expiryMinutes} minutes if not approved.`);
|
|
942
970
|
try {
|
|
943
|
-
const instanceId = await pollUntilBound(opts.apiUrl, bindId, pollTimeoutMs);
|
|
971
|
+
const instanceId = await pollUntilBound(opts.apiUrl, bindId, pollTimeoutMs, log);
|
|
944
972
|
return { status: "bound", instanceId, source: "fresh" };
|
|
945
973
|
} catch (e) {
|
|
946
974
|
if (e instanceof BeeosError && e.code === "bind_pending_expired") {
|
|
@@ -980,8 +1008,10 @@ async function presentBindUrl(bindUrl, forceHeadless, log = console.log) {
|
|
|
980
1008
|
log(" -----------------------------------------------------");
|
|
981
1009
|
}
|
|
982
1010
|
}
|
|
983
|
-
async function pollUntilBound(apiUrl, bindId, timeoutMs) {
|
|
1011
|
+
async function pollUntilBound(apiUrl, bindId, timeoutMs, log = () => void 0) {
|
|
984
1012
|
const deadline = Date.now() + timeoutMs;
|
|
1013
|
+
let lastTick = Date.now();
|
|
1014
|
+
const TICK_INTERVAL_MS = 3e4;
|
|
985
1015
|
while (true) {
|
|
986
1016
|
if (Date.now() > deadline) {
|
|
987
1017
|
throw new BeeosError({
|
|
@@ -991,10 +1021,23 @@ async function pollUntilBound(apiUrl, bindId, timeoutMs) {
|
|
|
991
1021
|
details: { source: "client_timeout", bindId }
|
|
992
1022
|
});
|
|
993
1023
|
}
|
|
1024
|
+
if (Date.now() - lastTick >= TICK_INTERVAL_MS) {
|
|
1025
|
+
const remainingSec = Math.max(0, Math.round((deadline - Date.now()) / 1e3));
|
|
1026
|
+
log(` Still waiting for confirmation... (${remainingSec}s remaining)`);
|
|
1027
|
+
lastTick = Date.now();
|
|
1028
|
+
}
|
|
994
1029
|
try {
|
|
995
1030
|
const resp = await pollBind(apiUrl, bindId);
|
|
996
1031
|
if (resp.status === "bound") {
|
|
997
|
-
|
|
1032
|
+
if (!resp.instance_id) {
|
|
1033
|
+
throw new BeeosError({
|
|
1034
|
+
code: "platform_protocol_violation",
|
|
1035
|
+
message: "Platform poll returned status='bound' but no instance_id.",
|
|
1036
|
+
hint: "This indicates a platform bug. Re-run `beeos init`; if it persists, open a support ticket with the bind_id below.",
|
|
1037
|
+
details: { bindId, response: resp }
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
return resp.instance_id;
|
|
998
1041
|
}
|
|
999
1042
|
if (resp.status === "expired") {
|
|
1000
1043
|
throw new BeeosError({
|
|
@@ -1005,7 +1048,7 @@ async function pollUntilBound(apiUrl, bindId, timeoutMs) {
|
|
|
1005
1048
|
});
|
|
1006
1049
|
}
|
|
1007
1050
|
} catch (e) {
|
|
1008
|
-
if (e instanceof BeeosError && e.code === "bind_pending_expired") {
|
|
1051
|
+
if (e instanceof BeeosError && (e.code === "bind_pending_expired" || e.code === "platform_protocol_violation" || e.code === "platform_unauthorized")) {
|
|
1009
1052
|
throw e;
|
|
1010
1053
|
}
|
|
1011
1054
|
await sleep(2e3);
|
|
@@ -3205,6 +3248,74 @@ var init_agent_status = __esm({
|
|
|
3205
3248
|
}
|
|
3206
3249
|
});
|
|
3207
3250
|
|
|
3251
|
+
// ../core/dist/openclaw/desktop-detect.js
|
|
3252
|
+
function printMacosDesktopHint() {
|
|
3253
|
+
for (const line of MACOS_DESKTOP_HINT_LINES)
|
|
3254
|
+
console.log(line);
|
|
3255
|
+
}
|
|
3256
|
+
function readEnv(name) {
|
|
3257
|
+
return globalThis.process?.env?.[name];
|
|
3258
|
+
}
|
|
3259
|
+
async function probeLocalVnc(opts = {}) {
|
|
3260
|
+
if (readEnv("BEEOS_NO_DESKTOP") === "1")
|
|
3261
|
+
return null;
|
|
3262
|
+
const p = getPlatformAdapter();
|
|
3263
|
+
const platform = p.platform();
|
|
3264
|
+
if (platform !== "darwin" && platform !== "linux") {
|
|
3265
|
+
return null;
|
|
3266
|
+
}
|
|
3267
|
+
const host = "127.0.0.1";
|
|
3268
|
+
const timeoutMs = 200;
|
|
3269
|
+
if (platform === "darwin") {
|
|
3270
|
+
const ok2 = await p.tcpProbe(host, 5900, timeoutMs).catch(() => false);
|
|
3271
|
+
if (ok2) {
|
|
3272
|
+
return { kind: "macos", host, port: 5900, osType: "macos-desktop" };
|
|
3273
|
+
}
|
|
3274
|
+
return null;
|
|
3275
|
+
}
|
|
3276
|
+
const ok = await p.tcpProbe(host, 5901, timeoutMs).catch(() => false);
|
|
3277
|
+
if (ok) {
|
|
3278
|
+
return { kind: "linux", host, port: 5901, osType: "linux-desktop" };
|
|
3279
|
+
}
|
|
3280
|
+
if (opts.ttyHints) {
|
|
3281
|
+
for (const line of LINUX_VNC_HINT_LINES)
|
|
3282
|
+
console.log(line);
|
|
3283
|
+
}
|
|
3284
|
+
return null;
|
|
3285
|
+
}
|
|
3286
|
+
var MACOS_DESKTOP_HINT_LINES, MACOS_DESKTOP_DOCTOR_HINT_LINES, LINUX_VNC_HINT_LINES;
|
|
3287
|
+
var init_desktop_detect = __esm({
|
|
3288
|
+
"../core/dist/openclaw/desktop-detect.js"() {
|
|
3289
|
+
"use strict";
|
|
3290
|
+
init_platform_adapter();
|
|
3291
|
+
MACOS_DESKTOP_HINT_LINES = [
|
|
3292
|
+
"Detected macOS Screen Sharing on :5900. Starting vnc-bridge...",
|
|
3293
|
+
" If desktop view shows a black screen / connection error on the dashboard:",
|
|
3294
|
+
" \u2022 System Settings \u2192 General \u2192 Sharing \u2192 Screen Sharing must be enabled",
|
|
3295
|
+
" \u2022 System Settings \u2192 Privacy & Security \u2192 Screen Recording must grant",
|
|
3296
|
+
" permission to the macOS Screen Sharing process",
|
|
3297
|
+
" \u2022 If a VNC password was set, export BEEOS_VNC_PASSWORD=<password>",
|
|
3298
|
+
" before re-running `beeos init`."
|
|
3299
|
+
];
|
|
3300
|
+
MACOS_DESKTOP_DOCTOR_HINT_LINES = [
|
|
3301
|
+
"vnc-bridge-openclaw is failing on macOS. Likely causes:",
|
|
3302
|
+
" \u2022 System Settings \u2192 General \u2192 Sharing \u2192 Screen Sharing turned off",
|
|
3303
|
+
" \u2022 System Settings \u2192 Privacy & Security \u2192 Screen Recording missing",
|
|
3304
|
+
" permission for the macOS Screen Sharing process",
|
|
3305
|
+
" \u2022 A VNC password is set; export BEEOS_VNC_PASSWORD=<password>",
|
|
3306
|
+
" before `beeos start openclaw --force` to retry."
|
|
3307
|
+
];
|
|
3308
|
+
LINUX_VNC_HINT_LINES = [
|
|
3309
|
+
"Tip: no VNC server detected on :5901. To enable BeeOS desktop streaming:",
|
|
3310
|
+
" Debian/Ubuntu: sudo apt install tigervnc-standalone-server",
|
|
3311
|
+
" RHEL/Fedora: sudo dnf install tigervnc-server",
|
|
3312
|
+
" Arch: sudo pacman -S tigervnc",
|
|
3313
|
+
"Then run `vncserver :1` and re-run `beeos start openclaw`.",
|
|
3314
|
+
"Set BEEOS_NO_DESKTOP=1 to suppress this hint."
|
|
3315
|
+
];
|
|
3316
|
+
}
|
|
3317
|
+
});
|
|
3318
|
+
|
|
3208
3319
|
// ../core/dist/detect.js
|
|
3209
3320
|
async function detectExistingInstall() {
|
|
3210
3321
|
const p = getPlatformAdapter();
|
|
@@ -3381,7 +3492,7 @@ function initDecisionLabel(decision) {
|
|
|
3381
3492
|
case "rebind":
|
|
3382
3493
|
return "Re-bind (keep key, refresh binding \u2014 same instance)";
|
|
3383
3494
|
case "reset-all":
|
|
3384
|
-
return "Reset everything (NEW key, NEW instance \u2014
|
|
3495
|
+
return "Reset everything (NEW key, NEW instance \u2014 old instance becomes orphaned on dashboard, delete it manually if needed; use this only when the key is compromised)";
|
|
3385
3496
|
case "skip":
|
|
3386
3497
|
return "Skip (do nothing)";
|
|
3387
3498
|
}
|
|
@@ -3447,12 +3558,20 @@ function buildVncBridgeTargetSpec(binary, opts) {
|
|
|
3447
3558
|
label: `vnc-bridge (${opts.serial})`
|
|
3448
3559
|
};
|
|
3449
3560
|
}
|
|
3561
|
+
function buildOpenclawDesktopVncBridgeSpec(binary, opts) {
|
|
3562
|
+
return buildVncBridgeTargetSpec(binary, {
|
|
3563
|
+
...opts,
|
|
3564
|
+
serial: OPENCLAW_VNC_BRIDGE_SERIAL
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
var OPENCLAW_VNC_BRIDGE_SERIAL;
|
|
3450
3568
|
var init_target_spec = __esm({
|
|
3451
3569
|
"../core/dist/services/target-spec.js"() {
|
|
3452
3570
|
"use strict";
|
|
3453
3571
|
init_scrcpy_bridge();
|
|
3454
3572
|
init_vnc_bridge();
|
|
3455
3573
|
init_spawn_env2();
|
|
3574
|
+
OPENCLAW_VNC_BRIDGE_SERIAL = "openclaw";
|
|
3456
3575
|
}
|
|
3457
3576
|
});
|
|
3458
3577
|
|
|
@@ -4953,7 +5072,7 @@ function getCliVersion(moduleUrl, distTag) {
|
|
|
4953
5072
|
return { version, distTag: safeTag, display };
|
|
4954
5073
|
}
|
|
4955
5074
|
function resolveActiveDistTag() {
|
|
4956
|
-
const envTag =
|
|
5075
|
+
const envTag = readEnv2("BEEOS_CLI_TAG");
|
|
4957
5076
|
if (envTag && envTag.trim())
|
|
4958
5077
|
return envTag.trim();
|
|
4959
5078
|
const baked = readBakedDistTag();
|
|
@@ -4961,7 +5080,7 @@ function resolveActiveDistTag() {
|
|
|
4961
5080
|
return baked.trim();
|
|
4962
5081
|
return "latest";
|
|
4963
5082
|
}
|
|
4964
|
-
function
|
|
5083
|
+
function readEnv2(key) {
|
|
4965
5084
|
const env = globalThis.process?.env;
|
|
4966
5085
|
return env?.[key];
|
|
4967
5086
|
}
|
|
@@ -5051,6 +5170,7 @@ var init_dist = __esm({
|
|
|
5051
5170
|
init_driver2();
|
|
5052
5171
|
init_constants();
|
|
5053
5172
|
init_agent_status();
|
|
5173
|
+
init_desktop_detect();
|
|
5054
5174
|
init_detect();
|
|
5055
5175
|
init_registry();
|
|
5056
5176
|
init_target_spec();
|
|
@@ -6744,8 +6864,10 @@ async function run(agentFramework, options) {
|
|
|
6744
6864
|
};
|
|
6745
6865
|
const launchOutcome = await driver.launch(ctx, reporter);
|
|
6746
6866
|
const spec = launchOutcome.spec;
|
|
6747
|
-
const launchWarnings = launchOutcome.warnings;
|
|
6867
|
+
const launchWarnings = [...launchOutcome.warnings];
|
|
6748
6868
|
reporter.stop();
|
|
6869
|
+
const ttyHints = !options.json && process.stdin.isTTY === true;
|
|
6870
|
+
const vncEndpoint = agentFramework === OPENCLAW_ID ? await probeLocalVnc({ ttyHints }) : null;
|
|
6749
6871
|
const hostname = buildHostname();
|
|
6750
6872
|
const cachedBinding = await loadBindingInfo();
|
|
6751
6873
|
const outcome = await bindAgent({
|
|
@@ -6755,6 +6877,7 @@ async function run(agentFramework, options) {
|
|
|
6755
6877
|
fingerprint: fp,
|
|
6756
6878
|
agentFramework,
|
|
6757
6879
|
hostname,
|
|
6880
|
+
osType: vncEndpoint?.osType,
|
|
6758
6881
|
headless: options.browser === false,
|
|
6759
6882
|
cachedBinding: cachedBinding ? {
|
|
6760
6883
|
fingerprint: cachedBinding.fingerprint,
|
|
@@ -6813,11 +6936,28 @@ async function run(agentFramework, options) {
|
|
|
6813
6936
|
const isRebind = !isOffline && cachedBinding !== null && cachedBinding.instance_id !== boundInstanceId;
|
|
6814
6937
|
const mgr = await getServiceManager();
|
|
6815
6938
|
maybePrintFallbackBanner(mgr.kind);
|
|
6816
|
-
let status2
|
|
6939
|
+
let status2;
|
|
6940
|
+
try {
|
|
6941
|
+
status2 = await mgr.install(spec);
|
|
6942
|
+
} catch (err) {
|
|
6943
|
+
throw wrapServiceInstallFailure(err, {
|
|
6944
|
+
stage: "install",
|
|
6945
|
+
instanceId: boundInstanceId,
|
|
6946
|
+
driverId: driver.id
|
|
6947
|
+
});
|
|
6948
|
+
}
|
|
6817
6949
|
if (isRebind) {
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6950
|
+
try {
|
|
6951
|
+
await mgr.restart(spec.id);
|
|
6952
|
+
const refreshed = await mgr.status(spec.id);
|
|
6953
|
+
if (refreshed) status2 = refreshed;
|
|
6954
|
+
} catch (err) {
|
|
6955
|
+
throw wrapServiceInstallFailure(err, {
|
|
6956
|
+
stage: "restart",
|
|
6957
|
+
instanceId: boundInstanceId,
|
|
6958
|
+
driverId: driver.id
|
|
6959
|
+
});
|
|
6960
|
+
}
|
|
6821
6961
|
}
|
|
6822
6962
|
if (spec.healthcheck) {
|
|
6823
6963
|
try {
|
|
@@ -6829,15 +6969,32 @@ async function run(agentFramework, options) {
|
|
|
6829
6969
|
throw new BeeosError({
|
|
6830
6970
|
code: "service_install_failed",
|
|
6831
6971
|
message: diag.reason,
|
|
6832
|
-
hint: diag.hints.length > 0 ? diag.hints.map((h) => `\u2022 ${h}`).join("
|
|
6972
|
+
hint: `Binding to instance ${boundInstanceId} is intact on disk. Re-run \`beeos start ${agentFramework} --force\` to retry from this step. ` + (diag.hints.length > 0 ? "Diagnostics: " + diag.hints.map((h) => `\u2022 ${h}`).join("; ") : ""),
|
|
6833
6973
|
cause: err,
|
|
6834
|
-
details: { driver: driver.id }
|
|
6974
|
+
details: { driver: driver.id, instance_id: boundInstanceId }
|
|
6835
6975
|
});
|
|
6836
6976
|
}
|
|
6837
6977
|
}
|
|
6838
|
-
throw err
|
|
6978
|
+
throw wrapServiceInstallFailure(err, {
|
|
6979
|
+
stage: "healthcheck",
|
|
6980
|
+
instanceId: boundInstanceId,
|
|
6981
|
+
driverId: driver.id
|
|
6982
|
+
});
|
|
6839
6983
|
}
|
|
6840
6984
|
}
|
|
6985
|
+
if (vncEndpoint && !isOffline) {
|
|
6986
|
+
if (vncEndpoint.kind === "macos" && ttyHints) {
|
|
6987
|
+
printMacosDesktopHint();
|
|
6988
|
+
}
|
|
6989
|
+
const desktopWarning = await tryAttachOpenclawDesktopBridge({
|
|
6990
|
+
vncEndpoint,
|
|
6991
|
+
instanceId: boundInstanceId,
|
|
6992
|
+
keyFile,
|
|
6993
|
+
agentGatewayUrl,
|
|
6994
|
+
mgr
|
|
6995
|
+
});
|
|
6996
|
+
if (desktopWarning) launchWarnings.push(desktopWarning);
|
|
6997
|
+
}
|
|
6841
6998
|
const gatewayPid = status2.pid ?? void 0;
|
|
6842
6999
|
emit(options.json, {
|
|
6843
7000
|
status: isOffline ? "bound_offline" : "bound",
|
|
@@ -6850,11 +7007,52 @@ async function run(agentFramework, options) {
|
|
|
6850
7007
|
}, isOffline ? `Agent running (offline, cached instance: ${boundInstanceId})` : `Agent bound to instance: ${boundInstanceId}`);
|
|
6851
7008
|
if (!options.json) printLaunchWarnings(launchWarnings);
|
|
6852
7009
|
}
|
|
7010
|
+
async function tryAttachOpenclawDesktopBridge(params) {
|
|
7011
|
+
const reporter = new CliReporter();
|
|
7012
|
+
let binary = null;
|
|
7013
|
+
try {
|
|
7014
|
+
binary = await vncBridgeRuntime.ensureInstalled(reporter);
|
|
7015
|
+
} catch (e) {
|
|
7016
|
+
reporter.stop();
|
|
7017
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
7018
|
+
return `vnc-bridge install failed: ${reason}; OpenClaw is bound but desktop streaming is unavailable.`;
|
|
7019
|
+
}
|
|
7020
|
+
reporter.stop();
|
|
7021
|
+
if (!binary) {
|
|
7022
|
+
return `vnc-bridge binary unavailable (download or PATH lookup failed); OpenClaw is bound but desktop streaming is unavailable. Set BEEOS_VNC_BRIDGE_BIN to override.`;
|
|
7023
|
+
}
|
|
7024
|
+
const spec = buildOpenclawDesktopVncBridgeSpec(binary, {
|
|
7025
|
+
deviceId: params.instanceId,
|
|
7026
|
+
vncHost: params.vncEndpoint.host,
|
|
7027
|
+
vncPort: params.vncEndpoint.port,
|
|
7028
|
+
agentGatewayUrl: params.agentGatewayUrl,
|
|
7029
|
+
bridgePrivateKeyFile: params.keyFile,
|
|
7030
|
+
bridgePublicKeyFile: params.keyFile
|
|
7031
|
+
});
|
|
7032
|
+
try {
|
|
7033
|
+
await params.mgr.install(spec);
|
|
7034
|
+
} catch (e) {
|
|
7035
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
7036
|
+
return `vnc-bridge service install failed: ${reason}; OpenClaw is bound but desktop streaming is unavailable. Re-run \`beeos start openclaw --force\` to retry.`;
|
|
7037
|
+
}
|
|
7038
|
+
return null;
|
|
7039
|
+
}
|
|
6853
7040
|
function buildHostname() {
|
|
6854
7041
|
const machine = os6.hostname();
|
|
6855
7042
|
const user = process.env.USER || process.env.USERNAME || "";
|
|
6856
7043
|
return user ? `${user}@${machine}` : machine;
|
|
6857
7044
|
}
|
|
7045
|
+
function wrapServiceInstallFailure(err, ctx) {
|
|
7046
|
+
if (err instanceof BeeosError) return err;
|
|
7047
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7048
|
+
return new BeeosError({
|
|
7049
|
+
code: "service_install_failed",
|
|
7050
|
+
message: `Bind succeeded (instance ${ctx.instanceId}) but service ${ctx.stage} failed: ${msg}`,
|
|
7051
|
+
hint: `Local binding.json is intact. Re-run \`beeos start ${ctx.driverId} --force\` to retry from the service-${ctx.stage} step \u2014 you do NOT need to bind again.`,
|
|
7052
|
+
cause: err,
|
|
7053
|
+
details: { stage: ctx.stage, instance_id: ctx.instanceId, driver: ctx.driverId }
|
|
7054
|
+
});
|
|
7055
|
+
}
|
|
6858
7056
|
function emit(json, payload, human) {
|
|
6859
7057
|
if (json) {
|
|
6860
7058
|
emitJsonEnvelope(jsonOk(payload));
|
|
@@ -7063,644 +7261,656 @@ init_device2();
|
|
|
7063
7261
|
// src/commands/init.ts
|
|
7064
7262
|
init_dist();
|
|
7065
7263
|
import readline3 from "readline";
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
import fs9 from "fs/promises";
|
|
7071
|
-
import path9 from "path";
|
|
7072
|
-
var LOG_SIZE_WARN_BYTES = 50 * 1024 * 1024;
|
|
7264
|
+
init_attach();
|
|
7265
|
+
init_progress2();
|
|
7266
|
+
init_fallback_banner();
|
|
7267
|
+
init_instance_picker();
|
|
7073
7268
|
async function run7(options) {
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
const serviceCheck = await mgr.selfCheck();
|
|
7080
|
-
let services = [];
|
|
7081
|
-
try {
|
|
7082
|
-
services = await mgr.list();
|
|
7083
|
-
} catch {
|
|
7084
|
-
}
|
|
7085
|
-
const resolvedAgentGatewayUrl = resolveAgentGatewayUrl(cfg);
|
|
7086
|
-
const agentGatewayHealth = await probeAgentGateway(resolvedAgentGatewayUrl);
|
|
7087
|
-
const tools = await collectToolStatus();
|
|
7088
|
-
const hasBoundDevices = state.devices.entries.length > 0;
|
|
7089
|
-
const shimReport = await collectShimReport(hasBoundDevices);
|
|
7090
|
-
const warnings = [];
|
|
7091
|
-
const hints = [];
|
|
7092
|
-
if (!state.hasIdentity) {
|
|
7093
|
-
warnings.push("identity keypair missing \u2014 will be generated on next bind");
|
|
7094
|
-
}
|
|
7095
|
-
if (state.openclaw.gatewayRunning && !state.openclaw.found) {
|
|
7096
|
-
warnings.push("port 18789 is in use but no OpenClaw install detected \u2014 another app may conflict");
|
|
7269
|
+
await ensureDirs();
|
|
7270
|
+
if (options.device) {
|
|
7271
|
+
const { attach: attach2 } = await Promise.resolve().then(() => (init_device2(), device_exports));
|
|
7272
|
+
await attach2({});
|
|
7273
|
+
return;
|
|
7097
7274
|
}
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
)
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
)
|
|
7275
|
+
const state = await detectExistingInstall();
|
|
7276
|
+
if (!options.json) {
|
|
7277
|
+
printBanner();
|
|
7278
|
+
for (const line of summarizeExistingInstall(state)) {
|
|
7279
|
+
console.log(` ${line}`);
|
|
7280
|
+
}
|
|
7281
|
+
if (state.supervisor.targets.length > 0 || state.supervisor.ipcReachable) {
|
|
7282
|
+
console.log("");
|
|
7283
|
+
console.log(
|
|
7284
|
+
" Legacy Node supervisor state detected. Run `beeos migrate` to convert it to OS-native services."
|
|
7285
|
+
);
|
|
7286
|
+
}
|
|
7287
|
+
console.log("");
|
|
7105
7288
|
}
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7289
|
+
const decision = await decideAction(state, options);
|
|
7290
|
+
if (decision === "skip") {
|
|
7291
|
+
console.log("Nothing to do. Run `beeos init` again or `beeos device attach` when ready.");
|
|
7292
|
+
return;
|
|
7110
7293
|
}
|
|
7111
|
-
if (
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7294
|
+
if (decision === "upgrade") {
|
|
7295
|
+
if (await shouldUpgradeBeeosSuite(options)) {
|
|
7296
|
+
const exited = await upgradeAndMaybeExit(options);
|
|
7297
|
+
if (exited) return;
|
|
7298
|
+
}
|
|
7299
|
+
console.log("Ensuring OpenClaw is running...\n");
|
|
7115
7300
|
}
|
|
7116
|
-
if (
|
|
7117
|
-
|
|
7118
|
-
`Agent Gateway unreachable at ${resolvedAgentGatewayUrl}: ${agentGatewayHealth.detail}. Binding and task delivery will fail until this resolves.`
|
|
7119
|
-
);
|
|
7120
|
-
hints.push(
|
|
7121
|
-
"check BEEOS_AGENT_GATEWAY_URL / platform.agent_gateway_url in ~/.beeos/config.toml"
|
|
7122
|
-
);
|
|
7301
|
+
if (decision === "rebind") {
|
|
7302
|
+
await removeBindingInfo();
|
|
7123
7303
|
}
|
|
7124
|
-
if (
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
);
|
|
7304
|
+
if (decision === "reset-all") {
|
|
7305
|
+
await rotateIdentity();
|
|
7306
|
+
await removeBindingInfo();
|
|
7128
7307
|
}
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
`service '${s.id}' is installed but not running \u2014 see log ${s.logFile}`
|
|
7135
|
-
);
|
|
7308
|
+
if (decision === "fresh" && !options.framework && !options.yes && !options.json && process.stdin.isTTY) {
|
|
7309
|
+
const sel = await pickFreshInstance(options);
|
|
7310
|
+
if (sel.kind === "skip") {
|
|
7311
|
+
console.log("Skipped. Re-run `beeos init` or `beeos device attach` whenever ready.");
|
|
7312
|
+
return;
|
|
7136
7313
|
}
|
|
7137
|
-
if (
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7314
|
+
if (sel.kind === "device") {
|
|
7315
|
+
await attach({ serial: sel.serial });
|
|
7316
|
+
return;
|
|
7317
|
+
}
|
|
7318
|
+
const descriptor2 = frameworkById(sel.id);
|
|
7319
|
+
if (!descriptor2 || descriptor2.status !== "available") {
|
|
7320
|
+
throw new BeeosError({
|
|
7321
|
+
code: "framework_unavailable",
|
|
7322
|
+
message: `Agent framework '${sel.id}' is not available.`,
|
|
7323
|
+
hint: "Pick a different framework or reinstall the CLI.",
|
|
7324
|
+
details: { requested: sel.id, available: availableFrameworks().map((f) => f.id) }
|
|
7325
|
+
});
|
|
7326
|
+
}
|
|
7327
|
+
await run(descriptor2.id, {
|
|
7328
|
+
force: true,
|
|
7329
|
+
json: options.json,
|
|
7330
|
+
browser: options.browser !== false
|
|
7331
|
+
});
|
|
7332
|
+
if (!options.json && !options.skipServicePrompt) {
|
|
7333
|
+
await maybePromptServiceInstall();
|
|
7334
|
+
}
|
|
7335
|
+
if (!options.json) {
|
|
7336
|
+
printNextSteps();
|
|
7141
7337
|
}
|
|
7338
|
+
return;
|
|
7142
7339
|
}
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
);
|
|
7340
|
+
const frameworkId = await decideFramework(state, decision, options);
|
|
7341
|
+
const descriptor = frameworkById(frameworkId);
|
|
7342
|
+
if (!descriptor || descriptor.status !== "available") {
|
|
7343
|
+
const avail = availableFrameworks().map((f) => f.id).join(", ");
|
|
7344
|
+
throw new BeeosError({
|
|
7345
|
+
code: "framework_unavailable",
|
|
7346
|
+
message: `Agent framework '${frameworkId}' is not available. Available: ${avail}.`,
|
|
7347
|
+
hint: avail.length > 0 ? `Re-run with --framework <one-of-the-above>` : "No frameworks are registered \u2014 reinstall the CLI.",
|
|
7348
|
+
details: { requested: frameworkId, available: availableFrameworks().map((f) => f.id) }
|
|
7349
|
+
});
|
|
7147
7350
|
}
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
} else if (e.outcome === "error" || e.outcome === "not_installed_yet") {
|
|
7156
|
-
}
|
|
7351
|
+
await run(descriptor.id, {
|
|
7352
|
+
force: true,
|
|
7353
|
+
json: options.json,
|
|
7354
|
+
browser: options.browser !== false
|
|
7355
|
+
});
|
|
7356
|
+
if (!options.json && !options.skipServicePrompt) {
|
|
7357
|
+
await maybePromptServiceInstall();
|
|
7157
7358
|
}
|
|
7158
|
-
if (
|
|
7159
|
-
|
|
7160
|
-
"run `beeos init` and pick option [1] to upgrade CLI + agents to the latest npm release"
|
|
7161
|
-
);
|
|
7359
|
+
if (!options.json) {
|
|
7360
|
+
printNextSteps();
|
|
7162
7361
|
}
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
hints.push(`truncate safely: : > ${l.path}`);
|
|
7362
|
+
}
|
|
7363
|
+
async function decideAction(state, options) {
|
|
7364
|
+
const choice = inferInitChoices(state);
|
|
7365
|
+
if (choice.options.length === 1) {
|
|
7366
|
+
return choice.defaultDecision;
|
|
7169
7367
|
}
|
|
7170
|
-
if (options.json) {
|
|
7171
|
-
|
|
7172
|
-
jsonOk({
|
|
7173
|
-
platform: p.platform(),
|
|
7174
|
-
arch: process.arch,
|
|
7175
|
-
node: process.version,
|
|
7176
|
-
beeosHome: state.beeosHome,
|
|
7177
|
-
config: cfg,
|
|
7178
|
-
state,
|
|
7179
|
-
serviceManager: {
|
|
7180
|
-
kind: mgr.kind,
|
|
7181
|
-
available: serviceCheck.available,
|
|
7182
|
-
fallbackReason: fallbackReason2,
|
|
7183
|
-
services
|
|
7184
|
-
},
|
|
7185
|
-
agentGateway: agentGatewayHealth,
|
|
7186
|
-
tools,
|
|
7187
|
-
npmShims: shimReport,
|
|
7188
|
-
warnings,
|
|
7189
|
-
hints
|
|
7190
|
-
})
|
|
7191
|
-
);
|
|
7192
|
-
return;
|
|
7368
|
+
if (options.yes || options.json) {
|
|
7369
|
+
return choice.defaultDecision;
|
|
7193
7370
|
}
|
|
7371
|
+
if (!process.stdin.isTTY) {
|
|
7372
|
+
return choice.defaultDecision;
|
|
7373
|
+
}
|
|
7374
|
+
console.log("Detected existing install. Choose an action:");
|
|
7375
|
+
choice.options.forEach((d, i) => {
|
|
7376
|
+
const marker = d === choice.defaultDecision ? "*" : " ";
|
|
7377
|
+
console.log(` ${marker} [${i + 1}] ${initDecisionLabel(d)}`);
|
|
7378
|
+
});
|
|
7194
7379
|
console.log("");
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
console.log("");
|
|
7202
|
-
for (const line of summarizeExistingInstall(state)) {
|
|
7203
|
-
console.log(` ${line}`);
|
|
7380
|
+
const answer = await prompt(`Choose [1-${choice.options.length}] (default ${choice.options.indexOf(choice.defaultDecision) + 1}): `);
|
|
7381
|
+
const trimmed = answer.trim();
|
|
7382
|
+
if (!trimmed) return choice.defaultDecision;
|
|
7383
|
+
const idx = parseInt(trimmed, 10);
|
|
7384
|
+
if (!Number.isFinite(idx) || idx < 1 || idx > choice.options.length) {
|
|
7385
|
+
return choice.defaultDecision;
|
|
7204
7386
|
}
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
const pid = s.pid != null ? ` pid=${s.pid}` : "";
|
|
7211
|
-
console.log(` - ${s.id.padEnd(28)} ${status2}${pid}`);
|
|
7387
|
+
return choice.options[idx - 1];
|
|
7388
|
+
}
|
|
7389
|
+
async function decideFramework(state, decision, options) {
|
|
7390
|
+
if (options.framework && options.framework.trim()) {
|
|
7391
|
+
return options.framework.trim();
|
|
7212
7392
|
}
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
console.log(" npm shims:");
|
|
7218
|
-
for (const e of shimReport.entries) {
|
|
7219
|
-
const marker = shimMarker(e.outcome);
|
|
7220
|
-
const installed = e.installed ?? "(missing)";
|
|
7221
|
-
const latest = e.latest ?? "(unknown)";
|
|
7222
|
-
console.log(
|
|
7223
|
-
` ${marker} ${e.pkg.padEnd(34)} installed=${installed.padEnd(8)} npm=${latest}`
|
|
7224
|
-
);
|
|
7393
|
+
if (state.binding && decision === "upgrade") {
|
|
7394
|
+
const persisted = state.binding.framework?.trim();
|
|
7395
|
+
if (persisted) return persisted;
|
|
7396
|
+
return defaultFrameworkId();
|
|
7225
7397
|
}
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
console.log(" Warnings:");
|
|
7229
|
-
for (const w of warnings) {
|
|
7230
|
-
console.log(` - ${w}`);
|
|
7231
|
-
}
|
|
7232
|
-
console.log("");
|
|
7233
|
-
} else {
|
|
7234
|
-
console.log(" No warnings \u2014 machine looks good.\n");
|
|
7398
|
+
if (options.yes || options.json || !process.stdin.isTTY) {
|
|
7399
|
+
return defaultFrameworkId();
|
|
7235
7400
|
}
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7401
|
+
const all = listFrameworks();
|
|
7402
|
+
const avail = availableFrameworks();
|
|
7403
|
+
if (avail.length <= 1) {
|
|
7404
|
+
const only = avail[0];
|
|
7405
|
+
if (only) {
|
|
7406
|
+
console.log(`Installing ${only.displayName} (only available framework).`);
|
|
7407
|
+
return only.id;
|
|
7240
7408
|
}
|
|
7241
|
-
|
|
7409
|
+
return defaultFrameworkId();
|
|
7410
|
+
}
|
|
7411
|
+
console.log("");
|
|
7412
|
+
console.log("Choose agent framework:");
|
|
7413
|
+
const def = defaultFrameworkId();
|
|
7414
|
+
all.forEach((f, i) => {
|
|
7415
|
+
const marker = f.id === def ? "*" : " ";
|
|
7416
|
+
const tag = f.status === "coming-soon" ? " [coming soon]" : "";
|
|
7417
|
+
const padName = f.displayName.padEnd(12);
|
|
7418
|
+
console.log(` ${marker} [${i + 1}] ${padName} ${f.description}${tag}`);
|
|
7419
|
+
});
|
|
7420
|
+
console.log("");
|
|
7421
|
+
const defaultIdx = all.findIndex((f) => f.id === def) + 1;
|
|
7422
|
+
const answer = await prompt(`Choose [1-${all.length}] (default ${defaultIdx}): `);
|
|
7423
|
+
const trimmed = answer.trim();
|
|
7424
|
+
if (!trimmed) return def;
|
|
7425
|
+
const idx = parseInt(trimmed, 10);
|
|
7426
|
+
if (!Number.isFinite(idx) || idx < 1 || idx > all.length) {
|
|
7427
|
+
return def;
|
|
7428
|
+
}
|
|
7429
|
+
const picked = all[idx - 1];
|
|
7430
|
+
if (picked.status !== "available") {
|
|
7431
|
+
console.log(
|
|
7432
|
+
` ${picked.displayName} is not yet available. Falling back to ${def}.`
|
|
7433
|
+
);
|
|
7434
|
+
return def;
|
|
7242
7435
|
}
|
|
7436
|
+
return picked.id;
|
|
7243
7437
|
}
|
|
7244
|
-
async function
|
|
7245
|
-
const
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
scrcpyBridge: { path: scrcpyPath },
|
|
7253
|
-
vncBridge: { path: vncPath }
|
|
7254
|
-
};
|
|
7438
|
+
async function safeListAdbDevices() {
|
|
7439
|
+
const adbPath = await findAdb().catch(() => null);
|
|
7440
|
+
if (!adbPath) return [];
|
|
7441
|
+
try {
|
|
7442
|
+
return await deviceRuntime.listAdbDevices();
|
|
7443
|
+
} catch {
|
|
7444
|
+
return [];
|
|
7445
|
+
}
|
|
7255
7446
|
}
|
|
7256
|
-
async function
|
|
7257
|
-
const
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
outcome = deviceSuite.has(pkg) && !hasBoundDevices ? "not_installed_yet" : "missing";
|
|
7273
|
-
} else if (installed === latest) outcome = "ok";
|
|
7274
|
-
else outcome = "outdated";
|
|
7275
|
-
if (pkg === NPM_PKGS.CLI && installed === null) {
|
|
7276
|
-
outcome = "missing";
|
|
7277
|
-
}
|
|
7278
|
-
return { pkg, installed, latest, spec, outcome };
|
|
7279
|
-
})
|
|
7280
|
-
);
|
|
7281
|
-
return { entries };
|
|
7447
|
+
async function pickFreshInstance(_opts) {
|
|
7448
|
+
const frameworks = availableFrameworks().map((f) => ({
|
|
7449
|
+
kind: "framework",
|
|
7450
|
+
id: f.id,
|
|
7451
|
+
label: f.displayName,
|
|
7452
|
+
description: f.description,
|
|
7453
|
+
isDefault: f.id === defaultFrameworkId()
|
|
7454
|
+
}));
|
|
7455
|
+
const devices = (await safeListAdbDevices()).map((d) => ({
|
|
7456
|
+
kind: "device",
|
|
7457
|
+
serial: d.serial,
|
|
7458
|
+
status: d.status
|
|
7459
|
+
}));
|
|
7460
|
+
return pickInstanceInteractive([...frameworks, ...devices], {
|
|
7461
|
+
caller: "init"
|
|
7462
|
+
});
|
|
7282
7463
|
}
|
|
7283
|
-
function
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7464
|
+
async function maybePromptServiceInstall() {
|
|
7465
|
+
try {
|
|
7466
|
+
const mgr = await getServiceManager();
|
|
7467
|
+
maybePrintFallbackBanner(mgr.kind);
|
|
7468
|
+
const check = await mgr.selfCheck();
|
|
7469
|
+
if (!check.available) {
|
|
7470
|
+
console.log(` \u26A0 Service manager '${mgr.kind}' is not fully available \u2014 persistence limited.`);
|
|
7471
|
+
}
|
|
7472
|
+
for (const w of check.warnings) console.log(` ! ${w}`);
|
|
7473
|
+
for (const h of check.hints) console.log(` $ ${h}`);
|
|
7474
|
+
} catch (e) {
|
|
7475
|
+
console.log(` (service-manager self-check skipped: ${e instanceof Error ? e.message : String(e)})`);
|
|
7295
7476
|
}
|
|
7296
7477
|
}
|
|
7297
|
-
async function
|
|
7298
|
-
const
|
|
7299
|
-
|
|
7478
|
+
async function rotateIdentity() {
|
|
7479
|
+
const p = getPlatformAdapter();
|
|
7480
|
+
const home = beeoHome();
|
|
7481
|
+
const identityDir = p.joinPath(home, "identity");
|
|
7482
|
+
const keypair = p.joinPath(identityDir, "keypair.json");
|
|
7483
|
+
const fp = p.joinPath(identityDir, "fingerprint");
|
|
7484
|
+
if (!await p.exists(keypair)) return;
|
|
7485
|
+
const ts = Math.floor(Date.now() / 1e3);
|
|
7486
|
+
const backup = p.joinPath(identityDir, `keypair.json.backup-${ts}`);
|
|
7300
7487
|
try {
|
|
7301
|
-
|
|
7488
|
+
await p.copyFile(keypair, backup);
|
|
7302
7489
|
} catch {
|
|
7303
|
-
return [];
|
|
7304
7490
|
}
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
const full = path9.join(dir, name);
|
|
7309
|
-
try {
|
|
7310
|
-
const st = await fs9.stat(full);
|
|
7311
|
-
if (st.isFile() && st.size >= LOG_SIZE_WARN_BYTES) {
|
|
7312
|
-
bloated.push({ path: full, size: st.size });
|
|
7313
|
-
}
|
|
7314
|
-
} catch {
|
|
7315
|
-
}
|
|
7491
|
+
try {
|
|
7492
|
+
await p.rm(keypair);
|
|
7493
|
+
} catch {
|
|
7316
7494
|
}
|
|
7317
|
-
|
|
7495
|
+
try {
|
|
7496
|
+
await p.rm(fp);
|
|
7497
|
+
} catch {
|
|
7498
|
+
}
|
|
7499
|
+
console.log(`Rotated identity. Previous keypair saved at ${backup}`);
|
|
7318
7500
|
}
|
|
7319
|
-
function
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
return `${n}B`;
|
|
7501
|
+
function printBanner() {
|
|
7502
|
+
console.log("");
|
|
7503
|
+
console.log(" BeeOS init");
|
|
7504
|
+
console.log("");
|
|
7324
7505
|
}
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
method: "GET",
|
|
7333
|
-
signal: ac.signal,
|
|
7334
|
-
headers: { accept: "application/json, text/plain;q=0.9, */*;q=0.1" }
|
|
7335
|
-
});
|
|
7336
|
-
const latency = Date.now() - started;
|
|
7337
|
-
if (res.ok) {
|
|
7338
|
-
return {
|
|
7339
|
-
url,
|
|
7340
|
-
status: "ok",
|
|
7341
|
-
httpStatus: res.status,
|
|
7342
|
-
latencyMs: latency,
|
|
7343
|
-
detail: `HTTP ${res.status} in ${latency}ms`
|
|
7344
|
-
};
|
|
7345
|
-
}
|
|
7346
|
-
return {
|
|
7347
|
-
url,
|
|
7348
|
-
status: "unhealthy",
|
|
7349
|
-
httpStatus: res.status,
|
|
7350
|
-
latencyMs: latency,
|
|
7351
|
-
detail: `HTTP ${res.status} in ${latency}ms`
|
|
7352
|
-
};
|
|
7353
|
-
} catch (err) {
|
|
7354
|
-
const latency = Date.now() - started;
|
|
7355
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
7356
|
-
const timedOut = ac.signal.aborted;
|
|
7357
|
-
return {
|
|
7358
|
-
url,
|
|
7359
|
-
status: "unreachable",
|
|
7360
|
-
httpStatus: null,
|
|
7361
|
-
latencyMs: latency,
|
|
7362
|
-
detail: timedOut ? "timeout after 3s" : msg
|
|
7363
|
-
};
|
|
7364
|
-
} finally {
|
|
7365
|
-
clearTimeout(timer);
|
|
7366
|
-
}
|
|
7506
|
+
function printNextSteps() {
|
|
7507
|
+
console.log("");
|
|
7508
|
+
console.log("Next steps:");
|
|
7509
|
+
console.log(" beeos status # show running agents");
|
|
7510
|
+
console.log(" beeos device attach # bind a physical Android device");
|
|
7511
|
+
console.log(" beeos doctor # health check & diagnostics");
|
|
7512
|
+
console.log("");
|
|
7367
7513
|
}
|
|
7368
|
-
function
|
|
7369
|
-
const
|
|
7370
|
-
return
|
|
7514
|
+
function prompt(question) {
|
|
7515
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
7516
|
+
return new Promise((resolve) => {
|
|
7517
|
+
rl.question(question, (answer) => {
|
|
7518
|
+
rl.close();
|
|
7519
|
+
resolve(answer);
|
|
7520
|
+
});
|
|
7521
|
+
});
|
|
7371
7522
|
}
|
|
7372
|
-
function
|
|
7373
|
-
if (
|
|
7374
|
-
if (
|
|
7375
|
-
return
|
|
7523
|
+
async function shouldUpgradeBeeosSuite(options) {
|
|
7524
|
+
if (options.headless) return false;
|
|
7525
|
+
if (process.env.BEEOS_INIT_SKIP_NPM_UPGRADE === "1") return false;
|
|
7526
|
+
return true;
|
|
7376
7527
|
}
|
|
7377
|
-
|
|
7378
|
-
// src/commands/init.ts
|
|
7379
|
-
init_attach();
|
|
7380
|
-
init_progress2();
|
|
7381
|
-
init_fallback_banner();
|
|
7382
|
-
init_instance_picker();
|
|
7383
|
-
async function run8(options) {
|
|
7384
|
-
await ensureDirs();
|
|
7385
|
-
if (options.device) {
|
|
7386
|
-
const { attach: attach2 } = await Promise.resolve().then(() => (init_device2(), device_exports));
|
|
7387
|
-
await attach2({});
|
|
7388
|
-
return;
|
|
7389
|
-
}
|
|
7390
|
-
const state = await detectExistingInstall();
|
|
7528
|
+
async function upgradeAndMaybeExit(options) {
|
|
7391
7529
|
if (options.json) {
|
|
7392
|
-
|
|
7393
|
-
} else {
|
|
7394
|
-
printBanner();
|
|
7395
|
-
for (const line of summarizeExistingInstall(state)) {
|
|
7396
|
-
console.log(` ${line}`);
|
|
7397
|
-
}
|
|
7398
|
-
if (state.supervisor.targets.length > 0 || state.supervisor.ipcReachable) {
|
|
7399
|
-
console.log("");
|
|
7400
|
-
console.log(
|
|
7401
|
-
" Legacy Node supervisor state detected. Run `beeos migrate` to convert it to OS-native services."
|
|
7402
|
-
);
|
|
7403
|
-
}
|
|
7404
|
-
console.log("");
|
|
7405
|
-
}
|
|
7406
|
-
const decision = await decideAction(state, options);
|
|
7407
|
-
if (decision === "skip") {
|
|
7408
|
-
console.log("Nothing to do. Run `beeos init` again or `beeos device attach` when ready.");
|
|
7409
|
-
return;
|
|
7530
|
+
return false;
|
|
7410
7531
|
}
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7532
|
+
const packages = await pickInstalledPackages();
|
|
7533
|
+
console.log("Checking for newer @beeos-ai/cli on npm...");
|
|
7534
|
+
const reporter = new CliReporter();
|
|
7535
|
+
let outcome;
|
|
7536
|
+
try {
|
|
7537
|
+
outcome = await upgradeBeeosSuite({
|
|
7538
|
+
packages,
|
|
7539
|
+
progress: reporter
|
|
7540
|
+
});
|
|
7541
|
+
} catch (e) {
|
|
7542
|
+
reporter.stop();
|
|
7543
|
+
console.log(
|
|
7544
|
+
` (upgrade skipped \u2014 ${e instanceof Error ? e.message : String(e)})
|
|
7545
|
+
Tip: re-run \`npm install -g @beeos-ai/cli\` manually. The device
|
|
7546
|
+
suite (device-agent + device-mcp-server) will be auto-installed
|
|
7547
|
+
on the next \`beeos device attach\`.`
|
|
7548
|
+
);
|
|
7549
|
+
return false;
|
|
7417
7550
|
}
|
|
7418
|
-
|
|
7419
|
-
|
|
7551
|
+
reporter.stop();
|
|
7552
|
+
if (outcome.anyFailed) {
|
|
7553
|
+
const failed = outcome.packages.find((p) => p.failed)?.failed ?? "unknown error";
|
|
7554
|
+
console.log(` \u26A0 npm install failed: ${failed}`);
|
|
7555
|
+
console.log(
|
|
7556
|
+
" Manual fix:\n npm i -g @beeos-ai/cli\n (the device suite is auto-installed on `beeos device attach`)\n"
|
|
7557
|
+
);
|
|
7558
|
+
return false;
|
|
7420
7559
|
}
|
|
7421
|
-
if (
|
|
7422
|
-
|
|
7423
|
-
|
|
7560
|
+
if (!outcome.anyChanged) {
|
|
7561
|
+
console.log(" All BeeOS npm packages are up to date.\n");
|
|
7562
|
+
return false;
|
|
7424
7563
|
}
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
console.log("Skipped. Re-run `beeos init` or `beeos device attach` whenever ready.");
|
|
7429
|
-
return;
|
|
7430
|
-
}
|
|
7431
|
-
if (sel.kind === "device") {
|
|
7432
|
-
await attach({ serial: sel.serial });
|
|
7433
|
-
return;
|
|
7434
|
-
}
|
|
7435
|
-
const descriptor2 = frameworkById(sel.id);
|
|
7436
|
-
if (!descriptor2 || descriptor2.status !== "available") {
|
|
7437
|
-
throw new BeeosError({
|
|
7438
|
-
code: "framework_unavailable",
|
|
7439
|
-
message: `Agent framework '${sel.id}' is not available.`,
|
|
7440
|
-
hint: "Pick a different framework or reinstall the CLI.",
|
|
7441
|
-
details: { requested: sel.id, available: availableFrameworks().map((f) => f.id) }
|
|
7442
|
-
});
|
|
7443
|
-
}
|
|
7444
|
-
await run(descriptor2.id, {
|
|
7445
|
-
force: true,
|
|
7446
|
-
json: options.json,
|
|
7447
|
-
browser: options.browser !== false
|
|
7448
|
-
});
|
|
7449
|
-
if (!options.json && !options.skipServicePrompt) {
|
|
7450
|
-
await maybePromptServiceInstall();
|
|
7451
|
-
}
|
|
7452
|
-
if (!options.json) {
|
|
7453
|
-
printNextSteps();
|
|
7564
|
+
for (const pkg of outcome.packages) {
|
|
7565
|
+
if (pkg.changed) {
|
|
7566
|
+
console.log(` ${pkg.pkg}: ${pkg.before ?? "(none)"} \u2192 ${pkg.after}`);
|
|
7454
7567
|
}
|
|
7455
|
-
return;
|
|
7456
7568
|
}
|
|
7457
|
-
const
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
hint: avail.length > 0 ? `Re-run with --framework <one-of-the-above>` : "No frameworks are registered \u2014 reinstall the CLI.",
|
|
7465
|
-
details: { requested: frameworkId, available: availableFrameworks().map((f) => f.id) }
|
|
7466
|
-
});
|
|
7467
|
-
}
|
|
7468
|
-
await run(descriptor.id, {
|
|
7469
|
-
force: true,
|
|
7470
|
-
json: options.json,
|
|
7471
|
-
browser: options.browser !== false
|
|
7472
|
-
});
|
|
7473
|
-
if (!options.json && !options.skipServicePrompt) {
|
|
7474
|
-
await maybePromptServiceInstall();
|
|
7569
|
+
const cliEntry = outcome.packages.find((p) => p.pkg === NPM_PKGS.CLI);
|
|
7570
|
+
if (cliEntry?.changed) {
|
|
7571
|
+
console.log("");
|
|
7572
|
+
console.log(" CLI itself was upgraded \u2014 please re-run `beeos init` to continue");
|
|
7573
|
+
console.log(" with the new code. The current process is still running the old version.");
|
|
7574
|
+
console.log("");
|
|
7575
|
+
return true;
|
|
7475
7576
|
}
|
|
7476
|
-
|
|
7477
|
-
|
|
7577
|
+
console.log(" Agents updated. Continuing init...\n");
|
|
7578
|
+
return false;
|
|
7579
|
+
}
|
|
7580
|
+
async function pickInstalledPackages() {
|
|
7581
|
+
const packages = [NPM_PKGS.CLI];
|
|
7582
|
+
for (const pkg of [NPM_PKGS.DEVICE_AGENT, NPM_PKGS.DEVICE_MCP_SERVER]) {
|
|
7583
|
+
const installed = await npmGlobalVersion(pkg).catch(() => null);
|
|
7584
|
+
if (installed !== null) packages.push(pkg);
|
|
7478
7585
|
}
|
|
7586
|
+
return packages;
|
|
7479
7587
|
}
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7588
|
+
|
|
7589
|
+
// src/commands/doctor.ts
|
|
7590
|
+
init_dist();
|
|
7591
|
+
init_json_envelope();
|
|
7592
|
+
import fs9 from "fs/promises";
|
|
7593
|
+
import path9 from "path";
|
|
7594
|
+
var LOG_SIZE_WARN_BYTES = 50 * 1024 * 1024;
|
|
7595
|
+
async function run8(options) {
|
|
7596
|
+
const state = await detectExistingInstall();
|
|
7597
|
+
const cfg = await loadOrCreateConfig();
|
|
7598
|
+
const p = getPlatformAdapter();
|
|
7599
|
+
const mgr = await getServiceManager();
|
|
7600
|
+
const fallbackReason2 = activeFallbackReason();
|
|
7601
|
+
const serviceCheck = await mgr.selfCheck();
|
|
7602
|
+
let services = [];
|
|
7603
|
+
try {
|
|
7604
|
+
services = await mgr.list();
|
|
7605
|
+
} catch {
|
|
7484
7606
|
}
|
|
7485
|
-
|
|
7486
|
-
|
|
7607
|
+
const resolvedAgentGatewayUrl = resolveAgentGatewayUrl(cfg);
|
|
7608
|
+
const agentGatewayHealth = await probeAgentGateway(resolvedAgentGatewayUrl);
|
|
7609
|
+
const tools = await collectToolStatus();
|
|
7610
|
+
const hasBoundDevices = state.devices.entries.length > 0;
|
|
7611
|
+
const shimReport = await collectShimReport(hasBoundDevices);
|
|
7612
|
+
const warnings = [];
|
|
7613
|
+
const hints = [];
|
|
7614
|
+
if (!state.hasIdentity) {
|
|
7615
|
+
warnings.push("identity keypair missing \u2014 will be generated on next bind");
|
|
7487
7616
|
}
|
|
7488
|
-
if (!
|
|
7489
|
-
|
|
7617
|
+
if (state.openclaw.gatewayRunning && !state.openclaw.found) {
|
|
7618
|
+
warnings.push("port 18789 is in use but no OpenClaw install detected \u2014 another app may conflict");
|
|
7490
7619
|
}
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
const trimmed = answer.trim();
|
|
7499
|
-
if (!trimmed) return choice.defaultDecision;
|
|
7500
|
-
const idx = parseInt(trimmed, 10);
|
|
7501
|
-
if (!Number.isFinite(idx) || idx < 1 || idx > choice.options.length) {
|
|
7502
|
-
return choice.defaultDecision;
|
|
7620
|
+
if (state.openclaw.found && state.openclaw.pluginInstalled === false) {
|
|
7621
|
+
warnings.push(
|
|
7622
|
+
`OpenClaw is installed at ${state.openclaw.home ?? "?"} but the beeos-claw plugin is not registered. BeeOS-specific tools may be unavailable to agents until this is fixed.`
|
|
7623
|
+
);
|
|
7624
|
+
hints.push(
|
|
7625
|
+
"re-run `beeos start openclaw --force` to re-install the plugin, or check ~/.beeos/logs/services/openclaw.log for the original failure"
|
|
7626
|
+
);
|
|
7503
7627
|
}
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
return options.framework.trim();
|
|
7628
|
+
if (state.devices.entries.length > 0 && services.length === 0) {
|
|
7629
|
+
warnings.push(
|
|
7630
|
+
"devices tracked via legacy devices.json \u2014 no OS services registered. Re-run `beeos device attach` to migrate them to the OS service manager."
|
|
7631
|
+
);
|
|
7509
7632
|
}
|
|
7510
|
-
if (state.
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7633
|
+
if (state.supervisor.targets.length > 0 || state.supervisor.ipcReachable) {
|
|
7634
|
+
warnings.push(
|
|
7635
|
+
"legacy Node supervisor state detected \u2014 run `beeos migrate` to convert it to OS-native services."
|
|
7636
|
+
);
|
|
7514
7637
|
}
|
|
7515
|
-
if (
|
|
7516
|
-
|
|
7638
|
+
if (agentGatewayHealth.status !== "ok") {
|
|
7639
|
+
warnings.push(
|
|
7640
|
+
`Agent Gateway unreachable at ${resolvedAgentGatewayUrl}: ${agentGatewayHealth.detail}. Binding and task delivery will fail until this resolves.`
|
|
7641
|
+
);
|
|
7642
|
+
hints.push(
|
|
7643
|
+
"check BEEOS_AGENT_GATEWAY_URL / platform.agent_gateway_url in ~/.beeos/config.toml"
|
|
7644
|
+
);
|
|
7517
7645
|
}
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7646
|
+
if (fallbackReason2) {
|
|
7647
|
+
warnings.push(
|
|
7648
|
+
`No native OS service manager available (${fallbackReason2}) \u2014 using fallback, services WON'T persist across reboots.`
|
|
7649
|
+
);
|
|
7650
|
+
}
|
|
7651
|
+
for (const w of serviceCheck.warnings) warnings.push(w);
|
|
7652
|
+
for (const h of serviceCheck.hints) hints.push(h);
|
|
7653
|
+
for (const s of services) {
|
|
7654
|
+
if (!s.running && s.installed) {
|
|
7655
|
+
warnings.push(
|
|
7656
|
+
`service '${s.id}' is installed but not running \u2014 see log ${s.logFile}`
|
|
7657
|
+
);
|
|
7658
|
+
}
|
|
7659
|
+
if (s.lastExitCode !== null && s.lastExitCode !== 0) {
|
|
7660
|
+
warnings.push(
|
|
7661
|
+
`service '${s.id}' last exited with code ${s.lastExitCode} \u2014 see log ${s.logFile}`
|
|
7662
|
+
);
|
|
7663
|
+
if (s.lastExitCode === 127) {
|
|
7664
|
+
hints.push(
|
|
7665
|
+
`service '${s.id}' looks like its Node binary moved (exit 127). Re-run \`beeos start ${s.id} --force\` to refresh the service definition with the current node path.`
|
|
7666
|
+
);
|
|
7667
|
+
}
|
|
7668
|
+
if (s.id === `vnc-bridge-${OPENCLAW_VNC_BRIDGE_SERIAL}`) {
|
|
7669
|
+
if (process.platform === "darwin") {
|
|
7670
|
+
for (const line of MACOS_DESKTOP_DOCTOR_HINT_LINES) {
|
|
7671
|
+
hints.push(line);
|
|
7672
|
+
}
|
|
7673
|
+
} else {
|
|
7674
|
+
hints.push(
|
|
7675
|
+
`vnc-bridge-${OPENCLAW_VNC_BRIDGE_SERIAL} is failing \u2014 see ${s.logFile} for the underlying error. Common causes: VNC server moved port, BEEOS_VNC_PASSWORD missing/incorrect, network blocking outbound MQTT.`
|
|
7676
|
+
);
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
if (!tools.adb.path) {
|
|
7682
|
+
warnings.push(
|
|
7683
|
+
"adb not found \u2014 `beeos device attach` will download Android platform-tools on first use"
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
for (const e of shimReport.entries) {
|
|
7687
|
+
if (e.outcome === "outdated") {
|
|
7688
|
+
warnings.push(
|
|
7689
|
+
`${e.pkg}: installed ${e.installed ?? "(missing)"}, npm latest ${e.latest}.`
|
|
7690
|
+
);
|
|
7691
|
+
} else if (e.outcome === "missing") {
|
|
7692
|
+
warnings.push(`${e.pkg}: not installed globally \u2014 run 'beeos init' to install.`);
|
|
7693
|
+
} else if (e.outcome === "error" || e.outcome === "not_installed_yet") {
|
|
7525
7694
|
}
|
|
7526
|
-
|
|
7695
|
+
}
|
|
7696
|
+
if (shimReport.entries.some((e) => e.outcome === "outdated" || e.outcome === "missing")) {
|
|
7697
|
+
hints.push(
|
|
7698
|
+
"run `beeos init` and pick option [1] to upgrade CLI + agents to the latest npm release"
|
|
7699
|
+
);
|
|
7700
|
+
}
|
|
7701
|
+
const bloatedLogs = await checkLogFileSizes();
|
|
7702
|
+
for (const l of bloatedLogs) {
|
|
7703
|
+
warnings.push(
|
|
7704
|
+
`Service log large: ${path9.basename(l.path)} = ${formatBytes(l.size)}`
|
|
7705
|
+
);
|
|
7706
|
+
hints.push(`truncate safely: : > ${l.path}`);
|
|
7707
|
+
}
|
|
7708
|
+
if (options.json) {
|
|
7709
|
+
emitJsonEnvelope(
|
|
7710
|
+
jsonOk({
|
|
7711
|
+
platform: p.platform(),
|
|
7712
|
+
arch: process.arch,
|
|
7713
|
+
node: process.version,
|
|
7714
|
+
beeosHome: state.beeosHome,
|
|
7715
|
+
config: cfg,
|
|
7716
|
+
state,
|
|
7717
|
+
serviceManager: {
|
|
7718
|
+
kind: mgr.kind,
|
|
7719
|
+
available: serviceCheck.available,
|
|
7720
|
+
fallbackReason: fallbackReason2,
|
|
7721
|
+
services
|
|
7722
|
+
},
|
|
7723
|
+
agentGateway: agentGatewayHealth,
|
|
7724
|
+
tools,
|
|
7725
|
+
npmShims: shimReport,
|
|
7726
|
+
warnings,
|
|
7727
|
+
hints
|
|
7728
|
+
})
|
|
7729
|
+
);
|
|
7730
|
+
return;
|
|
7527
7731
|
}
|
|
7528
7732
|
console.log("");
|
|
7529
|
-
console.log(
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
console.log(` ${marker} [${i + 1}] ${padName} ${f.description}${tag}`);
|
|
7536
|
-
});
|
|
7733
|
+
console.log(` OS/Arch : ${p.platform()}/${process.arch}`);
|
|
7734
|
+
console.log(` Node.js : ${process.version}`);
|
|
7735
|
+
console.log(` API URL : ${cfg.platform.api_url}`);
|
|
7736
|
+
console.log(
|
|
7737
|
+
` AgentGw : ${resolvedAgentGatewayUrl} [${formatAgentGatewayStatus(agentGatewayHealth)}]`
|
|
7738
|
+
);
|
|
7537
7739
|
console.log("");
|
|
7538
|
-
const
|
|
7539
|
-
|
|
7540
|
-
const trimmed = answer.trim();
|
|
7541
|
-
if (!trimmed) return def;
|
|
7542
|
-
const idx = parseInt(trimmed, 10);
|
|
7543
|
-
if (!Number.isFinite(idx) || idx < 1 || idx > all.length) {
|
|
7544
|
-
return def;
|
|
7740
|
+
for (const line of summarizeExistingInstall(state)) {
|
|
7741
|
+
console.log(` ${line}`);
|
|
7545
7742
|
}
|
|
7546
|
-
|
|
7547
|
-
|
|
7743
|
+
console.log(
|
|
7744
|
+
` Service manager: ${mgr.kind}${fallbackReason2 ? " (fallback)" : ""} \u2014 ${services.length} service(s) registered`
|
|
7745
|
+
);
|
|
7746
|
+
for (const s of services) {
|
|
7747
|
+
const status2 = s.running ? "running" : s.installed ? "stopped" : "uninstalled";
|
|
7748
|
+
const pid = s.pid != null ? ` pid=${s.pid}` : "";
|
|
7749
|
+
console.log(` - ${s.id.padEnd(28)} ${status2}${pid}`);
|
|
7750
|
+
}
|
|
7751
|
+
console.log(` adb : ${tools.adb.path ?? "not found"}`);
|
|
7752
|
+
console.log(` scrcpy-bridge: ${tools.scrcpyBridge.path ?? "not installed (auto on attach)"}`);
|
|
7753
|
+
console.log(` vnc-bridge : ${tools.vncBridge.path ?? "not installed (auto on --vnc-host)"}`);
|
|
7754
|
+
console.log("");
|
|
7755
|
+
console.log(" npm shims:");
|
|
7756
|
+
for (const e of shimReport.entries) {
|
|
7757
|
+
const marker = shimMarker(e.outcome);
|
|
7758
|
+
const installed = e.installed ?? "(missing)";
|
|
7759
|
+
const latest = e.latest ?? "(unknown)";
|
|
7548
7760
|
console.log(
|
|
7549
|
-
`
|
|
7761
|
+
` ${marker} ${e.pkg.padEnd(34)} installed=${installed.padEnd(8)} npm=${latest}`
|
|
7550
7762
|
);
|
|
7551
|
-
return def;
|
|
7552
7763
|
}
|
|
7553
|
-
|
|
7764
|
+
console.log("");
|
|
7765
|
+
if (warnings.length > 0) {
|
|
7766
|
+
console.log(" Warnings:");
|
|
7767
|
+
for (const w of warnings) {
|
|
7768
|
+
console.log(` - ${w}`);
|
|
7769
|
+
}
|
|
7770
|
+
console.log("");
|
|
7771
|
+
} else {
|
|
7772
|
+
console.log(" No warnings \u2014 machine looks good.\n");
|
|
7773
|
+
}
|
|
7774
|
+
if (hints.length > 0) {
|
|
7775
|
+
console.log(" Hints:");
|
|
7776
|
+
for (const h of hints) {
|
|
7777
|
+
console.log(` $ ${h}`);
|
|
7778
|
+
}
|
|
7779
|
+
console.log("");
|
|
7780
|
+
}
|
|
7554
7781
|
}
|
|
7555
|
-
async function
|
|
7556
|
-
const adbPath = await
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7782
|
+
async function collectToolStatus() {
|
|
7783
|
+
const [adbPath, scrcpyPath, vncPath] = await Promise.all([
|
|
7784
|
+
findAdb().catch(() => null),
|
|
7785
|
+
scrcpyBridgeRuntime.findBinary().catch(() => null),
|
|
7786
|
+
vncBridgeRuntime.findBinary().catch(() => null)
|
|
7787
|
+
]);
|
|
7788
|
+
return {
|
|
7789
|
+
adb: { path: adbPath },
|
|
7790
|
+
scrcpyBridge: { path: scrcpyPath },
|
|
7791
|
+
vncBridge: { path: vncPath }
|
|
7792
|
+
};
|
|
7563
7793
|
}
|
|
7564
|
-
async function
|
|
7565
|
-
const
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7794
|
+
async function collectShimReport(hasBoundDevices) {
|
|
7795
|
+
const sources = readPinSourcesFromEnv();
|
|
7796
|
+
const deviceSuite = /* @__PURE__ */ new Set([
|
|
7797
|
+
NPM_PKGS.DEVICE_AGENT,
|
|
7798
|
+
NPM_PKGS.DEVICE_MCP_SERVER
|
|
7799
|
+
]);
|
|
7800
|
+
const entries = await Promise.all(
|
|
7801
|
+
ALL_BEEOS_PKGS.map(async (pkg) => {
|
|
7802
|
+
const spec = resolveInstallSpec(pkg, sources);
|
|
7803
|
+
const [installed, latest] = await Promise.all([
|
|
7804
|
+
npmGlobalVersion(pkg).catch(() => null),
|
|
7805
|
+
npmRegistryVersion(spec).catch(() => null)
|
|
7806
|
+
]);
|
|
7807
|
+
let outcome;
|
|
7808
|
+
if (latest === null) outcome = "error";
|
|
7809
|
+
else if (installed === null) {
|
|
7810
|
+
outcome = deviceSuite.has(pkg) && !hasBoundDevices ? "not_installed_yet" : "missing";
|
|
7811
|
+
} else if (installed === latest) outcome = "ok";
|
|
7812
|
+
else outcome = "outdated";
|
|
7813
|
+
if (pkg === NPM_PKGS.CLI && installed === null) {
|
|
7814
|
+
outcome = "missing";
|
|
7815
|
+
}
|
|
7816
|
+
return { pkg, installed, latest, spec, outcome };
|
|
7817
|
+
})
|
|
7818
|
+
);
|
|
7819
|
+
return { entries };
|
|
7580
7820
|
}
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7821
|
+
function shimMarker(outcome) {
|
|
7822
|
+
switch (outcome) {
|
|
7823
|
+
case "ok":
|
|
7824
|
+
return "\u2713";
|
|
7825
|
+
case "outdated":
|
|
7826
|
+
return "\u2191";
|
|
7827
|
+
case "missing":
|
|
7828
|
+
return "\u2717";
|
|
7829
|
+
case "not_installed_yet":
|
|
7830
|
+
return "\xB7";
|
|
7831
|
+
case "error":
|
|
7832
|
+
return "?";
|
|
7593
7833
|
}
|
|
7594
7834
|
}
|
|
7595
|
-
async function
|
|
7596
|
-
const
|
|
7597
|
-
|
|
7598
|
-
const identityDir = p.joinPath(home, "identity");
|
|
7599
|
-
const keypair = p.joinPath(identityDir, "keypair.json");
|
|
7600
|
-
const fp = p.joinPath(identityDir, "fingerprint");
|
|
7601
|
-
if (!await p.exists(keypair)) return;
|
|
7602
|
-
const ts = Math.floor(Date.now() / 1e3);
|
|
7603
|
-
const backup = p.joinPath(identityDir, `keypair.json.backup-${ts}`);
|
|
7604
|
-
try {
|
|
7605
|
-
await p.copyFile(keypair, backup);
|
|
7606
|
-
} catch {
|
|
7607
|
-
}
|
|
7835
|
+
async function checkLogFileSizes() {
|
|
7836
|
+
const dir = path9.join(beeoHome(), "logs", "services");
|
|
7837
|
+
let entries;
|
|
7608
7838
|
try {
|
|
7609
|
-
await
|
|
7839
|
+
entries = await fs9.readdir(dir);
|
|
7610
7840
|
} catch {
|
|
7841
|
+
return [];
|
|
7611
7842
|
}
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7843
|
+
const bloated = [];
|
|
7844
|
+
for (const name of entries) {
|
|
7845
|
+
if (!name.endsWith(".log")) continue;
|
|
7846
|
+
const full = path9.join(dir, name);
|
|
7847
|
+
try {
|
|
7848
|
+
const st = await fs9.stat(full);
|
|
7849
|
+
if (st.isFile() && st.size >= LOG_SIZE_WARN_BYTES) {
|
|
7850
|
+
bloated.push({ path: full, size: st.size });
|
|
7851
|
+
}
|
|
7852
|
+
} catch {
|
|
7853
|
+
}
|
|
7615
7854
|
}
|
|
7616
|
-
|
|
7617
|
-
}
|
|
7618
|
-
function printBanner() {
|
|
7619
|
-
console.log("");
|
|
7620
|
-
console.log(" BeeOS init");
|
|
7621
|
-
console.log("");
|
|
7622
|
-
}
|
|
7623
|
-
function printNextSteps() {
|
|
7624
|
-
console.log("");
|
|
7625
|
-
console.log("Next steps:");
|
|
7626
|
-
console.log(" beeos status # show running agents");
|
|
7627
|
-
console.log(" beeos device attach # bind a physical Android device");
|
|
7628
|
-
console.log(" beeos doctor # health check & diagnostics");
|
|
7629
|
-
console.log("");
|
|
7630
|
-
}
|
|
7631
|
-
function prompt(question) {
|
|
7632
|
-
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
7633
|
-
return new Promise((resolve) => {
|
|
7634
|
-
rl.question(question, (answer) => {
|
|
7635
|
-
rl.close();
|
|
7636
|
-
resolve(answer);
|
|
7637
|
-
});
|
|
7638
|
-
});
|
|
7855
|
+
return bloated;
|
|
7639
7856
|
}
|
|
7640
|
-
|
|
7641
|
-
if (
|
|
7642
|
-
if (
|
|
7643
|
-
return
|
|
7857
|
+
function formatBytes(n) {
|
|
7858
|
+
if (n >= 1024 * 1024 * 1024) return `${(n / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
7859
|
+
if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(0)}MB`;
|
|
7860
|
+
if (n >= 1024) return `${(n / 1024).toFixed(0)}KB`;
|
|
7861
|
+
return `${n}B`;
|
|
7644
7862
|
}
|
|
7645
|
-
async function
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
const
|
|
7650
|
-
console.log("Checking for newer @beeos-ai/cli on npm...");
|
|
7651
|
-
const reporter = new CliReporter();
|
|
7652
|
-
let outcome;
|
|
7863
|
+
async function probeAgentGateway(baseUrl) {
|
|
7864
|
+
const url = joinHealthzUrl(baseUrl);
|
|
7865
|
+
const started = Date.now();
|
|
7866
|
+
const ac = new AbortController();
|
|
7867
|
+
const timer = setTimeout(() => ac.abort(), 3e3);
|
|
7653
7868
|
try {
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7869
|
+
const res = await fetch(url, {
|
|
7870
|
+
method: "GET",
|
|
7871
|
+
signal: ac.signal,
|
|
7872
|
+
headers: { accept: "application/json, text/plain;q=0.9, */*;q=0.1" }
|
|
7657
7873
|
});
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
}
|
|
7668
|
-
reporter.stop();
|
|
7669
|
-
if (outcome.anyFailed) {
|
|
7670
|
-
const failed = outcome.packages.find((p) => p.failed)?.failed ?? "unknown error";
|
|
7671
|
-
console.log(` \u26A0 npm install failed: ${failed}`);
|
|
7672
|
-
console.log(
|
|
7673
|
-
" Manual fix:\n npm i -g @beeos-ai/cli\n (the device suite is auto-installed on `beeos device attach`)\n"
|
|
7674
|
-
);
|
|
7675
|
-
return false;
|
|
7676
|
-
}
|
|
7677
|
-
if (!outcome.anyChanged) {
|
|
7678
|
-
console.log(" All BeeOS npm packages are up to date.\n");
|
|
7679
|
-
return false;
|
|
7680
|
-
}
|
|
7681
|
-
for (const pkg of outcome.packages) {
|
|
7682
|
-
if (pkg.changed) {
|
|
7683
|
-
console.log(` ${pkg.pkg}: ${pkg.before ?? "(none)"} \u2192 ${pkg.after}`);
|
|
7874
|
+
const latency = Date.now() - started;
|
|
7875
|
+
if (res.ok) {
|
|
7876
|
+
return {
|
|
7877
|
+
url,
|
|
7878
|
+
status: "ok",
|
|
7879
|
+
httpStatus: res.status,
|
|
7880
|
+
latencyMs: latency,
|
|
7881
|
+
detail: `HTTP ${res.status} in ${latency}ms`
|
|
7882
|
+
};
|
|
7684
7883
|
}
|
|
7884
|
+
return {
|
|
7885
|
+
url,
|
|
7886
|
+
status: "unhealthy",
|
|
7887
|
+
httpStatus: res.status,
|
|
7888
|
+
latencyMs: latency,
|
|
7889
|
+
detail: `HTTP ${res.status} in ${latency}ms`
|
|
7890
|
+
};
|
|
7891
|
+
} catch (err) {
|
|
7892
|
+
const latency = Date.now() - started;
|
|
7893
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7894
|
+
const timedOut = ac.signal.aborted;
|
|
7895
|
+
return {
|
|
7896
|
+
url,
|
|
7897
|
+
status: "unreachable",
|
|
7898
|
+
httpStatus: null,
|
|
7899
|
+
latencyMs: latency,
|
|
7900
|
+
detail: timedOut ? "timeout after 3s" : msg
|
|
7901
|
+
};
|
|
7902
|
+
} finally {
|
|
7903
|
+
clearTimeout(timer);
|
|
7685
7904
|
}
|
|
7686
|
-
const cliEntry = outcome.packages.find((p) => p.pkg === NPM_PKGS.CLI);
|
|
7687
|
-
if (cliEntry?.changed) {
|
|
7688
|
-
console.log("");
|
|
7689
|
-
console.log(" CLI itself was upgraded \u2014 please re-run `beeos init` to continue");
|
|
7690
|
-
console.log(" with the new code. The current process is still running the old version.");
|
|
7691
|
-
console.log("");
|
|
7692
|
-
return true;
|
|
7693
|
-
}
|
|
7694
|
-
console.log(" Agents updated. Continuing init...\n");
|
|
7695
|
-
return false;
|
|
7696
7905
|
}
|
|
7697
|
-
|
|
7698
|
-
const
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
}
|
|
7703
|
-
return
|
|
7906
|
+
function joinHealthzUrl(base) {
|
|
7907
|
+
const trimmed = base.replace(/\/+$/, "");
|
|
7908
|
+
return `${trimmed}/healthz`;
|
|
7909
|
+
}
|
|
7910
|
+
function formatAgentGatewayStatus(h) {
|
|
7911
|
+
if (h.status === "ok") return `ok ${h.latencyMs}ms`;
|
|
7912
|
+
if (h.status === "unhealthy") return `HTTP ${h.httpStatus ?? "?"}`;
|
|
7913
|
+
return h.detail;
|
|
7704
7914
|
}
|
|
7705
7915
|
|
|
7706
7916
|
// src/commands/service.ts
|
|
@@ -7924,8 +8134,8 @@ setPlatformAdapter(new NodePlatformAdapter());
|
|
|
7924
8134
|
var program = new Command();
|
|
7925
8135
|
var cliVersion = getCliVersion(import.meta.url, resolveActiveDistTag());
|
|
7926
8136
|
program.name("beeos").version(cliVersion.display).description("BeeOS \u2014 run AI agents from your desktop");
|
|
7927
|
-
program.command("init").description("One-shot install + bind flow (default entry from the curl installer)").option("--framework <name>", "Agent framework to install (default: openclaw)").option("--yes", "Non-interactive \u2014 accept the default action", false).option("--json", "Output machine-readable JSON (implies --yes)", false).option("--no-browser", "Don't auto-open a browser for bind confirmation").option("--headless", "Never open a browser (use terminal QR only)", false).option("--skip-service-prompt", "Skip the system-service install prompt", false).option("--device", "Jump straight into `beeos device attach` instead", false).action((opts) =>
|
|
7928
|
-
program.command("doctor").description("Inspect local BeeOS state (install, binding, services, warnings)").option("--json", "Output machine-readable JSON", false).action((opts) =>
|
|
8137
|
+
program.command("init").description("One-shot install + bind flow (default entry from the curl installer)").option("--framework <name>", "Agent framework to install (default: openclaw)").option("--yes", "Non-interactive \u2014 accept the default action", false).option("--json", "Output machine-readable JSON (implies --yes)", false).option("--no-browser", "Don't auto-open a browser for bind confirmation").option("--headless", "Never open a browser (use terminal QR only)", false).option("--skip-service-prompt", "Skip the system-service install prompt", false).option("--device", "Jump straight into `beeos device attach` instead", false).action((opts) => run7(opts));
|
|
8138
|
+
program.command("doctor").description("Inspect local BeeOS state (install, binding, services, warnings)").option("--json", "Output machine-readable JSON", false).action((opts) => run8(opts));
|
|
7929
8139
|
program.command("migrate").description("Migrate legacy Node supervisor state to OS-native services (one-shot)").option("--json", "Output machine-readable JSON", false).action((opts) => run9(opts));
|
|
7930
8140
|
program.command("logs").description(
|
|
7931
8141
|
"Tail logs for a BeeOS service. Pass an ADB serial or service id; omit to list every registered service's log file path."
|