-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.ps1
More file actions
189 lines (169 loc) · 7.95 KB
/
Copy pathinstall.ps1
File metadata and controls
189 lines (169 loc) · 7.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# UTMStack CLI installer (Windows, native PowerShell)
#
# irm https://raw.githubusercontent.com/utmstack/utmstack-cli/main/install.ps1 | iex
#
# The bash installer (`install`) only runs under Git Bash / MSYS / WSL. This is
# the native path for PowerShell. It installs the `utmstack` CLI and, unless
# skipped, the UTMStack MCP server that provides the SIEM tools.
$ErrorActionPreference = 'Stop'
# On PowerShell 5.1 the Invoke-WebRequest progress bar makes large
# downloads roughly an order of magnitude slower.
$ProgressPreference = 'SilentlyContinue'
$Repo = 'utmstack/utmstack-cli'
$App = 'utmstack'
$Version = if ($env:UTMSTACK_VERSION) { $env:UTMSTACK_VERSION } else { 'latest' }
function Info($m) { Write-Host $m }
# `throw`, not `exit`: these scripts are documented for `irm ... | iex`, which
# runs in the caller's scope — `exit` would close the user's console before
# they could read the error, and inside a &-invoked scriptblock it bypasses
# the surrounding try/catch entirely.
function Fail($m) { throw "utmstack install: $m" }
# --- platform ------------------------------------------------------------- #
# Under WOW64 (a 32-bit PowerShell host) PROCESSOR_ARCHITECTURE reads x86
# on an x64 machine; PROCESSOR_ARCHITEW6432 carries the real value.
$rawArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE }
# Windows always uses the x64 build. The native ARM64 build cannot start its
# terminal UI — a Bun/OpenTUI FFI limitation that breaks opencode's own
# windows-arm64 binary identically — so we do not ship one. On ARM64 the x64
# build runs under Windows emulation, where the TUI works (verified on Windows
# 11 ARM64).
switch ($rawArch) {
'AMD64' { $target = 'windows-x64' }
'ARM64' { $target = 'windows-x64' }
'x86' { Fail '32-bit Windows is not supported' }
default { Fail "unsupported architecture: $rawArch" }
}
$asset = "$App-$target.zip"
# --- resolve release ------------------------------------------------------ #
$api = if ($Version -eq 'latest') {
"https://api.github.com/repos/$Repo/releases/latest"
} else {
"https://api.github.com/repos/$Repo/releases/tags/v$($Version -replace '^v','')"
}
Info "Resolving $Version release of $Repo ..."
try {
$release = Invoke-RestMethod -Uri $api -UseBasicParsing
} catch {
Fail 'could not resolve a release (is the repository public and does it have a release?)'
}
$tag = $release.tag_name
if (-not $tag) { Fail 'no tag_name in release response' }
Info " version: $tag"
Info " platform: $target"
$base = "https://github.com/$Repo/releases/download/$tag"
$tmp = Join-Path $env:TEMP ("utmstack-install-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
try {
# --- download + verify ------------------------------------------------ #
Info "Downloading $asset ..."
$pkg = Join-Path $tmp $asset
try {
Invoke-WebRequest -Uri "$base/$asset" -OutFile $pkg -UseBasicParsing
} catch {
Fail "no build published for $target in $tag"
}
Info 'Verifying checksum ...'
$sumFile = Join-Path $tmp 'checksums.txt'
try {
Invoke-WebRequest -Uri "$base/checksums.txt" -OutFile $sumFile -UseBasicParsing
} catch {
Fail 'could not download checksums.txt - refusing to install an unverified binary'
}
$expected = $null
foreach ($line in Get-Content $sumFile) {
if ($line -match "^([0-9a-fA-F]{64})\s+\*?$([regex]::Escape($asset))$") {
$expected = $Matches[1].ToLower(); break
}
}
if (-not $expected) { Fail "no checksum listed for $asset - refusing to install" }
$actual = (Get-FileHash -Path $pkg -Algorithm SHA256).Hash.ToLower()
if ($expected -ne $actual) { Fail "checksum mismatch - expected $expected, got $actual" }
Info ' checksum OK'
# --- install ---------------------------------------------------------- #
$installDir = Join-Path $env:USERPROFILE ".$App\bin"
New-Item -ItemType Directory -Force -Path $installDir | Out-Null
Info "Installing to $installDir ..."
# Extract into a dedicated subdirectory. Expanding into $tmp would mix the
# archive and checksums.txt in with the payload, and the sidecar copy below
# would then install them too.
$extract = Join-Path $tmp 'extract'
New-Item -ItemType Directory -Force -Path $extract | Out-Null
# Prefer tar (bsdtar, shipped on Windows 10+): Expand-Archive is very slow on
# ARM for a large many-file zip and can take minutes. Fall back to
# Expand-Archive where tar is unavailable.
$tar = Get-Command tar.exe -ErrorAction SilentlyContinue
if ($tar) {
& $tar.Source -xf $pkg -C $extract
if ($LASTEXITCODE -ne 0) { Expand-Archive -Path $pkg -DestinationPath $extract -Force }
} else {
Expand-Archive -Path $pkg -DestinationPath $extract -Force
}
$exeSource = Get-ChildItem -Path $extract -Recurse -Filter "$App.exe" | Select-Object -First 1
if (-not $exeSource) { Fail "archive does not contain $App.exe" }
# Windows refuses to overwrite a running executable; say so plainly.
try {
Copy-Item -Path $exeSource.FullName -Destination (Join-Path $installDir "$App.exe") -Force -ErrorAction Stop
} catch {
Fail "could not write $App.exe - it may be running. Close any $App session and re-run."
}
# Ship any sidecar files the archive carries alongside the executable.
Get-ChildItem -Path $exeSource.DirectoryName -File |
Where-Object { $_.Name -ne "$App.exe" } |
ForEach-Object { Copy-Item $_.FullName -Destination $installDir -Force }
$exe = Join-Path $installDir "$App.exe"
# --- PATH ------------------------------------------------------------- #
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$pathUpdated = $false
if ($userPath -notlike "*$installDir*") {
$newPath = if ($userPath) { "$userPath;$installDir" } else { $installDir }
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
$env:Path = "$env:Path;$installDir"
$pathUpdated = $true
}
$installed = (& $exe --version 2>&1 | Out-String).Trim()
Info ''
Info "Installed $App $installed"
Info " binary: $exe"
# --- UTMStack MCP server ---------------------------------------------- #
# The CLI ships a default config pointing at `utmstack-mcp`, so the SIEM
# tools only work if that binary exists. A failure here is reported but
# never fails the CLI install.
if ($env:UTMSTACK_SKIP_MCP) {
Info ''
Info 'Skipping UTMStack MCP server (UTMSTACK_SKIP_MCP set)'
} elseif (Get-Command utmstack-mcp -ErrorAction SilentlyContinue) {
Info 'UTMStack MCP server already installed'
} else {
Info ''
Info 'Installing utmstack-mcp (SIEM tools) ...'
try {
& ([scriptblock]::Create((Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/utmstack/MCP/main/install.ps1' -UseBasicParsing)))
} catch {
Info 'Could not install the UTMStack MCP server automatically.'
Info 'The CLI works without it; SIEM tools stay unavailable until you run:'
Info ' irm https://raw.githubusercontent.com/utmstack/MCP/main/install.ps1 | iex'
}
}
Info ''
Info ' UTMSTACK CLI'
Info ''
if ($pathUpdated) {
Info ' NOTE: PATH was updated. Open a new terminal for `utmstack` to resolve.'
Info ''
}
if ($rawArch -eq 'ARM64') {
Info ''
Info ' Note: installed the x64 build, which runs under Windows ARM64 emulation.'
Info ' (There is no working native ARM64 CLI — the limitation affects opencode too.)'
}
Info ''
Info 'First run:'
Info ' utmstack-mcp init # connect your UTMStack server'
Info ' utmstack # start the CLI, then /connect for your ThreatWinds API key'
Info ''
Info 'Docs: https://docs.utmstack.com'
Info ''
}
finally {
Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
}