# Life OS Bridge — установка одной командой. # # irm https://mcp.archik.tech/devices/install.ps1 | iex # # Скачивает мост для этой системы, сверяет контрольную сумму, кладёт его в # %LOCALAPPDATA%\LifeOS и запускает подключение. Права администратора не # нужны: ничего не пишется вне профиля пользователя. # # Всё лежит в блоке, который вызывается последней строкой. Так задумано: # оборванная загрузка оставит незакрытую скобку, и PowerShell не выполнит # половину работы, а откажется разбирать файл. Заодно ни переменные, ни # $ErrorActionPreference не протекают в сессию того, кто это запустил. & { $ErrorActionPreference = 'Stop' # Контрольные суммы сборок, которые сервер отдаёт прямо сейчас. Их # подставили при выдаче этого файла, поэтому они описывают ровно те # двоичные файлы, которые скачаются следующей командой. $sums = @{ 'windows-amd64' = '94b1abca0ba7e3886764b520015ccc4b01a75acd4874a78f1df4ed3347b5e5f0' 'windows-arm64' = 'dfee41b7ceeb8b1d67c8aa68ae990cac578272f646e7f52720ae753a463bf0a0' } $server = if ($env:LIFEOS_SERVER) { $env:LIFEOS_SERVER } else { 'https://mcp.archik.tech' } $dir = if ($env:LIFEOS_BIN_DIR) { $env:LIFEOS_BIN_DIR } else { Join-Path $env:LOCALAPPDATA 'LifeOS' } # PROCESSOR_ARCHITECTURE говорит про процесс, а не про машину: в # 32-разрядном PowerShell на ARM он соврёт, и правду хранит # PROCESSOR_ARCHITEW6432. $arch = if ($env:PROCESSOR_ARCHITEW6432 -eq 'ARM64' -or $env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64' } else { 'amd64' } $build = "windows-$arch" New-Item -ItemType Directory -Force -Path $dir | Out-Null $target = Join-Path $dir 'lifeos-bridge.exe' $fresh = "$target.new" # Скобки по той же причине, что и в install.sh: имя переменной не должно # упираться в букву или знак вне ASCII. Write-Host " Скачиваем мост для ${build}…" try { Invoke-WebRequest -UseBasicParsing -Uri "$server/devices/bridge/$build" -OutFile $fresh } catch { throw "Не удалось скачать $server/devices/bridge/$build — $($_.Exception.Message)" } $expected = $sums[$build] $actual = (Get-FileHash -Algorithm SHA256 -Path $fresh).Hash.ToLower() if ($expected -and $expected -ne $actual) { Remove-Item -Force $fresh throw 'Контрольная сумма не совпала. Файл не установлен — попробуйте ещё раз, а если повторится, напишите нам.' } if (-not $expected) { Write-Host ' Сервер не назвал контрольную сумму — проверку пропускаем.' } # Работающий мост держит свой .exe открытым: заменить файл нельзя, а # переименовать — можно. Поэтому старый отодвигается в сторону и # убирается потом, когда его отпустят. if (Test-Path $target) { $aside = "$target.old" Remove-Item -Force $aside -ErrorAction SilentlyContinue Move-Item -Force $target $aside } Move-Item -Force $fresh $target Remove-Item -Force "$target.old" -ErrorAction SilentlyContinue Write-Host " Мост установлен: $target" Write-Host '' # LIFEOS_ADMIN=1 ставит мост с правами администратора: он будет # запускаться по входу в систему как Запланированная задача с «высшими # правами», и уровень «Администратор» на странице подтверждения станет # настоящим. Без этого флага — обычная установка от пользователя, как # раньше. $admin = $env:LIFEOS_ADMIN -eq '1' if (-not $admin) { # Флаг стоит до команды: разбор аргументов в мосте останавливается # на первом слове без дефиса, и после «enroll» --server уже не увидят. & $target --server $server enroll return } # Задача с высшими правами имеет смысл только из окна, которое само # запущено от администратора: обычный сеанс не может её зарегистрировать. # Проверяем это здесь, а не оставляем на невнятную ошибку планировщика. $isAdmin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ` ).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) if (-not $isAdmin) { throw 'Для установки с правами администратора запустите PowerShell от имени администратора и повторите команду.' } # Обычная установка оставила ярлык в «Автозагрузке» и, скорее всего, уже # работающий мост. Подключение ниже перепишет тот же bridge.json, и при # следующем входе стартуют оба: задача с правами и ярлык без них, на # одном токене, так что команду с правами подхватит кто успеет. Один # компьютер — один мост: ярлык убираем, прежний процесс останавливаем. $startup = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs\Startup\Life OS Bridge.vbs' if (Test-Path $startup) { Remove-Item -Force $startup Write-Host ' Прежний автозапуск без прав убран.' } Get-Process -Name 'lifeos-bridge' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue # Сначала подключение: мост получает токен и с --no-autostart сразу # возвращает управление, не занимая окно (запуском будет ведать задача). Write-Host ' Подключение (выберите на странице уровень «Администратор»):' Write-Host '' & $target --server $server --no-autostart enroll if ($LASTEXITCODE -ne 0) { throw 'Подключение не завершено — задача не создавалась.' } # Запланированная задача: запуск по входу текущего пользователя, с # высшими правами (это и даёт мосту административные полномочия), без # ограничения по времени, с перезапуском при сбое. $taskName = 'LifeOS Bridge (admin)' $whoami = "$env:USERDOMAIN\$env:USERNAME" $action = New-ScheduledTaskAction -Execute $target ` -Argument "--server $server --no-autostart" $trigger = New-ScheduledTaskTrigger -AtLogOn -User $whoami $principal = New-ScheduledTaskPrincipal -UserId $whoami ` -LogonType Interactive -RunLevel Highest $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries ` -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Seconds 0) ` -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) Register-ScheduledTask -TaskName $taskName -Force ` -Action $action -Trigger $trigger -Principal $principal ` -Settings $settings | Out-Null # Запустить сейчас, чтобы не ждать перезагрузки. Start-ScheduledTask -TaskName $taskName Write-Host '' Write-Host " Готово. Мост работает как задача «${taskName}» с правами администратора" Write-Host ' и запустится сам при каждом входе в систему.' }