@anvil-works/anvil-cli 0.4.3 → 0.5.1
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/README.md +198 -120
- package/dist/cli.js +727 -104
- package/dist/index.js +12 -5
- package/package.json +3 -2
- package/scripts/install/install.cmd +26 -0
- package/scripts/install/install.ps1 +180 -0
- package/scripts/install/install.sh +151 -0
package/dist/index.js
CHANGED
|
@@ -20042,7 +20042,8 @@ var __webpack_exports__ = {};
|
|
|
20042
20042
|
}
|
|
20043
20043
|
const configDefaults = {
|
|
20044
20044
|
devMode: false,
|
|
20045
|
-
verbose: false
|
|
20045
|
+
verbose: false,
|
|
20046
|
+
preferredEditor: ""
|
|
20046
20047
|
};
|
|
20047
20048
|
function isTestMode() {
|
|
20048
20049
|
return "test" === process.env.NODE_ENV || !!process.env.RSTEST;
|
|
@@ -22721,6 +22722,7 @@ var __webpack_exports__ = {};
|
|
|
22721
22722
|
});
|
|
22722
22723
|
this.ws.on("error", (error)=>{
|
|
22723
22724
|
logger_logger.debug(`[WebSocket ${this.sessionId}]`, `Error: ${error.message}`);
|
|
22725
|
+
if (this.isClosing) return;
|
|
22724
22726
|
if (error.message.includes("502")) logger_logger.error(chalk_source.red("WebSocket connection failed (502 Bad Gateway) - likely a temporary server issue"));
|
|
22725
22727
|
else logger_logger.error(chalk_source.red(`WebSocket error: ${error.message}`));
|
|
22726
22728
|
this.emit("error", {
|
|
@@ -22821,15 +22823,20 @@ var __webpack_exports__ = {};
|
|
|
22821
22823
|
}
|
|
22822
22824
|
teardownSocket() {
|
|
22823
22825
|
this.stopHeartbeat();
|
|
22824
|
-
|
|
22826
|
+
const socket = this.ws;
|
|
22827
|
+
this.ws = null;
|
|
22828
|
+
if (!socket) return;
|
|
22825
22829
|
try {
|
|
22826
22830
|
logger_logger.debug(`[WebSocket ${this.sessionId}]`, "Closing WebSocket");
|
|
22827
|
-
|
|
22828
|
-
|
|
22831
|
+
if (socket.readyState === ws_wrapper.CONNECTING) {
|
|
22832
|
+
socket.once("error", ()=>{});
|
|
22833
|
+
socket.terminate();
|
|
22834
|
+
return;
|
|
22835
|
+
}
|
|
22836
|
+
if (socket.readyState === ws_wrapper.OPEN) return void socket.close();
|
|
22829
22837
|
} catch (e) {
|
|
22830
22838
|
logger_logger.debug(`[WebSocket ${this.sessionId}]`, "Error closing WebSocket (ignoring):", e);
|
|
22831
22839
|
}
|
|
22832
|
-
this.ws = null;
|
|
22833
22840
|
}
|
|
22834
22841
|
}
|
|
22835
22842
|
async function detectRemoteChanges(gitService, oldCommitId, newCommitId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anvil-works/anvil-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "CLI tool for developing Anvil apps locally",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"anvil": "./dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"scripts/install"
|
|
12
13
|
],
|
|
13
14
|
"publishConfig": {
|
|
14
15
|
"access": "public"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM Source of truth: @anvil-works/anvil-cli (scripts/install/install.cmd).
|
|
3
|
+
REM This script is synced into anvil website static install endpoints; do not edit the synced copy directly.
|
|
4
|
+
setlocal
|
|
5
|
+
|
|
6
|
+
set "SCRIPT_URL=%ANVIL_INSTALL_URL%"
|
|
7
|
+
if not defined SCRIPT_URL set "SCRIPT_URL=https://anvil.works/install/anvil-cli/install.ps1"
|
|
8
|
+
set "SCRIPT_PATH=%TEMP%\anvil-install-%RANDOM%%RANDOM%.ps1"
|
|
9
|
+
|
|
10
|
+
where curl >nul 2>&1
|
|
11
|
+
if errorlevel 1 (
|
|
12
|
+
echo Error: curl is required but was not found.
|
|
13
|
+
exit /b 1
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
curl -fsSL "%SCRIPT_URL%" -o "%SCRIPT_PATH%"
|
|
17
|
+
if errorlevel 1 (
|
|
18
|
+
echo Error: failed to download installer from %SCRIPT_URL%
|
|
19
|
+
exit /b 1
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_PATH%"
|
|
23
|
+
set "EXIT_CODE=%ERRORLEVEL%"
|
|
24
|
+
|
|
25
|
+
del /q "%SCRIPT_PATH%" >nul 2>&1
|
|
26
|
+
exit /b %EXIT_CODE%
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Source of truth: @anvil-works/anvil-cli (scripts/install/install.ps1).
|
|
2
|
+
# This script is synced into anvil website static install endpoints; do not edit the synced copy directly.
|
|
3
|
+
$ErrorActionPreference = 'Stop'
|
|
4
|
+
Set-StrictMode -Version Latest
|
|
5
|
+
|
|
6
|
+
$AnvilHome = if ($env:ANVIL_HOME) { $env:ANVIL_HOME } else { Join-Path $env:USERPROFILE '.anvil-cli' }
|
|
7
|
+
$NodeDir = Join-Path $AnvilHome 'node-v20'
|
|
8
|
+
$NpmPrefix = Join-Path $AnvilHome 'npm-global'
|
|
9
|
+
$MinTargetMajor = 20
|
|
10
|
+
|
|
11
|
+
function Write-Info([string] $Message) {
|
|
12
|
+
Write-Host $Message
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Fail([string] $Message) {
|
|
16
|
+
throw $Message
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Ensure-Directory([string] $Path) {
|
|
20
|
+
if (-not (Test-Path -LiteralPath $Path)) {
|
|
21
|
+
New-Item -ItemType Directory -Path $Path | Out-Null
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Get-PlatformArch {
|
|
26
|
+
if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64') {
|
|
27
|
+
return 'arm64'
|
|
28
|
+
}
|
|
29
|
+
return 'x64'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function Get-NodeMajorFromPath {
|
|
33
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
|
34
|
+
return $null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$version = (& node -v).Trim()
|
|
38
|
+
if ($version -match '^v(\d+)') {
|
|
39
|
+
return [int]$Matches[1]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return $null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Update-CurrentPath {
|
|
46
|
+
$env:Path = "$NpmPrefix;$NodeDir;$env:Path"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function Add-UserPathSegment([string] $Segment) {
|
|
50
|
+
$currentUserPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
51
|
+
if (-not $currentUserPath) {
|
|
52
|
+
[Environment]::SetEnvironmentVariable('Path', $Segment, 'User')
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$parts = $currentUserPath -split ';' | Where-Object { $_ -ne '' }
|
|
57
|
+
if ($parts -contains $Segment) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
[Environment]::SetEnvironmentVariable('Path', "$Segment;$currentUserPath", 'User')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function Install-NodeV20 {
|
|
65
|
+
$arch = Get-PlatformArch
|
|
66
|
+
$baseUrl = 'https://nodejs.org/dist/latest-v20.x'
|
|
67
|
+
$sumsUrl = "$baseUrl/SHASUMS256.txt"
|
|
68
|
+
|
|
69
|
+
$tmpRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("anvil-install-" + [System.Guid]::NewGuid().ToString('N'))
|
|
70
|
+
Ensure-Directory -Path $tmpRoot
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
$sumsPath = Join-Path $tmpRoot 'SHASUMS256.txt'
|
|
74
|
+
Invoke-WebRequest -UseBasicParsing -Uri $sumsUrl -OutFile $sumsPath
|
|
75
|
+
|
|
76
|
+
$pattern = "node-v20\.[0-9]+\.[0-9]+-win-$arch\.zip$"
|
|
77
|
+
$archiveLine = Get-Content -LiteralPath $sumsPath | Where-Object { $_ -match $pattern } | Select-Object -First 1
|
|
78
|
+
if (-not $archiveLine) {
|
|
79
|
+
Fail "Could not resolve Node.js archive for win-$arch"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$tokens = $archiveLine -split '\s+'
|
|
83
|
+
$archiveName = $tokens[-1]
|
|
84
|
+
$archivePath = Join-Path $tmpRoot $archiveName
|
|
85
|
+
$archiveUrl = "$baseUrl/$archiveName"
|
|
86
|
+
|
|
87
|
+
Write-Info "Installing Node.js v20 for win-$arch..."
|
|
88
|
+
Invoke-WebRequest -UseBasicParsing -Uri $archiveUrl -OutFile $archivePath
|
|
89
|
+
|
|
90
|
+
$expectedHash = ($tokens | Where-Object { $_ -match '^[A-Fa-f0-9]{64}$' } | Select-Object -First 1)
|
|
91
|
+
if ($expectedHash) {
|
|
92
|
+
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $archivePath).Hash.ToLowerInvariant()
|
|
93
|
+
if ($actualHash -ne $expectedHash.ToLowerInvariant()) {
|
|
94
|
+
Fail 'Checksum verification failed for Node.js archive'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Test-Path -LiteralPath $NodeDir) {
|
|
99
|
+
Remove-Item -LiteralPath $NodeDir -Recurse -Force
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Expand-Archive -LiteralPath $archivePath -DestinationPath $tmpRoot -Force
|
|
103
|
+
$expandedRoot = Get-ChildItem -LiteralPath $tmpRoot -Directory | Where-Object { $_.Name -like 'node-v20.*-win-*' } | Select-Object -First 1
|
|
104
|
+
if (-not $expandedRoot) {
|
|
105
|
+
Fail 'Failed to locate extracted Node.js directory'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Move-Item -LiteralPath $expandedRoot.FullName -Destination $NodeDir
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (Test-Path -LiteralPath $tmpRoot) {
|
|
112
|
+
Remove-Item -LiteralPath $tmpRoot -Recurse -Force
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function Ensure-Node {
|
|
118
|
+
$major = Get-NodeMajorFromPath
|
|
119
|
+
if ($major -and $major -ge $MinTargetMajor) {
|
|
120
|
+
$version = (& node -v).Trim()
|
|
121
|
+
Write-Info "Using existing Node.js $version"
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ($major) {
|
|
126
|
+
Write-Info "Existing Node.js major v$major is too old; installing Node.js v20 user-local"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Ensure-Directory -Path $AnvilHome
|
|
130
|
+
Install-NodeV20
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function Ensure-Npm {
|
|
134
|
+
$npmCmd = Join-Path $NodeDir 'npm.cmd'
|
|
135
|
+
if (Test-Path -LiteralPath $npmCmd) {
|
|
136
|
+
return $npmCmd
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (Get-Command npm -ErrorAction SilentlyContinue) {
|
|
140
|
+
return 'npm'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Fail 'npm is unavailable after Node.js setup'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function Main {
|
|
147
|
+
Ensure-Directory -Path $AnvilHome
|
|
148
|
+
Ensure-Directory -Path $NpmPrefix
|
|
149
|
+
|
|
150
|
+
Ensure-Node
|
|
151
|
+
|
|
152
|
+
Update-CurrentPath
|
|
153
|
+
Add-UserPathSegment -Segment $NodeDir
|
|
154
|
+
Add-UserPathSegment -Segment $NpmPrefix
|
|
155
|
+
|
|
156
|
+
$npm = Ensure-Npm
|
|
157
|
+
|
|
158
|
+
Write-Info 'Installing anvil-cli...'
|
|
159
|
+
& $npm config set prefix "$NpmPrefix"
|
|
160
|
+
& $npm install -g @anvil-works/anvil-cli@latest
|
|
161
|
+
|
|
162
|
+
$anvilCmd = Join-Path $NpmPrefix 'anvil.cmd'
|
|
163
|
+
if (-not (Test-Path -LiteralPath $anvilCmd)) {
|
|
164
|
+
$anvilCmd = 'anvil'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
$version = (& $anvilCmd --version).Trim()
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
Fail "anvil installed but command verification failed: $($_.Exception.Message)"
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Write-Info ''
|
|
175
|
+
Write-Info "anvil-cli installed successfully (v$version)"
|
|
176
|
+
Write-Info "Next step: run 'anvil onboard'"
|
|
177
|
+
Write-Info "If your current terminal cannot find 'anvil', start a new terminal session."
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Main
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Source of truth: @anvil-works/anvil-cli (scripts/install/install.sh).
|
|
3
|
+
# This script is synced into anvil website static install endpoints; do not edit the synced copy directly.
|
|
4
|
+
set -eu
|
|
5
|
+
|
|
6
|
+
ANVIL_HOME="${ANVIL_HOME:-$HOME/.anvil-cli}"
|
|
7
|
+
NODE_DIR="$ANVIL_HOME/node-v20"
|
|
8
|
+
NPM_PREFIX="$ANVIL_HOME/npm-global"
|
|
9
|
+
MIN_NODE_MAJOR=20
|
|
10
|
+
|
|
11
|
+
log() {
|
|
12
|
+
printf '%s\n' "$*"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fail() {
|
|
16
|
+
printf 'Error: %s\n' "$*" >&2
|
|
17
|
+
exit 1
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
need_cmd() {
|
|
21
|
+
command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
append_path_export() {
|
|
25
|
+
profile_file="$1"
|
|
26
|
+
line='export PATH="$HOME/.anvil-cli/npm-global/bin:$HOME/.anvil-cli/node-v20/bin:$PATH"'
|
|
27
|
+
|
|
28
|
+
[ -f "$profile_file" ] || touch "$profile_file"
|
|
29
|
+
grep -F "$line" "$profile_file" >/dev/null 2>&1 || {
|
|
30
|
+
printf '\n%s\n' "$line" >> "$profile_file"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
persist_path_hint() {
|
|
35
|
+
append_path_export "$HOME/.profile"
|
|
36
|
+
|
|
37
|
+
if [ -n "${ZSH_VERSION:-}" ] || [ "${SHELL:-}" = "$(command -v zsh 2>/dev/null || true)" ]; then
|
|
38
|
+
append_path_export "$HOME/.zshrc"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [ -n "${BASH_VERSION:-}" ] || [ "${SHELL:-}" = "$(command -v bash 2>/dev/null || true)" ]; then
|
|
42
|
+
append_path_export "$HOME/.bashrc"
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
detect_platform() {
|
|
47
|
+
os_name="$(uname -s)"
|
|
48
|
+
arch_name="$(uname -m)"
|
|
49
|
+
|
|
50
|
+
case "$os_name" in
|
|
51
|
+
Darwin) platform_os="darwin" ;;
|
|
52
|
+
Linux) platform_os="linux" ;;
|
|
53
|
+
*) fail "Unsupported OS: $os_name (only macOS and Linux are supported)" ;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
case "$arch_name" in
|
|
57
|
+
x86_64|amd64) platform_arch="x64" ;;
|
|
58
|
+
arm64|aarch64) platform_arch="arm64" ;;
|
|
59
|
+
*) fail "Unsupported architecture: $arch_name" ;;
|
|
60
|
+
esac
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
node_version_major() {
|
|
64
|
+
node -v 2>/dev/null | sed -E 's/^v([0-9]+).*/\1/'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
install_node_v20() {
|
|
68
|
+
detect_platform
|
|
69
|
+
need_cmd curl
|
|
70
|
+
need_cmd tar
|
|
71
|
+
|
|
72
|
+
base_url="https://nodejs.org/dist/latest-v20.x"
|
|
73
|
+
sums_url="$base_url/SHASUMS256.txt"
|
|
74
|
+
tmp_dir="$(mktemp -d)"
|
|
75
|
+
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
|
|
76
|
+
|
|
77
|
+
log "Installing Node.js v20 for $platform_os-$platform_arch..."
|
|
78
|
+
curl -fsSL "$sums_url" -o "$tmp_dir/SHASUMS256.txt"
|
|
79
|
+
|
|
80
|
+
archive_name="$(grep -E "node-v20\\.[0-9]+\\.[0-9]+-$platform_os-$platform_arch\\.tar\\.xz$" "$tmp_dir/SHASUMS256.txt" | awk '{print $2}' | head -n1)"
|
|
81
|
+
[ -n "$archive_name" ] || fail "Could not resolve Node.js archive for $platform_os-$platform_arch"
|
|
82
|
+
|
|
83
|
+
archive_url="$base_url/$archive_name"
|
|
84
|
+
archive_path="$tmp_dir/$archive_name"
|
|
85
|
+
|
|
86
|
+
curl -fsSL "$archive_url" -o "$archive_path"
|
|
87
|
+
|
|
88
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
89
|
+
expected_sum="$(grep " $archive_name$" "$tmp_dir/SHASUMS256.txt" | awk '{print $1}' | head -n1)"
|
|
90
|
+
actual_sum="$(shasum -a 256 "$archive_path" | awk '{print $1}')"
|
|
91
|
+
[ "$expected_sum" = "$actual_sum" ] || fail "Checksum verification failed for Node.js archive"
|
|
92
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
93
|
+
expected_sum="$(grep " $archive_name$" "$tmp_dir/SHASUMS256.txt" | awk '{print $1}' | head -n1)"
|
|
94
|
+
actual_sum="$(sha256sum "$archive_path" | awk '{print $1}')"
|
|
95
|
+
[ "$expected_sum" = "$actual_sum" ] || fail "Checksum verification failed for Node.js archive"
|
|
96
|
+
else
|
|
97
|
+
log "Warning: no sha256 tool found; skipping checksum verification"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
rm -rf "$NODE_DIR"
|
|
101
|
+
mkdir -p "$NODE_DIR"
|
|
102
|
+
tar -xJf "$archive_path" -C "$NODE_DIR" --strip-components=1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ensure_node() {
|
|
106
|
+
if command -v node >/dev/null 2>&1; then
|
|
107
|
+
major="$(node_version_major || true)"
|
|
108
|
+
if [ -n "$major" ] && [ "$major" -ge "$MIN_NODE_MAJOR" ]; then
|
|
109
|
+
log "Using existing Node.js $(node -v)"
|
|
110
|
+
return
|
|
111
|
+
fi
|
|
112
|
+
log "Existing Node.js $(node -v) is too old; installing Node.js v20 user-local"
|
|
113
|
+
install_node_v20
|
|
114
|
+
return
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
install_node_v20
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main() {
|
|
121
|
+
need_cmd curl
|
|
122
|
+
need_cmd tar
|
|
123
|
+
|
|
124
|
+
mkdir -p "$ANVIL_HOME" "$NPM_PREFIX"
|
|
125
|
+
|
|
126
|
+
ensure_node
|
|
127
|
+
|
|
128
|
+
export PATH="$NPM_PREFIX/bin:$NODE_DIR/bin:$PATH"
|
|
129
|
+
persist_path_hint
|
|
130
|
+
|
|
131
|
+
need_cmd npm
|
|
132
|
+
|
|
133
|
+
log "Installing anvil-cli..."
|
|
134
|
+
npm config set prefix "$NPM_PREFIX" >/dev/null
|
|
135
|
+
NPM_CONFIG_PREFIX="$NPM_PREFIX" npm install -g @anvil-works/anvil-cli@latest
|
|
136
|
+
|
|
137
|
+
if command -v anvil >/dev/null 2>&1; then
|
|
138
|
+
version="$(anvil --version 2>/dev/null || true)"
|
|
139
|
+
elif [ -x "$NPM_PREFIX/bin/anvil" ]; then
|
|
140
|
+
version="$($NPM_PREFIX/bin/anvil --version 2>/dev/null || true)"
|
|
141
|
+
else
|
|
142
|
+
fail "anvil installed but command not found on PATH"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
log ""
|
|
146
|
+
log "anvil-cli installed successfully${version:+ (v$version)}"
|
|
147
|
+
log "Next step: run 'anvil onboard'"
|
|
148
|
+
log "If your current shell cannot find 'anvil', open a new terminal session."
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
main "$@"
|