@beeos-ai/cli 1.0.7 → 1.0.9
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 +2871 -1949
- package/package.json +1 -1
- package/scripts/install.ps1 +139 -1
- package/scripts/install.sh +198 -16
package/package.json
CHANGED
package/scripts/install.ps1
CHANGED
|
@@ -33,6 +33,26 @@ $MinNodeVersion = 18
|
|
|
33
33
|
|
|
34
34
|
$CliPackage = if ($Version -eq "latest") { "@beeos-ai/cli@latest" } else { "@beeos-ai/cli@$Version" }
|
|
35
35
|
|
|
36
|
+
# Sibling packages installed alongside the CLI so that `beeos device attach`
|
|
37
|
+
# works on a clean machine without further `npm i -g` steps (a.k.a. the
|
|
38
|
+
# "fleet ready" install strategy — see agents/device-agent/ARCHITECTURE.md
|
|
39
|
+
# for the 0.2.0+ sibling-pair topology between device-agent and
|
|
40
|
+
# device-mcp-server). Versions are independent of `-Version` (which only
|
|
41
|
+
# pins the CLI) — both packages follow their own semver. Override via
|
|
42
|
+
# $env:BEEOS_DEVICE_AGENT_VERSION / $env:BEEOS_MCP_SERVER_VERSION when you
|
|
43
|
+
# need to pin a specific build (e.g. for a staging smoke test).
|
|
44
|
+
$DeviceAgentVersion = if ($env:BEEOS_DEVICE_AGENT_VERSION) { $env:BEEOS_DEVICE_AGENT_VERSION } else { "latest" }
|
|
45
|
+
$McpServerVersion = if ($env:BEEOS_MCP_SERVER_VERSION) { $env:BEEOS_MCP_SERVER_VERSION } else { "latest" }
|
|
46
|
+
$DeviceAgentPackage = "@beeos-ai/device-agent@$DeviceAgentVersion"
|
|
47
|
+
$McpServerPackage = "@beeos-ai/device-mcp-server@$McpServerVersion"
|
|
48
|
+
|
|
49
|
+
# Telemetry endpoint (override with $env:BEEOS_API_URL for staging).
|
|
50
|
+
if (-not $env:BEEOS_API_URL -or $env:BEEOS_API_URL.Length -eq 0) {
|
|
51
|
+
$script:BeeosApiUrl = "https://public-api.beeos.ai"
|
|
52
|
+
} else {
|
|
53
|
+
$script:BeeosApiUrl = $env:BEEOS_API_URL
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
# ── Helpers ──────────────────────────────────────────────────
|
|
37
57
|
|
|
38
58
|
function Write-BeeInfo { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Cyan }
|
|
@@ -40,14 +60,87 @@ function Write-BeeWarn { param([string]$Msg) Write-Host "[beeos] $Msg" -Foregro
|
|
|
40
60
|
function Write-BeeError { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Red }
|
|
41
61
|
function Write-BeeOk { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Green }
|
|
42
62
|
|
|
63
|
+
# ── Script-level telemetry (mirror of install.sh send_telemetry) ──
|
|
64
|
+
#
|
|
65
|
+
# Fire-and-forget POST to `/api/v1/telemetry/install`, capped at 2 s,
|
|
66
|
+
# error-swallowing. Matches `web/packages/cli/src/telemetry.ts` event
|
|
67
|
+
# names so dashboard queries don't have to special-case Windows.
|
|
68
|
+
#
|
|
69
|
+
# Opt-out: `$env:BEEOS_NO_TELEMETRY = "1"`.
|
|
70
|
+
function Send-Telemetry {
|
|
71
|
+
param(
|
|
72
|
+
[string]$Event,
|
|
73
|
+
[string]$ErrorCode = "",
|
|
74
|
+
[bool]$Success = $true
|
|
75
|
+
)
|
|
76
|
+
if ($env:BEEOS_NO_TELEMETRY -eq "1") { return }
|
|
77
|
+
|
|
78
|
+
$osArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE }
|
|
79
|
+
if (-not $osArch) { $osArch = "unknown" }
|
|
80
|
+
$nodeVer = ""
|
|
81
|
+
try {
|
|
82
|
+
$raw = & node -v 2>$null
|
|
83
|
+
if ($raw) { $nodeVer = $raw -replace '^v', '' }
|
|
84
|
+
} catch {}
|
|
85
|
+
|
|
86
|
+
$hostname = $env:COMPUTERNAME
|
|
87
|
+
if (-not $hostname) { $hostname = "unknown" }
|
|
88
|
+
|
|
89
|
+
$payload = @{
|
|
90
|
+
event = $Event
|
|
91
|
+
cli_version = ""
|
|
92
|
+
dist_tag = ""
|
|
93
|
+
os = "win32"
|
|
94
|
+
arch = $osArch
|
|
95
|
+
node_version = $nodeVer
|
|
96
|
+
success = $Success
|
|
97
|
+
error_code = $ErrorCode
|
|
98
|
+
host_hint = "$hostname-$osArch"
|
|
99
|
+
} | ConvertTo-Json -Compress
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
# `-TimeoutSec 2` + `-UseBasicParsing` mirrors `curl --max-time 2`.
|
|
103
|
+
# Errors are fully swallowed (no Stop propagation).
|
|
104
|
+
Invoke-RestMethod `
|
|
105
|
+
-Method POST `
|
|
106
|
+
-Uri "$($script:BeeosApiUrl.TrimEnd('/'))/api/v1/telemetry/install" `
|
|
107
|
+
-ContentType "application/json" `
|
|
108
|
+
-Body $payload `
|
|
109
|
+
-TimeoutSec 2 `
|
|
110
|
+
-UseBasicParsing `
|
|
111
|
+
-ErrorAction SilentlyContinue | Out-Null
|
|
112
|
+
} catch {
|
|
113
|
+
# Telemetry MUST NOT break installs.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
43
117
|
# ── Platform detection ───────────────────────────────────────
|
|
44
118
|
|
|
45
119
|
function Test-Platform {
|
|
46
120
|
$arch = $env:PROCESSOR_ARCHITECTURE
|
|
47
121
|
$effectiveArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $arch }
|
|
48
122
|
Write-BeeInfo "detected: win32 $effectiveArch"
|
|
123
|
+
|
|
124
|
+
# Architecture policy (kept in sync with install.sh):
|
|
125
|
+
# - Non-interactive: hard exit 2 — silent warnings are invisible
|
|
126
|
+
# in piped installs (`irm | iex`) and would trap users on a
|
|
127
|
+
# platform we don't ship native sidecars for.
|
|
128
|
+
# - Interactive: warn and require explicit confirmation.
|
|
49
129
|
if ($effectiveArch -notin @("AMD64","ARM64","x86_64")) {
|
|
50
|
-
|
|
130
|
+
$reason = "Unsupported Windows architecture: $effectiveArch (need AMD64 or ARM64)"
|
|
131
|
+
Write-BeeWarn $reason
|
|
132
|
+
if (-not [Environment]::UserInteractive) {
|
|
133
|
+
Send-Telemetry -Event "install.bootstrap.arch_fail" -ErrorCode $reason -Success $false
|
|
134
|
+
Write-BeeError "Non-interactive shell — aborting."
|
|
135
|
+
exit 2
|
|
136
|
+
}
|
|
137
|
+
$go = Read-Host "Continue anyway? [y/N]"
|
|
138
|
+
if ($go -notmatch '^(y|yes)$') {
|
|
139
|
+
Send-Telemetry -Event "install.bootstrap.arch_fail" -ErrorCode $reason -Success $false
|
|
140
|
+
Write-BeeError "Aborting per user."
|
|
141
|
+
exit 2
|
|
142
|
+
}
|
|
143
|
+
Write-BeeInfo "Proceeding on unsupported arch by user confirmation."
|
|
51
144
|
}
|
|
52
145
|
}
|
|
53
146
|
|
|
@@ -174,6 +267,39 @@ function Read-ExistingInstallAction {
|
|
|
174
267
|
}
|
|
175
268
|
}
|
|
176
269
|
|
|
270
|
+
# ── Device-agent sibling suite installer ─────────────────────
|
|
271
|
+
#
|
|
272
|
+
# Installs `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` globally
|
|
273
|
+
# so the very first `beeos device attach` on a clean machine does not need
|
|
274
|
+
# to fetch them separately. `@beeos-ai/device-common` is pulled
|
|
275
|
+
# transitively as a `dependencies` entry of both — no separate install
|
|
276
|
+
# needed.
|
|
277
|
+
#
|
|
278
|
+
# Failure here is NEVER fatal: the CLI itself was already installed
|
|
279
|
+
# successfully, and `beeos device attach` will prompt to install missing
|
|
280
|
+
# pieces via ensureDeviceAgent on first use. We just emit a WARN with the
|
|
281
|
+
# manual recovery command.
|
|
282
|
+
function Install-DeviceAgentSuite {
|
|
283
|
+
$npmPath = Get-Command npm -ErrorAction SilentlyContinue
|
|
284
|
+
if (-not $npmPath) {
|
|
285
|
+
Write-BeeWarn "npm not available — skipping device-agent suite install."
|
|
286
|
+
Write-BeeWarn "Manual fix: install Node + run ``npm i -g $DeviceAgentPackage $McpServerPackage``."
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
Write-BeeInfo "Installing device-agent suite ($DeviceAgentPackage, $McpServerPackage)..."
|
|
291
|
+
& npm install -g $DeviceAgentPackage $McpServerPackage
|
|
292
|
+
if ($LASTEXITCODE -eq 0) {
|
|
293
|
+
Write-BeeOk "device-agent suite installed."
|
|
294
|
+
} else {
|
|
295
|
+
Write-BeeWarn "Failed to install device-agent suite — ``beeos device attach`` will prompt for it on first use."
|
|
296
|
+
Write-BeeWarn "Manual fix: npm i -g $DeviceAgentPackage $McpServerPackage"
|
|
297
|
+
# Reset $LASTEXITCODE so subsequent `if ($LASTEXITCODE -ne 0)` checks
|
|
298
|
+
# in callers don't trip on this non-fatal failure.
|
|
299
|
+
$global:LASTEXITCODE = 0
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
177
303
|
# ── Run CLI ──────────────────────────────────────────────────
|
|
178
304
|
|
|
179
305
|
function Invoke-BeeosCli {
|
|
@@ -187,6 +313,11 @@ function Invoke-BeeosCli {
|
|
|
187
313
|
if ($ExtraArgs) { $args += $ExtraArgs }
|
|
188
314
|
|
|
189
315
|
if ($npxPath) {
|
|
316
|
+
# Temporary / npx path: deliberately do NOT install the device-agent
|
|
317
|
+
# suite globally. The user opted into the throwaway-install flow, so
|
|
318
|
+
# we honor that — if they later run `beeos device attach`,
|
|
319
|
+
# ensureDeviceAgent will guide them through a global install at that
|
|
320
|
+
# point.
|
|
190
321
|
& npx --yes $CliPackage @args
|
|
191
322
|
} else {
|
|
192
323
|
Write-BeeWarn "npx not found, installing globally..."
|
|
@@ -200,6 +331,7 @@ function Invoke-BeeosCli {
|
|
|
200
331
|
Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
|
|
201
332
|
exit 1
|
|
202
333
|
}
|
|
334
|
+
Install-DeviceAgentSuite
|
|
203
335
|
& beeos @args
|
|
204
336
|
}
|
|
205
337
|
}
|
|
@@ -212,13 +344,16 @@ Write-Host " https://beeos.ai" -ForegroundColor DarkGray
|
|
|
212
344
|
Write-Host ""
|
|
213
345
|
|
|
214
346
|
Test-Platform
|
|
347
|
+
Send-Telemetry -Event "install.bootstrap.start"
|
|
215
348
|
|
|
216
349
|
if (-not (Test-NodeVersion)) {
|
|
217
350
|
if (-not (Install-NodeAuto)) {
|
|
351
|
+
Send-Telemetry -Event "install.bootstrap.node_fail" -ErrorCode "node_install_failed" -Success $false
|
|
218
352
|
Show-InstallHints
|
|
219
353
|
exit 1
|
|
220
354
|
}
|
|
221
355
|
if (-not (Test-NodeVersion)) {
|
|
356
|
+
Send-Telemetry -Event "install.bootstrap.node_fail" -ErrorCode "node_version_too_old" -Success $false
|
|
222
357
|
Show-InstallHints
|
|
223
358
|
exit 1
|
|
224
359
|
}
|
|
@@ -247,6 +382,7 @@ if ($beeosCmd) {
|
|
|
247
382
|
Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
|
|
248
383
|
exit 1
|
|
249
384
|
}
|
|
385
|
+
Install-DeviceAgentSuite
|
|
250
386
|
}
|
|
251
387
|
"rerun" {
|
|
252
388
|
Write-BeeInfo "Reusing existing install."
|
|
@@ -257,6 +393,8 @@ if ($beeosCmd) {
|
|
|
257
393
|
Write-BeeInfo "Running BeeOS CLI..."
|
|
258
394
|
Write-Host ""
|
|
259
395
|
|
|
396
|
+
Send-Telemetry -Event "install.bootstrap.success"
|
|
397
|
+
|
|
260
398
|
if ($Device) {
|
|
261
399
|
Invoke-BeeosCli -Subcommand "device" -ExtraArgs (@("attach") + $Passthrough)
|
|
262
400
|
} else {
|
package/scripts/install.sh
CHANGED
|
@@ -27,17 +27,103 @@ RED="\033[31m"
|
|
|
27
27
|
CYAN="\033[36m"
|
|
28
28
|
|
|
29
29
|
CLI_PACKAGE="@beeos-ai/cli@latest"
|
|
30
|
+
# Sibling packages installed alongside the CLI so that `beeos device attach`
|
|
31
|
+
# works on a clean machine without further `npm i -g` steps (a.k.a. the
|
|
32
|
+
# "fleet ready" install strategy — see agents/device-agent/ARCHITECTURE.md
|
|
33
|
+
# for the 0.2.0+ sibling-pair topology between device-agent and
|
|
34
|
+
# device-mcp-server). Their versions are independent of `--version` (which
|
|
35
|
+
# only pins the CLI) — both packages follow their own semver. Override via
|
|
36
|
+
# BEEOS_DEVICE_AGENT_VERSION / BEEOS_MCP_SERVER_VERSION when you need to
|
|
37
|
+
# pin a specific build (e.g. for a staging smoke test).
|
|
38
|
+
DEVICE_AGENT_PACKAGE="@beeos-ai/device-agent@${BEEOS_DEVICE_AGENT_VERSION:-latest}"
|
|
39
|
+
MCP_SERVER_PACKAGE="@beeos-ai/device-mcp-server@${BEEOS_MCP_SERVER_VERSION:-latest}"
|
|
30
40
|
MIN_NODE_VERSION=18
|
|
31
41
|
NVM_VERSION="v0.40.1"
|
|
32
42
|
FNM_INSTALL_URL="https://fnm.vercel.app/install"
|
|
33
43
|
|
|
34
44
|
BEEOS_HOME="${BEEOS_HOME:-$HOME/.beeos}"
|
|
45
|
+
# Default telemetry endpoint. `$BEEOS_API_URL` lets developers aim at
|
|
46
|
+
# staging / a local gateway; empty-or-unset falls back to production.
|
|
47
|
+
BEEOS_API_URL="${BEEOS_API_URL:-https://public-api.beeos.ai}"
|
|
48
|
+
|
|
49
|
+
# Where the installer writes verbose stderr for Node auto-install.
|
|
50
|
+
# Overridable via `BEEOS_INSTALL_LOG=/path/to/file.log` so support can
|
|
51
|
+
# ask "run the one-liner with `BEEOS_INSTALL_LOG=~/beeos-install.log`
|
|
52
|
+
# and share the log" rather than asking the user to recreate the
|
|
53
|
+
# failure with manual `bash -x`.
|
|
54
|
+
#
|
|
55
|
+
# Pre-P1-12 we buried every `2>/dev/null` in `try_install_node`, which
|
|
56
|
+
# meant bug reports like "nvm install --lts failed" were unreproducible
|
|
57
|
+
# from anything but the user's terminal. We now preserve stderr in a
|
|
58
|
+
# per-PID file and reference it in the terminal hint on failure.
|
|
59
|
+
BEEOS_INSTALL_LOG="${BEEOS_INSTALL_LOG:-/tmp/beeos-install-$$.log}"
|
|
60
|
+
# Truncate on every run so the log reflects only the current attempt.
|
|
61
|
+
: > "$BEEOS_INSTALL_LOG" 2>/dev/null || true
|
|
35
62
|
|
|
36
63
|
info() { printf "${BOLD}${CYAN}[beeos]${RESET} %s\n" "$*"; }
|
|
37
64
|
warn() { printf "${BOLD}${YELLOW}[beeos]${RESET} %s\n" "$*"; }
|
|
38
65
|
error() { printf "${BOLD}${RED}[beeos]${RESET} %s\n" "$*" >&2; }
|
|
39
66
|
success() { printf "${BOLD}${GREEN}[beeos]${RESET} %s\n" "$*"; }
|
|
40
67
|
|
|
68
|
+
# ── Telemetry (script-level, pre-CLI) ────────────────────────
|
|
69
|
+
#
|
|
70
|
+
# Mirrors `web/packages/cli/src/telemetry.ts` so we can capture
|
|
71
|
+
# failures that happen BEFORE the Node CLI even starts (e.g. Node
|
|
72
|
+
# install failure, unsupported arch). Fire-and-forget with a 2 s cap —
|
|
73
|
+
# we must never block a real install on a flaky telemetry endpoint.
|
|
74
|
+
#
|
|
75
|
+
# Event names used here:
|
|
76
|
+
# - install.bootstrap.start — script entered `main()`
|
|
77
|
+
# - install.bootstrap.node_fail — could not install/find Node
|
|
78
|
+
# - install.bootstrap.arch_fail — unsupported arch
|
|
79
|
+
# - install.bootstrap.success — about to exec `beeos`
|
|
80
|
+
#
|
|
81
|
+
# `BEEOS_NO_TELEMETRY=1` short-circuits everything. This matches the
|
|
82
|
+
# Node CLI's opt-out env var verbatim so a user who disabled telemetry
|
|
83
|
+
# for the CLI doesn't unexpectedly get events from the bootstrap.
|
|
84
|
+
send_telemetry() {
|
|
85
|
+
local event="$1"
|
|
86
|
+
local error_code="${2:-}"
|
|
87
|
+
local success_flag="${3:-true}"
|
|
88
|
+
|
|
89
|
+
if [[ "${BEEOS_NO_TELEMETRY:-}" == "1" ]]; then
|
|
90
|
+
return 0
|
|
91
|
+
fi
|
|
92
|
+
if ! command -v curl &>/dev/null; then
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
local os_kind="${OS_KIND:-unknown}"
|
|
97
|
+
local os_arch="${OS_ARCH:-unknown}"
|
|
98
|
+
local node_ver=""
|
|
99
|
+
if command -v node &>/dev/null; then
|
|
100
|
+
node_ver=$(node -v 2>/dev/null | sed 's/^v//' || true)
|
|
101
|
+
fi
|
|
102
|
+
local host_hint
|
|
103
|
+
host_hint="$(hostname 2>/dev/null || echo unknown)-${os_arch}"
|
|
104
|
+
|
|
105
|
+
# POST body kept deliberately small — matches Node CLI payload.
|
|
106
|
+
# Note: `cli_version` is empty until after npx install; we fill it
|
|
107
|
+
# only when the bootstrap telemetry is fired post-install, from the
|
|
108
|
+
# Node side. For pre-install events it stays "" and that's fine.
|
|
109
|
+
local body
|
|
110
|
+
body=$(cat <<JSON
|
|
111
|
+
{"event":"${event}","cli_version":"","dist_tag":"","os":"${os_kind}","arch":"${os_arch}","node_version":"${node_ver}","success":${success_flag},"error_code":"${error_code}","host_hint":"${host_hint}"}
|
|
112
|
+
JSON
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# `--max-time 2` caps the whole request at 2 s. `-o /dev/null -s`
|
|
116
|
+
# silences output. Swallow non-zero exit — telemetry MUST NOT break
|
|
117
|
+
# an install.
|
|
118
|
+
curl -fsS -o /dev/null \
|
|
119
|
+
--max-time 2 \
|
|
120
|
+
-H "Content-Type: application/json" \
|
|
121
|
+
-X POST \
|
|
122
|
+
-d "$body" \
|
|
123
|
+
"${BEEOS_API_URL%/}/api/v1/telemetry/install" \
|
|
124
|
+
2>/dev/null || true
|
|
125
|
+
}
|
|
126
|
+
|
|
41
127
|
# ── Parse arguments ──────────────────────────────────────────
|
|
42
128
|
|
|
43
129
|
CLI_VERSION=""
|
|
@@ -102,17 +188,46 @@ detect_platform() {
|
|
|
102
188
|
|
|
103
189
|
info "detected: ${OS_KIND} ${OS_ARCH}${LIBC_KIND:+ (${LIBC_KIND})}"
|
|
104
190
|
|
|
191
|
+
# Architecture policy (kept in sync with install.ps1):
|
|
192
|
+
# - Non-interactive (no TTY): hard exit 2. The user cannot see a
|
|
193
|
+
# warning scroll past, and we must not silently install a CLI
|
|
194
|
+
# that will later fail to download its native sidecars.
|
|
195
|
+
# - Interactive TTY: warn and ask for confirmation. Power users
|
|
196
|
+
# sometimes run on odd arches (RISC-V Linux, IBM z, early
|
|
197
|
+
# Windows-on-ARM) for experimentation; bail out by default, but
|
|
198
|
+
# let them override when they know what they're doing.
|
|
199
|
+
local interactive=0
|
|
200
|
+
if [[ -t 0 ]] || [[ -t 1 ]]; then interactive=1; fi
|
|
201
|
+
local arch_bail arch_confirm
|
|
202
|
+
arch_bail() {
|
|
203
|
+
local reason="$1"
|
|
204
|
+
error "$reason"
|
|
205
|
+
send_telemetry "install.bootstrap.arch_fail" "$reason" false
|
|
206
|
+
exit 2
|
|
207
|
+
}
|
|
208
|
+
arch_confirm() {
|
|
209
|
+
local reason="$1"
|
|
210
|
+
warn "$reason"
|
|
211
|
+
if (( interactive == 0 )); then
|
|
212
|
+
arch_bail "$reason"
|
|
213
|
+
fi
|
|
214
|
+
local yn=""
|
|
215
|
+
read -rp "Continue anyway? [y/N]: " yn </dev/tty 2>/dev/null || yn=""
|
|
216
|
+
case "${yn,,}" in
|
|
217
|
+
y|yes) info "Proceeding on unsupported arch by user confirmation." ;;
|
|
218
|
+
*) arch_bail "$reason" ;;
|
|
219
|
+
esac
|
|
220
|
+
}
|
|
221
|
+
|
|
105
222
|
case "$OS_KIND" in
|
|
106
223
|
darwin)
|
|
107
224
|
if [[ "$OS_ARCH" != "arm64" && "$OS_ARCH" != "x86_64" ]]; then
|
|
108
|
-
|
|
109
|
-
exit 2
|
|
225
|
+
arch_confirm "Unsupported macOS architecture: $OS_ARCH (need arm64 or x86_64)"
|
|
110
226
|
fi
|
|
111
227
|
;;
|
|
112
228
|
linux)
|
|
113
229
|
if [[ "$OS_ARCH" != "arm64" && "$OS_ARCH" != "x86_64" ]]; then
|
|
114
|
-
|
|
115
|
-
exit 2
|
|
230
|
+
arch_confirm "Unsupported Linux architecture: $OS_ARCH (need arm64 or x86_64)"
|
|
116
231
|
fi
|
|
117
232
|
if [[ "$LIBC_KIND" == "musl" ]]; then
|
|
118
233
|
# No musl prebuilt for scrcpy-bridge / vnc-bridge yet. The CLI
|
|
@@ -122,8 +237,7 @@ detect_platform() {
|
|
|
122
237
|
fi
|
|
123
238
|
;;
|
|
124
239
|
*)
|
|
125
|
-
|
|
126
|
-
exit 2
|
|
240
|
+
arch_bail "Unsupported OS: $OS_KIND (use install.ps1 for Windows)"
|
|
127
241
|
;;
|
|
128
242
|
esac
|
|
129
243
|
}
|
|
@@ -151,15 +265,26 @@ check_node() {
|
|
|
151
265
|
|
|
152
266
|
try_install_node() {
|
|
153
267
|
info "Node.js not found or too old. Attempting automatic installation..."
|
|
268
|
+
info "Verbose output appended to: ${BEEOS_INSTALL_LOG}"
|
|
154
269
|
echo ""
|
|
155
270
|
|
|
271
|
+
# Every step below redirects stderr to $BEEOS_INSTALL_LOG (append)
|
|
272
|
+
# so that a post-mortem has the full picture. We deliberately do NOT
|
|
273
|
+
# use bare `2>/dev/null` anymore — it's the single biggest source of
|
|
274
|
+
# unactionable bug reports in this layer. Steps still short-circuit
|
|
275
|
+
# on success to avoid N-second retries when one tool works.
|
|
276
|
+
{
|
|
277
|
+
echo "=== try_install_node @ $(date -u +%FT%TZ) ==="
|
|
278
|
+
echo "OS_KIND=${OS_KIND} OS_ARCH=${OS_ARCH} LIBC_KIND=${LIBC_KIND:-}"
|
|
279
|
+
} >> "$BEEOS_INSTALL_LOG" 2>&1 || true
|
|
280
|
+
|
|
156
281
|
# 1. Existing nvm
|
|
157
282
|
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
|
158
283
|
if [[ -s "$NVM_DIR/nvm.sh" ]]; then
|
|
159
284
|
info "Loading existing nvm..."
|
|
160
285
|
# shellcheck source=/dev/null
|
|
161
286
|
. "$NVM_DIR/nvm.sh"
|
|
162
|
-
if nvm install --lts 2
|
|
287
|
+
if nvm install --lts >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
163
288
|
success "Node.js $(node -v) installed via existing nvm"
|
|
164
289
|
return 0
|
|
165
290
|
fi
|
|
@@ -167,10 +292,11 @@ try_install_node() {
|
|
|
167
292
|
|
|
168
293
|
# 2. Fresh nvm install
|
|
169
294
|
info "Installing nvm ${NVM_VERSION}..."
|
|
170
|
-
if curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh"
|
|
295
|
+
if curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" 2>>"$BEEOS_INSTALL_LOG" \
|
|
296
|
+
| bash >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
171
297
|
# shellcheck source=/dev/null
|
|
172
|
-
. "$NVM_DIR/nvm.sh" 2
|
|
173
|
-
if nvm install --lts 2
|
|
298
|
+
. "$NVM_DIR/nvm.sh" >> "$BEEOS_INSTALL_LOG" 2>&1 || true
|
|
299
|
+
if nvm install --lts >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
174
300
|
success "Node.js $(node -v) installed via nvm"
|
|
175
301
|
return 0
|
|
176
302
|
fi
|
|
@@ -179,10 +305,12 @@ try_install_node() {
|
|
|
179
305
|
# 3. fnm — works on locked-down Linux without git/curl quirks
|
|
180
306
|
if [[ "$OS_KIND" == "linux" ]] && ! command -v fnm &>/dev/null; then
|
|
181
307
|
info "Trying fnm (single-user, no sudo)..."
|
|
182
|
-
if curl -fsSL "$FNM_INSTALL_URL"
|
|
308
|
+
if curl -fsSL "$FNM_INSTALL_URL" 2>>"$BEEOS_INSTALL_LOG" \
|
|
309
|
+
| bash -s -- --skip-shell >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
183
310
|
export PATH="$HOME/.local/share/fnm:$PATH"
|
|
184
|
-
eval "$(fnm env --shell bash 2
|
|
185
|
-
if fnm install --lts
|
|
311
|
+
eval "$(fnm env --shell bash 2>>"$BEEOS_INSTALL_LOG")" || true
|
|
312
|
+
if fnm install --lts >> "$BEEOS_INSTALL_LOG" 2>&1 \
|
|
313
|
+
&& fnm use lts-latest >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
186
314
|
success "Node.js $(node -v) installed via fnm"
|
|
187
315
|
return 0
|
|
188
316
|
fi
|
|
@@ -192,7 +320,7 @@ try_install_node() {
|
|
|
192
320
|
# 4. macOS Homebrew
|
|
193
321
|
if [[ "$OS_KIND" == "darwin" ]] && command -v brew &>/dev/null; then
|
|
194
322
|
info "Trying Homebrew..."
|
|
195
|
-
if brew install node 2
|
|
323
|
+
if brew install node >> "$BEEOS_INSTALL_LOG" 2>&1; then
|
|
196
324
|
success "Node.js installed via Homebrew"
|
|
197
325
|
return 0
|
|
198
326
|
fi
|
|
@@ -205,6 +333,7 @@ try_install_node() {
|
|
|
205
333
|
|
|
206
334
|
print_install_hints() {
|
|
207
335
|
error "Could not install Node.js automatically."
|
|
336
|
+
error "See ${BEEOS_INSTALL_LOG} for full stderr of every attempted method."
|
|
208
337
|
echo ""
|
|
209
338
|
info "Please install Node.js >= ${MIN_NODE_VERSION} manually:"
|
|
210
339
|
echo ""
|
|
@@ -294,14 +423,60 @@ prompt_existing_install_action() {
|
|
|
294
423
|
esac
|
|
295
424
|
}
|
|
296
425
|
|
|
426
|
+
# ── Device-agent sibling suite installer ─────────────────────
|
|
427
|
+
#
|
|
428
|
+
# Installs `@beeos-ai/device-agent` + `@beeos-ai/device-mcp-server` globally
|
|
429
|
+
# so the very first `beeos device attach` on a clean machine does not need
|
|
430
|
+
# to fetch them separately. `@beeos-ai/device-common` is pulled
|
|
431
|
+
# transitively as a `dependencies` entry of both — no separate install
|
|
432
|
+
# needed.
|
|
433
|
+
#
|
|
434
|
+
# Failure here is NEVER fatal: the CLI itself was already installed
|
|
435
|
+
# successfully, and `beeos device attach` will prompt to install missing
|
|
436
|
+
# pieces via ensureDeviceAgent on first use. We just emit a WARN with the
|
|
437
|
+
# manual recovery command.
|
|
438
|
+
install_device_agent_suite() {
|
|
439
|
+
if ! command -v npm &>/dev/null; then
|
|
440
|
+
warn "npm not available — skipping device-agent suite install."
|
|
441
|
+
warn "Manual fix: install Node + run \`npm i -g ${DEVICE_AGENT_PACKAGE} ${MCP_SERVER_PACKAGE}\`."
|
|
442
|
+
return 0
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
info "Installing device-agent suite (${DEVICE_AGENT_PACKAGE}, ${MCP_SERVER_PACKAGE})..."
|
|
446
|
+
if npm install -g "$DEVICE_AGENT_PACKAGE" "$MCP_SERVER_PACKAGE"; then
|
|
447
|
+
success "device-agent suite installed."
|
|
448
|
+
else
|
|
449
|
+
warn "Failed to install device-agent suite — \`beeos device attach\` will prompt for it on first use."
|
|
450
|
+
warn "Manual fix: npm i -g ${DEVICE_AGENT_PACKAGE} ${MCP_SERVER_PACKAGE}"
|
|
451
|
+
fi
|
|
452
|
+
return 0
|
|
453
|
+
}
|
|
454
|
+
|
|
297
455
|
# ── Main ─────────────────────────────────────────────────────
|
|
298
456
|
|
|
299
457
|
run_cli() {
|
|
300
458
|
local subcmd="$1"
|
|
301
459
|
shift || true
|
|
302
460
|
|
|
461
|
+
# When invoked as `curl ... | bash`, our own stdin is a pipe that has
|
|
462
|
+
# already been fully consumed by the time we exec the CLI. Node's
|
|
463
|
+
# readline treats a closed / non-TTY stdin as "no answer", silently
|
|
464
|
+
# collapsing every interactive prompt to its default. Reopen stdin
|
|
465
|
+
# against the controlling terminal so `beeos init` can still ask for
|
|
466
|
+
# framework choice, bind confirmation, etc. `-t 1` guards us against
|
|
467
|
+
# true headless runs (CI) where /dev/tty doesn't exist.
|
|
468
|
+
local stdin_src="/dev/stdin"
|
|
469
|
+
if [ -t 1 ] && [ -e /dev/tty ]; then
|
|
470
|
+
stdin_src="/dev/tty"
|
|
471
|
+
fi
|
|
472
|
+
|
|
303
473
|
if command -v npx &>/dev/null; then
|
|
304
|
-
|
|
474
|
+
# Temporary / npx path: deliberately do NOT install the device-agent
|
|
475
|
+
# suite globally. The user opted into the throwaway-install flow, so
|
|
476
|
+
# we honor that — if they later run `beeos device attach`,
|
|
477
|
+
# ensureDeviceAgent will guide them through a global install at that
|
|
478
|
+
# point.
|
|
479
|
+
exec npx --yes "$CLI_PACKAGE" "$subcmd" "$@" <"$stdin_src"
|
|
305
480
|
else
|
|
306
481
|
warn "npx not found, installing globally..."
|
|
307
482
|
if ! npm install -g "$CLI_PACKAGE"; then
|
|
@@ -314,7 +489,8 @@ run_cli() {
|
|
|
314
489
|
error " use a node version manager (nvm / fnm) or sudo"
|
|
315
490
|
exit 1
|
|
316
491
|
fi
|
|
317
|
-
|
|
492
|
+
install_device_agent_suite
|
|
493
|
+
exec beeos "$subcmd" "$@" <"$stdin_src"
|
|
318
494
|
fi
|
|
319
495
|
}
|
|
320
496
|
|
|
@@ -325,13 +501,16 @@ main() {
|
|
|
325
501
|
echo ""
|
|
326
502
|
|
|
327
503
|
detect_platform
|
|
504
|
+
send_telemetry "install.bootstrap.start"
|
|
328
505
|
|
|
329
506
|
if ! check_node; then
|
|
330
507
|
if ! try_install_node; then
|
|
508
|
+
send_telemetry "install.bootstrap.node_fail" "node_install_failed" false
|
|
331
509
|
print_install_hints
|
|
332
510
|
exit 1
|
|
333
511
|
fi
|
|
334
512
|
if ! check_node; then
|
|
513
|
+
send_telemetry "install.bootstrap.node_fail" "node_version_too_old" false
|
|
335
514
|
print_install_hints
|
|
336
515
|
exit 1
|
|
337
516
|
fi
|
|
@@ -361,6 +540,7 @@ main() {
|
|
|
361
540
|
error " use a node version manager (nvm / fnm) or sudo"
|
|
362
541
|
exit 1
|
|
363
542
|
fi
|
|
543
|
+
install_device_agent_suite
|
|
364
544
|
fi
|
|
365
545
|
;;
|
|
366
546
|
rerun)
|
|
@@ -372,6 +552,8 @@ main() {
|
|
|
372
552
|
info "Running BeeOS CLI..."
|
|
373
553
|
echo ""
|
|
374
554
|
|
|
555
|
+
send_telemetry "install.bootstrap.success"
|
|
556
|
+
|
|
375
557
|
if [[ "$MODE" == "device" ]]; then
|
|
376
558
|
run_cli "device" "attach" "${PASSTHROUGH_ARGS[@]+"${PASSTHROUGH_ARGS[@]}"}"
|
|
377
559
|
else
|