@beeos-ai/cli 1.0.4 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beeos-ai/cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "BeeOS CLI — run AI agents from your desktop",
6
6
  "bin": {
@@ -11,8 +11,16 @@
11
11
  "files": [
12
12
  "bin/",
13
13
  "dist/",
14
+ "scripts/",
14
15
  "README.md"
15
16
  ],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "type-check": "tsc --noEmit",
20
+ "lint": "eslint src/",
21
+ "test": "vitest run --passWithNoTests",
22
+ "prepublishOnly": "pnpm build"
23
+ },
16
24
  "dependencies": {
17
25
  "@noble/ed25519": "^2.2.0",
18
26
  "@noble/hashes": "^1.7.0",
@@ -26,25 +34,19 @@
26
34
  "which": "^5.0.0"
27
35
  },
28
36
  "devDependencies": {
37
+ "@beeos/core": "workspace:*",
29
38
  "@types/node": "^22.0.0",
30
39
  "@types/proper-lockfile": "^4.1.4",
31
40
  "@types/qrcode": "^1.5.5",
32
41
  "@types/which": "^3.0.4",
33
42
  "tsup": "^8.3.0",
34
43
  "typescript": "^5.7.0",
35
- "vitest": "^3.0.0",
36
- "@beeos/core": "0.1.0"
44
+ "vitest": "^3.0.0"
37
45
  },
38
46
  "engines": {
39
47
  "node": ">=18"
40
48
  },
41
49
  "publishConfig": {
42
50
  "access": "public"
43
- },
44
- "scripts": {
45
- "build": "tsup",
46
- "type-check": "tsc --noEmit",
47
- "lint": "eslint src/",
48
- "test": "vitest run --passWithNoTests"
49
51
  }
50
- }
52
+ }
@@ -0,0 +1,255 @@
1
+ #Requires -Version 5.1
2
+ <#
3
+ .SYNOPSIS
4
+ BeeOS CLI Installer for Windows.
5
+
6
+ .DESCRIPTION
7
+ Detects or installs Node.js (via winget / Chocolatey fallback), then
8
+ dispatches into `beeos init` (default) or `beeos device attach` when
9
+ `-Device` is passed. Heavier deps (Python, uv, adb, scrcpy/vnc bridges)
10
+ are installed lazily by the CLI itself.
11
+
12
+ .EXAMPLE
13
+ # One-liner (PowerShell 5+):
14
+ irm https://beeos.ai/install.ps1 | iex
15
+
16
+ .EXAMPLE
17
+ # With specific CLI version:
18
+ .\install.ps1 -Version 1.0.4
19
+
20
+ .EXAMPLE
21
+ # Jump straight into device attach instead of init:
22
+ .\install.ps1 -Device
23
+ #>
24
+ param(
25
+ [string]$Version = "latest",
26
+ [switch]$Device,
27
+ [Parameter(ValueFromRemainingArguments=$true)]
28
+ [string[]]$Passthrough
29
+ )
30
+
31
+ $ErrorActionPreference = "Stop"
32
+ $MinNodeVersion = 18
33
+
34
+ $CliPackage = if ($Version -eq "latest") { "@beeos-ai/cli@latest" } else { "@beeos-ai/cli@$Version" }
35
+
36
+ # ── Helpers ──────────────────────────────────────────────────
37
+
38
+ function Write-BeeInfo { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Cyan }
39
+ function Write-BeeWarn { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Yellow }
40
+ function Write-BeeError { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Red }
41
+ function Write-BeeOk { param([string]$Msg) Write-Host "[beeos] $Msg" -ForegroundColor Green }
42
+
43
+ # ── Platform detection ───────────────────────────────────────
44
+
45
+ function Test-Platform {
46
+ $arch = $env:PROCESSOR_ARCHITECTURE
47
+ $effectiveArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $arch }
48
+ Write-BeeInfo "detected: win32 $effectiveArch"
49
+ if ($effectiveArch -notin @("AMD64","ARM64","x86_64")) {
50
+ Write-BeeWarn "Unusual CPU architecture: $effectiveArch. Some sidecar binaries may be unavailable."
51
+ }
52
+ }
53
+
54
+ # ── Node.js detection ────────────────────────────────────────
55
+
56
+ function Test-NodeVersion {
57
+ try {
58
+ $nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source
59
+ if (-not $nodePath) { return $false }
60
+
61
+ $raw = & node -v 2>$null
62
+ if (-not $raw) { return $false }
63
+
64
+ $ver = $raw -replace '^v', ''
65
+ $major = [int]($ver.Split('.')[0])
66
+
67
+ if ($major -lt $MinNodeVersion) {
68
+ Write-BeeWarn "Node.js $ver found, but v${MinNodeVersion}+ is required."
69
+ return $false
70
+ }
71
+ return $true
72
+ } catch {
73
+ return $false
74
+ }
75
+ }
76
+
77
+ # ── Auto-install Node.js ─────────────────────────────────────
78
+
79
+ function Install-NodeAuto {
80
+ Write-BeeInfo "Node.js not found. Attempting automatic installation..."
81
+ Write-Host ""
82
+
83
+ $winget = Get-Command winget -ErrorAction SilentlyContinue
84
+ if ($winget) {
85
+ Write-BeeInfo "Installing Node.js LTS via winget..."
86
+ try {
87
+ winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements --silent 2>$null
88
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
89
+ [System.Environment]::GetEnvironmentVariable("Path", "User")
90
+ if (Test-NodeVersion) {
91
+ Write-BeeOk "Node.js installed via winget"
92
+ return $true
93
+ }
94
+ } catch {
95
+ Write-BeeWarn "winget install failed: $_"
96
+ }
97
+ }
98
+
99
+ $choco = Get-Command choco -ErrorAction SilentlyContinue
100
+ if ($choco) {
101
+ Write-BeeInfo "Installing Node.js LTS via Chocolatey..."
102
+ try {
103
+ choco install nodejs-lts -y 2>$null
104
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
105
+ [System.Environment]::GetEnvironmentVariable("Path", "User")
106
+ if (Test-NodeVersion) {
107
+ Write-BeeOk "Node.js installed via Chocolatey"
108
+ return $true
109
+ }
110
+ } catch {
111
+ Write-BeeWarn "Chocolatey install failed: $_"
112
+ }
113
+ }
114
+
115
+ return $false
116
+ }
117
+
118
+ # ── Manual install hints ─────────────────────────────────────
119
+
120
+ function Show-InstallHints {
121
+ Write-BeeError "Could not install Node.js automatically."
122
+ Write-Host ""
123
+ Write-BeeInfo "Please install Node.js >= $MinNodeVersion manually:"
124
+ Write-Host ""
125
+ Write-Host " winget (recommended):" -ForegroundColor White
126
+ Write-Host " winget install OpenJS.NodeJS.LTS"
127
+ Write-Host ""
128
+ Write-Host " Official installer:" -ForegroundColor White
129
+ Write-Host " https://nodejs.org/en/download/"
130
+ Write-Host ""
131
+ Write-Host " Chocolatey:" -ForegroundColor White
132
+ Write-Host " choco install nodejs-lts"
133
+ Write-Host ""
134
+ Write-BeeInfo "Then run this script again:"
135
+ Write-Host " irm https://beeos.ai/install.ps1 | iex"
136
+ Write-Host ""
137
+ }
138
+
139
+ # ── Existing install detection ───────────────────────────────
140
+
141
+ function Get-BeeosVersion {
142
+ try {
143
+ $out = & beeos --version 2>$null
144
+ if ($out) {
145
+ if ($out -match "\d+\.\d+\.\d+") { return $Matches[0] }
146
+ return ($out -replace '\s+','')
147
+ }
148
+ } catch {}
149
+ return $null
150
+ }
151
+
152
+ function Read-ExistingInstallAction {
153
+ $ver = Get-BeeosVersion
154
+ if (-not $ver) { $ver = "unknown" }
155
+ Write-BeeWarn "BeeOS CLI is already installed (v$ver)."
156
+ Write-Host ""
157
+ Write-Host " [1] Upgrade to the latest version and run beeos init"
158
+ Write-Host " [2] Re-run beeos init without upgrading"
159
+ Write-Host " [3] Skip (do nothing)"
160
+ Write-Host ""
161
+
162
+ if (-not [Environment]::UserInteractive) {
163
+ Write-BeeInfo "Non-interactive shell — defaulting to upgrade."
164
+ return "upgrade"
165
+ }
166
+
167
+ $choice = Read-Host "Choose [1-3]"
168
+ switch ($choice) {
169
+ "" { return "upgrade" }
170
+ "1" { return "upgrade" }
171
+ "2" { return "rerun" }
172
+ "3" { return "skip" }
173
+ default { return "upgrade" }
174
+ }
175
+ }
176
+
177
+ # ── Run CLI ──────────────────────────────────────────────────
178
+
179
+ function Invoke-BeeosCli {
180
+ param(
181
+ [string]$Subcommand,
182
+ [string[]]$ExtraArgs
183
+ )
184
+
185
+ $npxPath = Get-Command npx -ErrorAction SilentlyContinue
186
+ $args = @($Subcommand)
187
+ if ($ExtraArgs) { $args += $ExtraArgs }
188
+
189
+ if ($npxPath) {
190
+ & npx --yes $CliPackage @args
191
+ } else {
192
+ Write-BeeWarn "npx not found, installing globally..."
193
+ & npm install -g $CliPackage
194
+ & beeos @args
195
+ }
196
+ }
197
+
198
+ # ── Main ─────────────────────────────────────────────────────
199
+
200
+ Write-Host ""
201
+ Write-Host " BeeOS CLI Installer" -ForegroundColor White
202
+ Write-Host " https://beeos.ai" -ForegroundColor DarkGray
203
+ Write-Host ""
204
+
205
+ Test-Platform
206
+
207
+ if (-not (Test-NodeVersion)) {
208
+ if (-not (Install-NodeAuto)) {
209
+ Show-InstallHints
210
+ exit 1
211
+ }
212
+ if (-not (Test-NodeVersion)) {
213
+ Show-InstallHints
214
+ exit 1
215
+ }
216
+ }
217
+
218
+ $nodeVer = & node -v
219
+ Write-BeeOk "Node.js $nodeVer detected"
220
+
221
+ $beeosCmd = Get-Command beeos -ErrorAction SilentlyContinue
222
+ if ($beeosCmd) {
223
+ $action = Read-ExistingInstallAction
224
+ switch ($action) {
225
+ "skip" {
226
+ Write-BeeInfo "Skipping. Run `beeos init` or `beeos device attach` manually later."
227
+ exit 0
228
+ }
229
+ "upgrade" {
230
+ Write-BeeInfo "Upgrading $CliPackage..."
231
+ & npm install -g $CliPackage
232
+ if ($LASTEXITCODE -ne 0) {
233
+ Write-BeeError "npm install -g $CliPackage failed."
234
+ Write-BeeError ""
235
+ Write-BeeError "Common fixes:"
236
+ Write-BeeError " - EEXIST on beeos from another package:"
237
+ Write-BeeError " npm uninstall -g beeos # then re-run installer"
238
+ Write-BeeError " - EACCES / permission error: run PowerShell as Administrator"
239
+ exit 1
240
+ }
241
+ }
242
+ "rerun" {
243
+ Write-BeeInfo "Reusing existing install."
244
+ }
245
+ }
246
+ }
247
+
248
+ Write-BeeInfo "Running BeeOS CLI..."
249
+ Write-Host ""
250
+
251
+ if ($Device) {
252
+ Invoke-BeeosCli -Subcommand "device" -ExtraArgs (@("attach") + $Passthrough)
253
+ } else {
254
+ Invoke-BeeosCli -Subcommand "init" -ExtraArgs $Passthrough
255
+ }
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # BeeOS CLI Installer (macOS / Linux)
4
+ # Usage:
5
+ # curl -fsSL https://beeos.ai/install | bash
6
+ # curl -fsSL https://beeos.ai/install | bash -s -- [beeos init args]
7
+ # curl -fsSL https://beeos.ai/install | bash -s -- --device # jump into device attach
8
+ #
9
+ # Entry script responsibilities:
10
+ # 1. Detect OS + CPU architecture, refuse unsupported targets early.
11
+ # 2. Make sure Node.js >= MIN_NODE_VERSION is available (install via nvm /
12
+ # fnm / brew when possible). Heavier deps (Python, uv, adb, scrcpy/vnc
13
+ # bridges) are handled lazily by the CLI itself.
14
+ # 3. Detect an existing BeeOS install and let the user pick
15
+ # upgrade / rebind / skip before re-running the bind flow.
16
+ # 4. Dispatch into `beeos init` (default) or `beeos device attach` when
17
+ # `--device` is passed.
18
+
19
+ set -euo pipefail
20
+
21
+ BOLD="\033[1m"
22
+ DIM="\033[2m"
23
+ RESET="\033[0m"
24
+ GREEN="\033[32m"
25
+ YELLOW="\033[33m"
26
+ RED="\033[31m"
27
+ CYAN="\033[36m"
28
+
29
+ CLI_PACKAGE="@beeos-ai/cli@latest"
30
+ MIN_NODE_VERSION=18
31
+ NVM_VERSION="v0.40.1"
32
+ FNM_INSTALL_URL="https://fnm.vercel.app/install"
33
+
34
+ BEEOS_HOME="${BEEOS_HOME:-$HOME/.beeos}"
35
+
36
+ info() { printf "${BOLD}${CYAN}[beeos]${RESET} %s\n" "$*"; }
37
+ warn() { printf "${BOLD}${YELLOW}[beeos]${RESET} %s\n" "$*"; }
38
+ error() { printf "${BOLD}${RED}[beeos]${RESET} %s\n" "$*" >&2; }
39
+ success() { printf "${BOLD}${GREEN}[beeos]${RESET} %s\n" "$*"; }
40
+
41
+ # ── Parse arguments ──────────────────────────────────────────
42
+
43
+ CLI_VERSION=""
44
+ MODE="init" # "init" | "device"
45
+ PASSTHROUGH_ARGS=()
46
+
47
+ while [[ $# -gt 0 ]]; do
48
+ case "$1" in
49
+ --version)
50
+ CLI_VERSION="$2"
51
+ shift 2
52
+ ;;
53
+ --device)
54
+ MODE="device"
55
+ shift
56
+ ;;
57
+ --init)
58
+ MODE="init"
59
+ shift
60
+ ;;
61
+ *)
62
+ PASSTHROUGH_ARGS+=("$1")
63
+ shift
64
+ ;;
65
+ esac
66
+ done
67
+
68
+ if [[ -n "$CLI_VERSION" ]]; then
69
+ CLI_PACKAGE="@beeos-ai/cli@${CLI_VERSION}"
70
+ fi
71
+
72
+ # ── OS / architecture detection ──────────────────────────────
73
+
74
+ OS_KIND="" # darwin | linux | *
75
+ OS_ARCH="" # x86_64 | arm64
76
+ LIBC_KIND="" # gnu | musl (Linux only, empty otherwise)
77
+
78
+ detect_platform() {
79
+ local uname_s uname_m
80
+ uname_s=$(uname -s 2>/dev/null || echo "unknown")
81
+ uname_m=$(uname -m 2>/dev/null || echo "unknown")
82
+
83
+ case "$uname_s" in
84
+ Darwin) OS_KIND="darwin" ;;
85
+ Linux) OS_KIND="linux" ;;
86
+ *) OS_KIND="$uname_s" ;;
87
+ esac
88
+
89
+ case "$uname_m" in
90
+ x86_64|amd64) OS_ARCH="x86_64" ;;
91
+ aarch64|arm64) OS_ARCH="arm64" ;;
92
+ *) OS_ARCH="$uname_m" ;;
93
+ esac
94
+
95
+ if [[ "$OS_KIND" == "linux" ]]; then
96
+ if ldd --version 2>&1 | head -n1 | grep -qi "musl"; then
97
+ LIBC_KIND="musl"
98
+ else
99
+ LIBC_KIND="gnu"
100
+ fi
101
+ fi
102
+
103
+ info "detected: ${OS_KIND} ${OS_ARCH}${LIBC_KIND:+ (${LIBC_KIND})}"
104
+
105
+ case "$OS_KIND" in
106
+ darwin)
107
+ if [[ "$OS_ARCH" != "arm64" && "$OS_ARCH" != "x86_64" ]]; then
108
+ error "Unsupported macOS architecture: $OS_ARCH (need arm64 or x86_64)"
109
+ exit 2
110
+ fi
111
+ ;;
112
+ linux)
113
+ if [[ "$OS_ARCH" != "arm64" && "$OS_ARCH" != "x86_64" ]]; then
114
+ error "Unsupported Linux architecture: $OS_ARCH (need arm64 or x86_64)"
115
+ exit 2
116
+ fi
117
+ if [[ "$LIBC_KIND" == "musl" ]]; then
118
+ # No musl prebuilt for scrcpy-bridge / vnc-bridge yet. The CLI
119
+ # still works (agents run on top of Node) but some sidecars
120
+ # will need to be built from source.
121
+ warn "musl libc detected — some Rust sidecar binaries may be unavailable."
122
+ fi
123
+ ;;
124
+ *)
125
+ error "Unsupported OS: $OS_KIND (use install.ps1 for Windows)"
126
+ exit 2
127
+ ;;
128
+ esac
129
+ }
130
+
131
+ # ── Node.js detection ────────────────────────────────────────
132
+
133
+ check_node() {
134
+ if ! command -v node &>/dev/null; then
135
+ return 1
136
+ fi
137
+
138
+ local version major
139
+ version=$(node -v 2>/dev/null | sed 's/^v//')
140
+ major=$(echo "$version" | cut -d. -f1)
141
+
142
+ if [ "$major" -lt "$MIN_NODE_VERSION" ] 2>/dev/null; then
143
+ warn "Node.js $version found, but v${MIN_NODE_VERSION}+ is required."
144
+ return 1
145
+ fi
146
+
147
+ return 0
148
+ }
149
+
150
+ # ── Auto-install Node.js (nvm -> fnm -> brew) ────────────────
151
+
152
+ try_install_node() {
153
+ info "Node.js not found or too old. Attempting automatic installation..."
154
+ echo ""
155
+
156
+ # 1. Existing nvm
157
+ export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
158
+ if [[ -s "$NVM_DIR/nvm.sh" ]]; then
159
+ info "Loading existing nvm..."
160
+ # shellcheck source=/dev/null
161
+ . "$NVM_DIR/nvm.sh"
162
+ if nvm install --lts 2>/dev/null; then
163
+ success "Node.js $(node -v) installed via existing nvm"
164
+ return 0
165
+ fi
166
+ fi
167
+
168
+ # 2. Fresh nvm install
169
+ info "Installing nvm ${NVM_VERSION}..."
170
+ if curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" | bash 2>/dev/null; then
171
+ # shellcheck source=/dev/null
172
+ . "$NVM_DIR/nvm.sh" 2>/dev/null || true
173
+ if nvm install --lts 2>/dev/null; then
174
+ success "Node.js $(node -v) installed via nvm"
175
+ return 0
176
+ fi
177
+ fi
178
+
179
+ # 3. fnm — works on locked-down Linux without git/curl quirks
180
+ if [[ "$OS_KIND" == "linux" ]] && ! command -v fnm &>/dev/null; then
181
+ info "Trying fnm (single-user, no sudo)..."
182
+ if curl -fsSL "$FNM_INSTALL_URL" | bash -s -- --skip-shell 2>/dev/null; then
183
+ export PATH="$HOME/.local/share/fnm:$PATH"
184
+ eval "$(fnm env --shell bash 2>/dev/null)" || true
185
+ if fnm install --lts 2>/dev/null && fnm use lts-latest 2>/dev/null; then
186
+ success "Node.js $(node -v) installed via fnm"
187
+ return 0
188
+ fi
189
+ fi
190
+ fi
191
+
192
+ # 4. macOS Homebrew
193
+ if [[ "$OS_KIND" == "darwin" ]] && command -v brew &>/dev/null; then
194
+ info "Trying Homebrew..."
195
+ if brew install node 2>/dev/null; then
196
+ success "Node.js installed via Homebrew"
197
+ return 0
198
+ fi
199
+ fi
200
+
201
+ return 1
202
+ }
203
+
204
+ # ── Manual install hints ─────────────────────────────────────
205
+
206
+ print_install_hints() {
207
+ error "Could not install Node.js automatically."
208
+ echo ""
209
+ info "Please install Node.js >= ${MIN_NODE_VERSION} manually:"
210
+ echo ""
211
+
212
+ case "$OS_KIND" in
213
+ darwin)
214
+ echo " ${BOLD}Homebrew (recommended):${RESET}"
215
+ echo " brew install node"
216
+ echo ""
217
+ echo " ${BOLD}Official installer:${RESET}"
218
+ echo " https://nodejs.org/en/download/"
219
+ ;;
220
+ linux)
221
+ echo " ${BOLD}nvm (recommended):${RESET}"
222
+ echo " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash"
223
+ echo " source ~/.bashrc && nvm install --lts"
224
+ echo ""
225
+ echo " ${BOLD}fnm (single-user, no sudo):${RESET}"
226
+ echo " curl -fsSL ${FNM_INSTALL_URL} | bash"
227
+ echo " fnm install --lts"
228
+ echo ""
229
+ echo " ${BOLD}NodeSource:${RESET}"
230
+ echo " https://github.com/nodesource/distributions"
231
+ echo ""
232
+ echo " ${BOLD}Official binaries:${RESET}"
233
+ echo " https://nodejs.org/en/download/"
234
+ ;;
235
+ *)
236
+ echo " Visit https://nodejs.org/en/download/"
237
+ ;;
238
+ esac
239
+
240
+ echo ""
241
+ info "Then run this script again:"
242
+ echo " curl -fsSL https://beeos.ai/install | bash"
243
+ echo ""
244
+ }
245
+
246
+ # ── Existing install detection ───────────────────────────────
247
+
248
+ beeos_already_installed() {
249
+ if command -v beeos &>/dev/null; then
250
+ return 0
251
+ fi
252
+ return 1
253
+ }
254
+
255
+ beeos_current_version() {
256
+ # `beeos --version` currently prints just the semver string. Tolerate
257
+ # other formats by extracting the first dotted number we can find.
258
+ local out
259
+ out=$(beeos --version 2>/dev/null | head -n1 | tr -d '[:space:]' || true)
260
+ if [[ "$out" =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
261
+ echo "${BASH_REMATCH[0]}"
262
+ else
263
+ echo "$out"
264
+ fi
265
+ }
266
+
267
+ prompt_existing_install_action() {
268
+ # Only prompt on an interactive TTY. When the script is piped without a
269
+ # TTY attached (e.g. one-liner with no tty allocated), default to
270
+ # "upgrade + re-run init" which is the safest non-destructive choice.
271
+ if [[ ! -t 0 ]] && [[ ! -t 1 ]]; then
272
+ info "Non-interactive shell detected — defaulting to upgrade."
273
+ echo "upgrade"
274
+ return 0
275
+ fi
276
+
277
+ local ver
278
+ ver=$(beeos_current_version || true)
279
+ ver="${ver:-unknown}"
280
+ warn "BeeOS CLI is already installed (v${ver})."
281
+ echo ""
282
+ echo " [1] Upgrade to the latest version and run ${BOLD}beeos init${RESET}"
283
+ echo " [2] Re-run ${BOLD}beeos init${RESET} without upgrading"
284
+ echo " [3] Skip (do nothing)"
285
+ echo ""
286
+
287
+ local choice
288
+ read -rp "Choose [1-3]: " choice </dev/tty || choice=""
289
+ case "$choice" in
290
+ ""|1) echo "upgrade" ;;
291
+ 2) echo "rerun" ;;
292
+ 3) echo "skip" ;;
293
+ *) echo "upgrade" ;;
294
+ esac
295
+ }
296
+
297
+ # ── Main ─────────────────────────────────────────────────────
298
+
299
+ run_cli() {
300
+ local subcmd="$1"
301
+ shift || true
302
+
303
+ if command -v npx &>/dev/null; then
304
+ exec npx --yes "$CLI_PACKAGE" "$subcmd" "$@"
305
+ else
306
+ warn "npx not found, installing globally..."
307
+ npm install -g "$CLI_PACKAGE"
308
+ exec beeos "$subcmd" "$@"
309
+ fi
310
+ }
311
+
312
+ main() {
313
+ echo ""
314
+ echo " ${BOLD}BeeOS CLI Installer${RESET}"
315
+ echo " ${DIM}https://beeos.ai${RESET}"
316
+ echo ""
317
+
318
+ detect_platform
319
+
320
+ if ! check_node; then
321
+ if ! try_install_node; then
322
+ print_install_hints
323
+ exit 1
324
+ fi
325
+ if ! check_node; then
326
+ print_install_hints
327
+ exit 1
328
+ fi
329
+ fi
330
+
331
+ success "Node.js $(node -v) detected"
332
+
333
+ # Existing install? Let the user pick upgrade / rerun / skip.
334
+ if beeos_already_installed; then
335
+ local action
336
+ action=$(prompt_existing_install_action)
337
+ case "$action" in
338
+ skip)
339
+ info "Skipping. Run \`beeos init\` or \`beeos device attach\` manually later."
340
+ exit 0
341
+ ;;
342
+ upgrade)
343
+ info "Upgrading ${CLI_PACKAGE}..."
344
+ if command -v npm &>/dev/null; then
345
+ if ! npm install -g "$CLI_PACKAGE"; then
346
+ error "npm install -g ${CLI_PACKAGE} failed."
347
+ error ""
348
+ error "Common fixes:"
349
+ error " - EEXIST on /bin/beeos from another package:"
350
+ error " npm uninstall -g beeos # then re-run installer"
351
+ error " - EACCES permission error:"
352
+ error " use a node version manager (nvm / fnm) or sudo"
353
+ exit 1
354
+ fi
355
+ fi
356
+ ;;
357
+ rerun)
358
+ info "Reusing existing install."
359
+ ;;
360
+ esac
361
+ fi
362
+
363
+ info "Running BeeOS CLI..."
364
+ echo ""
365
+
366
+ if [[ "$MODE" == "device" ]]; then
367
+ run_cli "device" "attach" "${PASSTHROUGH_ARGS[@]+"${PASSTHROUGH_ARGS[@]}"}"
368
+ else
369
+ run_cli "init" "${PASSTHROUGH_ARGS[@]+"${PASSTHROUGH_ARGS[@]}"}"
370
+ fi
371
+ }
372
+
373
+ main