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