AnyBot/install.ps1

308 lines
12 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AnyBot Windows 一鍵安裝(x64、arm64)。桌面、手腳、第一次設定。
#
# irm https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.ps1 | iex
# powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1 app
# powershell -File install.ps1 hands https://你的網域 482913
# powershell -File install.ps1 -Print
#
# 代理程式不要問問題:
# $env:ANYBOT_NONINTERACTIVE='1'
# $env:ANYBOT_MODE='hands'
# $env:ANYBOT_PAIR_URL='https://…'
# $env:ANYBOT_PAIR_CODE='482913'
# irm https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.ps1 | iex
#
# 伺服器(帳號+大腦+HTTPS)請在 Linux 上用 install.sh server。Windows 可以當大腦或手腳。
#Requires -Version 5.1
param(
[Parameter(Position = 0)][string]$Mode = "",
[Parameter(Position = 1)][string]$PairUrl = "",
[Parameter(Position = 2)][string]$PairCode = "",
[switch]$Print
)
$ErrorActionPreference = "Stop"
$Repo = if ($env:ANYBOT_REPO) { $env:ANYBOT_REPO } else { "https://code.30cm.net/daniel.w/AnyBot.git" }
$Branch = if ($env:ANYBOT_BRANCH) { $env:ANYBOT_BRANCH } else { "main" }
$Src = if ($env:ANYBOT_DIR) { $env:ANYBOT_DIR } else { Join-Path $env:USERPROFILE "AnyBot" }
$RustMin = 95
if (-not $Mode -and $env:ANYBOT_MODE) { $Mode = $env:ANYBOT_MODE }
if (-not $PairUrl -and $env:ANYBOT_PAIR_URL) { $PairUrl = $env:ANYBOT_PAIR_URL }
if (-not $PairCode -and $env:ANYBOT_PAIR_CODE) { $PairCode = $env:ANYBOT_PAIR_CODE }
if ($env:ANYBOT_PRINT -eq "1") { $Print = $true }
if ($Mode -eq "--print" -or $Mode -eq "print" -or $Mode -eq "doctor") { $Print = $true; if ($Mode -eq "doctor") { $script:Doctor = $true } }
function Say($msg) { Write-Host " $msg" }
function Ok($msg) { Write-Host " ✓ $msg" -ForegroundColor Green }
function Warn($msg) { Write-Host " ! $msg" -ForegroundColor Yellow }
function Die($msg, $hint) { Write-Host "`n ✗ $msg" -ForegroundColor Red; if ($hint) { Write-Host " $hint" }; exit 1 }
$ArchRaw = $env:PROCESSOR_ARCHITECTURE
if ($env:PROCESSOR_ARCHITEW6432) { $ArchRaw = $env:PROCESSOR_ARCHITEW6432 }
$ArchId = "x86_64"
$RustHost = "x86_64-pc-windows-msvc"
if ($ArchRaw -eq "ARM64") { $ArchId = "arm64"; $RustHost = "aarch64-pc-windows-msvc" }
elseif ($ArchRaw -ne "AMD64") { $ArchId = $ArchRaw; $RustHost = "" }
$Interactive = (-not $env:ANYBOT_NONINTERACTIVE) -and (-not $env:CI) -and [Environment]::UserInteractive -and -not [Console]::IsInputRedirected
function Report {
$ok = [bool]$RustHost
if ($script:Doctor) {
Write-Host "AnyBot 安裝檢查 Windows $ArchRaw"
Write-Host " Rust 目標 $(if ($RustHost) { $RustHost } else { '不支援' })"
Write-Host " 建議 app(有桌面)或 hands(讓大腦操控這台登入中的桌面)"
Write-Host " 伺服器 請在 Linux 上架(install.sh server)。這台當大腦:裝完 anybot setup --brain"
return
}
$obj = [ordered]@{
ok = $ok
os = "Windows"
arch = "$ArchRaw"
arch_id = "$ArchId"
rust_host = "$RustHost"
distro = "Windows"
libc = "msvc"
pm = "winget"
desktop = $true
headless = $false
wsl = $false
systemd = $false
recommended = "app"
server_ok = $false
modes = @("app", "hands", "deps")
notes = @(
"操控的是登入中的 Windows 桌面(工作排程器只在使用者登入時執行)。",
"伺服器請用 Linux 的 install.sh server。"
)
one_click = @{
app = "irm https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.ps1 | iex"
hands = "`$env:ANYBOT_MODE='hands'; `$env:ANYBOT_PAIR_URL='網址'; `$env:ANYBOT_PAIR_CODE='配對碼'; irm https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.ps1 | iex"
server = "curl -fsSL https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.sh | sh -s -- server"
print = "powershell -File install.ps1 -Print"
}
}
$obj | ConvertTo-Json -Compress
if (-not $ok) { exit 1 }
}
if ($Print) { Report; exit 0 }
if (-not $RustHost) { Die "不支援的 Windows 架構:$ArchRaw" "支援 x64 與 arm64。" }
if (-not $Mode -or $Mode -eq "auto" -or $Mode -eq "guide") {
if ($Interactive) {
Write-Host ""
Write-Host " ◆ AnyBot Windows $ArchRaw" -ForegroundColor Cyan
Write-Host " 1 桌面 App"
Write-Host " 2 手腳(讓另一台的 Bot 操控這台登入中的桌面)"
Write-Host " 3 只裝編譯需要的東西"
Write-Host " 伺服器請在 Linux 上安裝。選 1/2/3 [1]: " -NoNewline
$c = Read-Host
switch ($c) {
"2" { $Mode = "hands" }
"3" { $Mode = "deps" }
default { $Mode = "app" }
}
} else { $Mode = "app" }
}
if ($Mode -eq "server") { Die "Windows 不架帳號伺服器。" "請在任何有 Docker 的 Linux 執行 install.sh server。這台當大腦或手腳即可。" }
if ($Mode -notin @("app", "hands", "deps")) { Die "不認得「$Mode」。" "可以用 app、hands、deps、-Print。" }
Write-Host ""
Write-Host " ◆ AnyBot 安裝 $Mode · Windows $ArchRaw" -ForegroundColor Cyan
function Have($name) { [bool](Get-Command $name -ErrorAction SilentlyContinue) }
function Ensure-WingetPackage($id, $override) {
if (-not (Have "winget")) { return $false }
$args = @("install", "--id", $id, "-e", "--accept-package-agreements", "--accept-source-agreements")
if ($override) { $args += @("--override", $override) }
& winget @args
return $LASTEXITCODE -eq 0
}
function Ensure-Git {
if (Have "git") { Ok "git"; return }
Say "安裝 Git…"
if (-not (Ensure-WingetPackage "Git.Git" $null)) {
Die "沒有 git。" "請安裝 https://git-scm.com/download/win 後再開一個 PowerShell。"
}
$git = "C:\Program Files\Git\cmd"
if (Test-Path $git) { $env:Path = "$git;" + $env:Path }
if (-not (Have "git")) { Die "Git 裝了,但這個視窗還沒看到。" "請關掉 PowerShell 再開一次,然後重跑同一行。" }
Ok "git"
}
function Rust-Ok {
if (-not (Have "rustc")) { return $false }
$v = & rustc --version
if ($v -match "rustc (\d+)\.(\d+)") {
$maj = [int]$Matches[1]; $min = [int]$Matches[2]
return ($maj -gt 1) -or ($maj -eq 1 -and $min -ge $RustMin)
}
return $false
}
function Ensure-Msvc {
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhere) {
$found = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null
if ($found) { Ok "Visual Studio C++ 工具"; return }
}
if (Have "cl") { Ok "cl.exe"; return }
Say "安裝 Visual Studio Build Tools(C++,編譯 Rust 需要,約幾個 GB)…"
$override = "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
if (-not (Ensure-WingetPackage "Microsoft.VisualStudio.2022.BuildTools" $override)) {
Die "沒有 C++ 編譯器。" "請安裝 Visual Studio Build Tools 的「使用 C++ 的桌面開發」,再開一個 PowerShell:https://visualstudio.microsoft.com/visual-cpp-build-tools/"
}
Ok "已安裝 Build Tools。若接下來 rustc 仍找不到 link.exe,請關掉 PowerShell 再開一次。"
}
function Ensure-Rust {
$cargoBin = Join-Path $env:USERPROFILE ".cargo\bin"
if (Test-Path $cargoBin) { $env:Path = "$cargoBin;" + $env:Path }
if (Rust-Ok) { Ok "Rust $((rustc --version).Split(' ')[1])"; return }
Ensure-Msvc
if (Have "rustup") {
Say "更新 Rust…"
& rustup update stable
& rustup default stable
} else {
Say "安裝 Rust(rustup,目標 $RustHost)…"
$init = Join-Path $env:TEMP "rustup-init.exe"
Invoke-WebRequest -Uri "https://win.rustup.rs/x86_64" -OutFile $init
if ($ArchId -eq "arm64") {
Invoke-WebRequest -Uri "https://win.rustup.rs/aarch64" -OutFile $init
}
& $init -y --profile minimal --default-host $RustHost
if ($LASTEXITCODE -ne 0) { Die "安裝 Rust 失敗。" "手動:https://rustup.rs" }
}
$env:Path = "$cargoBin;" + $env:Path
if (-not (Rust-Ok)) { Die "Rust 還是太舊或找不到。" "關掉視窗再開,執行 rustup update stable。" }
if (-not ((& rustup target list --installed) -contains $RustHost)) {
& rustup target add $RustHost
}
Ok "Rust $((rustc --version).Split(' ')[1])"
}
function Find-Source {
$here = $PSScriptRoot
if ($here -and (Test-Path (Join-Path $here "Cargo.toml"))) {
$toml = Get-Content (Join-Path $here "Cargo.toml") -Raw
if ($toml -match 'name = "anybot"') { $script:Src = $here; Ok "用這份原始碼:$here"; return }
}
Ensure-Git
if (Test-Path (Join-Path $Src ".git")) {
Say "更新 $Src …"
& git -C $Src pull --ff-only
if ($LASTEXITCODE -ne 0) { Warn "沒辦法更新,用現有的版本" }
} else {
if (Test-Path $Src) { Die "$Src 已經存在,但不是 AnyBot 的原始碼。" "設 ANYBOT_DIR 換一個資料夾。" }
Say "下載原始碼到 $Src …"
& git clone --depth 1 -b $Branch $Repo $Src
if ($LASTEXITCODE -ne 0) { Die "下載原始碼失敗。" "網路連得到 $Repo 嗎?" }
}
$script:Src = $Src
Ok "原始碼:$Src"
}
function Build-Anybot {
Say "編譯中,第一次約 5–15 分鐘…"
Push-Location $script:Src
& cargo build --release --bin anybot
$code = $LASTEXITCODE
Pop-Location
if ($code -ne 0) { Die "編譯失敗。" "把最後幾行錯誤貼給維護的人。常見原因:沒有 C++ 建置工具,或記憶體不夠。" }
Ok "編譯完成"
}
function Install-Bin {
$from = Join-Path $script:Src "target\release\anybot.exe"
if (-not (Test-Path $from)) { Die "找不到 $from" "" }
$destDir = Join-Path $env:LOCALAPPDATA "AnyBot"
New-Item -ItemType Directory -Force -Path $destDir | Out-Null
$dest = Join-Path $destDir "anybot.exe"
Copy-Item $from $dest -Force
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($userPath -notlike "*$destDir*") {
[Environment]::SetEnvironmentVariable("Path", "$userPath;$destDir", "User")
}
$env:Path = "$destDir;" + $env:Path
$script:Bin = $dest
Ok "已安裝 $dest"
}
function First-Config {
if ($PairUrl -and $PairCode) {
Say "用配對碼接上大腦,不另外詢問。"
& $script:Bin setup $PairUrl $PairCode
if ($LASTEXITCODE -ne 0) { Die "配對失敗。" "配對碼 10 分鐘、一次。到大腦按「加一台電腦」再拿一組。" }
return
}
if ($env:ANYBOT_PROVIDER) {
Say "寫入第一次的模型設定。"
& $script:Bin setup --seed
if ($LASTEXITCODE -ne 0) { Die "寫入第一次設定失敗。" "" }
return
}
if (-not $Interactive) {
Say "尚未做第一次設定。請設 ANYBOT_PAIR_URL 與 ANYBOT_PAIR_CODE,或 ANYBOT_PROVIDER 與 ANYBOT_API_KEY。"
return
}
Write-Host ""
Write-Host " 第一次設定(可略過)"
Write-Host " 1 稍後在 App 裡設定"
Write-Host " 2 貼上 API 金鑰,建立第一個 Bot"
Write-Host " 3 接到大腦,這台當手腳"
Write-Host " 選 1/2/3 [1]: " -NoNewline
switch (Read-Host) {
"2" {
$env:ANYBOT_PROVIDER = Read-Host " 服務 openai / anthropic / xai / gemini / openrouter / opencode / ollama / custom"
$sec = Read-Host " API 金鑰(ollama 可直接 Enter)" -AsSecureString
$env:ANYBOT_API_KEY = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec))
& $script:Bin setup --seed
}
"3" {
$u = Read-Host " 網址"
$c = Read-Host " 配對碼"
if ($u -and $c) { & $script:Bin setup $u $c }
}
}
}
if ($Mode -eq "deps") {
Ensure-Git
Ensure-Rust
Write-Host "`n 好了。在原始碼資料夾:cargo build --release --bin anybot" -ForegroundColor Green
exit 0
}
Ensure-Git
Ensure-Rust
Find-Source
Build-Anybot
Install-Bin
if ($Mode -eq "hands") {
if (-not $PairUrl -and $Interactive) {
Write-Host ""
Write-Host " 把這台接到大腦(AnyBot 按「加一台電腦」)"
$PairUrl = Read-Host " 網址(直接 Enter 先跳過)"
if ($PairUrl) { $PairCode = Read-Host " 配對碼" }
}
if ($PairUrl -and $PairCode) {
& $script:Bin setup $PairUrl $PairCode
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} else {
Write-Host "`n 裝好了。接著:anybot setup 網址 配對碼" -ForegroundColor Green
Say "配對後會在登入時自動執行。Bot 操控的是這個已登入的桌面,不是鎖定畫面。"
}
} else {
First-Config
Write-Host "`n 裝好了。從開始功能表找不到時,直接執行:" -ForegroundColor Green
Write-Host " $($script:Bin)"
Say "要讓 Bot 操控這台:在大腦按「加一台電腦」,用裡面的 Windows 那一行。"
}
Say "要更新:再執行一次同一行指令。"