Инструменты пользователя

Инструменты сайта


truenas:application:strata_tray

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

— truenas:application:strata_tray [2026/11/12 17:41] (текущий)
werwolf Strata Tray; кванты: что можно на текущем ПК и после апгрейда
Строка 1: Строка 1:
 +====== Strata Tray: значок в трее для запуска и остановки Strata (Windows) ======
 +
 +===== Введение =====
 +
 +**Strata Tray** — маленькая программа для Windows, которая висит в трее (возле часов) и:
 +  * показывает статус локального сервера Strata цветом значка;​
 +  * по **правому клику** даёт меню **Запустить / Остановить**;​
 +  * запускается вместе с Windows (модель при этом **не** запускается — только значок);​
 +  * показывает уведомления:​ «Strata запущена»,​ «остановлена»,​ «не запустилась» с последними строками журнала,​ а также если сервер неожиданно упал.
 +
 +Написана на PowerShell + WinForms: ничего доустанавливать не нужно. Работает поверх пресета Strata из инструкции [[:​truenas:​application:​strata_qwen_goose|Qwen3.8-Flash-Next на Strata + Goose Desktop]].
 +
 +===== Требования =====
 +
 +  * Windows 10/11, Windows PowerShell 5.1 (есть в системе).
 +  * Установленная Strata (''​%LOCALAPPDATA%\Strata\terminal\current\bin\strata.cmd''​).
 +  * Сохранённый пресет Strata — по умолчанию ''​Goose IQ3_S official 8081''​ (порт 8081).
 +
 +===== Как это выглядит =====
 +
 +^ Значок ^ Состояние ^
 +| зелёный «S» | Strata работает,​ модель загружена |
 +| жёлтый «S» | запускается / загружает модель / останавливается |
 +| серый «S» | остановлена |
 +
 +Подсказка при наведении:​ ''​Strata:​ работает · порт 8081''​. Двойной клик — уведомление с моделью,​ адресом API и размером контекста.
 +
 +Меню по правому клику:
 +
 +<​code>​
 +Strata: работает ​                      ​(статус)
 +qwen3.8-flash-next-iq3_s · http://​127.0.0.1:​8081
 +───────────────
 +Запустить ​                             (активно,​ когда Strata остановлена)
 +Остановить ​                            ​(активно,​ когда Strata работает)
 +───────────────
 +Монитор Strata ​                        ​(strata monitor в окне консоли)
 +Журнал запуска/​остановки ​              ​(actions.log в Блокноте)
 +Папка логов движка
 +───────────────
 +✓ Запускать вместе с Windows ​          ​(включить/​выключить автозапуск)
 +Выход (Strata продолжит работать)
 +</​code>​
 +
 +===== Как работает =====
 +
 +  * Каждые 5 секунд запрашивает ''​http://​127.0.0.1:​8081/​v1/​models''​. Ответ со статусом ''​loaded''​ — «работает»;​ сервер не отвечает — «остановлена». Ответ 401/403 (включён API-ключ) тоже считается «работает».
 +  * **Запустить** выполняет в скрытом окне: ''​strata launch --preset "Goose IQ3_S official 8081" --no-tui''​ (загрузка ~1 минута).
 +  * **Остановить**:​ ''​strata launch --stop --preset "Goose IQ3_S official 8081"''​ — освобождает VRAM и ~50 GB RAM.
 +  * Весь вывод этих команд дописывается в ''​%LOCALAPPDATA%\StrataTray\actions.log''​.
 +  * Автозапуск — ярлык ''​Strata Tray.lnk''​ в папке «Автозагрузка» (''​shell:​startup''​),​ он запускает ''​wscript.exe StrataTray.vbs'',​ а тот — PowerShell без окна консоли.
 +  * Запущена только одна копия (повторный запуск ничего не делает).
 +  * **Выход** закрывает только значок,​ сама Strata продолжает работать.
 +
 +===== Шаг 1: Файлы программы =====
 +
 +Создайте папку, например ''​F:​\projects\inst\strata-tray'',​ и сохраните в неё четыре файла ниже (у каждого блока кода есть ссылка для скачивания — имя файла над блоком) плюс ''​settings.json''​.
 +
 +**Важно:​** Windows PowerShell 5.1 читает ''​.ps1''​ без BOM в кодировке ANSI, и русские строки превращаются в кракозябры. После скачивания пересохраните ''​.ps1''​ в UTF-8 **с BOM** (в Блокноте:​ «Сохранить как» → кодировка «UTF-8 с BOM») или выполните в папке с файлами:​
 +
 +<code powershell>​
 +Get-ChildItem *.ps1 | ForEach-Object { $t = [IO.File]::​ReadAllText($_.FullName,​ [Text.Encoding]::​UTF8);​ [IO.File]::​WriteAllText($_.FullName,​ $t, (New-Object Text.UTF8Encoding($true))) }
 +</​code>​
 +
 +==== settings.json — настройки ====
 +
 +<code json settings.json>​
 +{
 +  "​Preset":​ "Goose IQ3_S official 8081",
 +  "​Port":​ 8081,
 +  "​PollSeconds":​ 5
 +}
 +</​code>​
 +
 +  * ''​Preset''​ — имя пресета Strata (список:​ ''​strata launch --list-presets''​).
 +  * ''​Port''​ — порт API этого пресета.
 +  * ''​PollSeconds''​ — как часто проверять статус.
 +
 +После установки настройки лежат в ''​%LOCALAPPDATA%\StrataTray\app\settings.json'';​ после правки — «Выход» в меню значка и запустить снова (или перезагрузить ПК).
 +
 +==== StrataTray.ps1 — основная программа ====
 +
 +<code powershell StrataTray.ps1>​
 +# Strata Tray: значок в трее, который показывает статус Strata и запускает/​останавливает её.
 +# Запуск без окна: StrataTray.vbs. Проверка без интерфейса:​ StrataTray.ps1 -SelfTest [-Cycle]
 +param(
 +    [switch]$SelfTest, ​  # вывести статус в консоль и выйти
 +    [switch]$Cycle ​      # вместе с -SelfTest: остановить и снова запустить Strata теми же командами,​ что и меню
 +)
 +
 +Add-Type -AssemblyName System.Windows.Forms
 +Add-Type -AssemblyName System.Drawing
 +
 +# ---------- настройки ----------
 +$AppDir = Split-Path -Parent $MyInvocation.MyCommand.Path
 +$Cfg = @{ Preset = 'Goose IQ3_S official 8081'; Port = 8081; PollSeconds = 5 }
 +$CfgPath = Join-Path $AppDir '​settings.json'​
 +if (Test-Path $CfgPath) {
 +    try {
 +        $j = Get-Content $CfgPath -Raw -Encoding UTF8 | ConvertFrom-Json
 +        foreach ($p in $j.PSObject.Properties) { $Cfg[$p.Name] = $p.Value }
 +    } catch { }
 +}
 +$Strata ​   = Join-Path $env:​LOCALAPPDATA '​Strata\terminal\current\bin\strata.cmd'​
 +$BaseUrl ​  = "​http://​127.0.0.1:​$($Cfg.Port)"​
 +$DataDir ​  = Join-Path $env:​LOCALAPPDATA '​StrataTray'​
 +$ActionLog = Join-Path $DataDir '​actions.log'​
 +$EngineLogDir = Join-Path $env:​LOCALAPPDATA '​Strata\launch'​
 +$StartupLnk = Join-Path ([Environment]::​GetFolderPath('​Startup'​)) '​Strata Tray.lnk'​
 +New-Item -ItemType Directory -Force $DataDir | Out-Null
 +
 +# ---------- состояние ----------
 +$script:​Proc = $null        # процесс strata launch / --stop, который сейчас выполняется
 +$script:​Pending = $null     # '​start'​ | '​stop'​ | $null
 +$script:​LastState = $null   # '​running'​ | '​loading'​ | '​stopped'​ | '​starting'​ | '​stopping'​
 +$script:​LastStatus = @{ Up = $false }
 +
 +function Write-ActionLog([string]$text) {
 +    try { Add-Content -Path $ActionLog -Value $text -Encoding UTF8 } catch { }
 +}
 +
 +function Get-StrataStatus {
 +    try {
 +        $req = [System.Net.HttpWebRequest]::​Create("​$BaseUrl/​v1/​models"​)
 +        $req.Timeout = 2000; $req.ReadWriteTimeout = 2000; $req.Proxy = $null
 +        $resp = $req.GetResponse()
 +        try {
 +            $body = (New-Object IO.StreamReader($resp.GetResponseStream())).ReadToEnd()
 +        } finally { $resp.Close() }
 +        $m = ($body | ConvertFrom-Json).data | Select-Object -First 1
 +        return @{ Up = $true; Model = $m.id; Loaded = ($m.status.value -eq '​loaded'​);​ Ctx = $m.meta.n_ctx }
 +    } catch {
 +        $e = $_.Exception
 +        while ($e -and -not ($e -is [System.Net.WebException])) { $e = $e.InnerException }
 +        if ($e -and $e.Response) {
 +            # сервер ответил ошибкой HTTP (например 401 при включённом API-ключе) — значит он работает
 +            return @{ Up = $true; Model = '?';​ Loaded = $true; Ctx = $null }
 +        }
 +        return @{ Up = $false }
 +    }
 +}
 +
 +function Start-StrataCommand([string]$argLine,​ [string]$kind) {
 +    if (-not (Test-Path $Strata)) { return "Не найден $Strata — Strata не установлена?"​ }
 +    Write-ActionLog "​`r`n==== $(Get-Date -Format '​yyyy-MM-dd HH:​mm:​ss'​) ${kind}: strata $argLine"​
 +    $psi = New-Object System.Diagnostics.ProcessStartInfo
 +    $psi.FileName = $env:​ComSpec
 +    $psi.Arguments = '/d /c ""'​ + $Strata + '"​ ' + $argLine + ' >> "'​ + $ActionLog + '"​ 2>&​1"'​
 +    $psi.UseShellExecute = $false
 +    $psi.CreateNoWindow = $true
 +    $script:​Proc = [System.Diagnostics.Process]::​Start($psi)
 +    $script:​Pending = $kind
 +    return $null
 +}
 +
 +function Start-Strata { Start-StrataCommand ('​launch --preset "'​ + $Cfg.Preset + '"​ --no-tui'​) '​start'​ }
 +function Stop-Strata ​ { Start-StrataCommand ('​launch --stop --preset "'​ + $Cfg.Preset + '"'​) '​stop'​ }
 +
 +function Get-LogTail([int]$n = 4) {
 +    try {
 +        $lines = Get-Content $ActionLog -Tail 40 -Encoding UTF8 | Where-Object { $_.Trim() }
 +        return (($lines | Select-Object -Last $n) -join "​`n"​)
 +    } catch { return ''​ }
 +}
 +
 +function Set-Autostart([bool]$on) {
 +    if ($on) {
 +        $sh = New-Object -ComObject WScript.Shell
 +        $l = $sh.CreateShortcut($StartupLnk)
 +        $l.TargetPath = Join-Path $env:WINDIR '​System32\wscript.exe'​
 +        $l.Arguments = '"'​ + (Join-Path $AppDir '​StrataTray.vbs'​) + '"'​
 +        $l.WorkingDirectory = $AppDir
 +        $l.Description = '​Strata Tray — статус и запуск Strata'​
 +        $l.Save()
 +    } elseif (Test-Path $StartupLnk) {
 +        Remove-Item $StartupLnk -Force
 +    }
 +}
 +
 +# ---------- режим проверки без интерфейса ----------
 +if ($SelfTest) {
 +    function Wait-Proc { while ($script:​Proc -and -not $script:​Proc.HasExited) { Start-Sleep -Milliseconds 500 }; " ​ код выхода:​ $($script:​Proc.ExitCode)"​ }
 +    $s = Get-StrataStatus
 +    "​Статус:​ Up=$($s.Up) Model=$($s.Model) Loaded=$($s.Loaded) Ctx=$($s.Ctx) ​ ($BaseUrl, пресет '​$($Cfg.Preset)'​)"​
 +    "​Автозапуск:​ $(Test-Path $StartupLnk) ​ ($StartupLnk)"​
 +    if ($Cycle) {
 +        "​Остановка...";​ $err = Stop-Strata;​ if ($err) { $err; exit 1 }; Wait-Proc
 +        $s = Get-StrataStatus;​ " ​ после остановки:​ Up=$($s.Up)"​
 +        "​Запуск...";​ $t0 = Get-Date; $err = Start-Strata;​ if ($err) { $err; exit 1 }; Wait-Proc
 +        $s = Get-StrataStatus;​ " ​ после запуска:​ Up=$($s.Up) Loaded=$($s.Loaded) за $([int]((Get-Date) - $t0).TotalSeconds) с"
 +        "​Хвост журнала:";​ Get-LogTail 6
 +    }
 +    exit 0
 +}
 +
 +# ---------- один экземпляр ----------
 +$created = $false
 +$script:​Mutex = New-Object System.Threading.Mutex($true,​ '​Local\StrataTray',​ [ref]$created)
 +if (-not $created) { exit 0 }
 +
 +# ---------- значки ----------
 +function New-StatusIcon([System.Drawing.Color]$color) {
 +    $size = 32
 +    $bmp = New-Object System.Drawing.Bitmap($size,​ $size)
 +    $g = [System.Drawing.Graphics]::​FromImage($bmp)
 +    $g.SmoothingMode = '​AntiAlias'​
 +    $g.TextRenderingHint = '​AntiAliasGridFit'​
 +    $g.FillEllipse((New-Object System.Drawing.SolidBrush($color)),​ 1, 1, $size - 2, $size - 2)
 +    $g.DrawEllipse((New-Object System.Drawing.Pen([System.Drawing.Color]::​FromArgb(90,​ 0, 0, 0), 1.5)), 1, 1, $size - 3, $size - 3)
 +    $font = New-Object System.Drawing.Font('​Segoe UI', 17, [System.Drawing.FontStyle]::​Bold,​ [System.Drawing.GraphicsUnit]::​Pixel)
 +    $fmt = New-Object System.Drawing.StringFormat
 +    $fmt.Alignment = '​Center';​ $fmt.LineAlignment = '​Center'​
 +    $g.DrawString('​S',​ $font, [System.Drawing.Brushes]::​White,​ (New-Object System.Drawing.RectangleF(0,​ 1, $size, $size)), $fmt)
 +    $g.Dispose()
 +    return [System.Drawing.Icon]::​FromHandle($bmp.GetHicon())
 +}
 +$Icons = @{
 +    running ​ = New-StatusIcon ([System.Drawing.Color]::​FromArgb(46,​ 160, 67))    # зелёный
 +    starting = New-StatusIcon ([System.Drawing.Color]::​FromArgb(212,​ 160, 23))   # жёлтый
 +    loading ​ = New-StatusIcon ([System.Drawing.Color]::​FromArgb(212,​ 160, 23))
 +    stopping = New-StatusIcon ([System.Drawing.Color]::​FromArgb(212,​ 160, 23))
 +    stopped ​ = New-StatusIcon ([System.Drawing.Color]::​FromArgb(128,​ 128, 128))  # серый
 +}
 +$StateText = @{
 +    running ​ = '​работает'​
 +    loading ​ = '​загружает модель'​
 +    starting = '​запускается…'​
 +    stopping = '​останавливается…'​
 +    stopped ​ = '​остановлена'​
 +}
 +
 +# ---------- значок и меню ----------
 +$Notify = New-Object System.Windows.Forms.NotifyIcon
 +$Notify.Icon = $Icons.stopped
 +$Notify.Text = '​Strata:​ проверка…'​
 +$Notify.Visible = $true
 +
 +$Menu = New-Object System.Windows.Forms.ContextMenuStrip
 +$miStatus ​ = $Menu.Items.Add('​Strata:​ проверка…'​);​ $miStatus.Enabled = $false
 +$miModel ​  = $Menu.Items.Add(''​);​ $miModel.Enabled = $false
 +[void]$Menu.Items.Add('​-'​)
 +$miStart ​  = $Menu.Items.Add('​Запустить'​)
 +$miStop ​   = $Menu.Items.Add('​Остановить'​)
 +[void]$Menu.Items.Add('​-'​)
 +$miMonitor = $Menu.Items.Add('​Монитор Strata'​)
 +$miLog ​    = $Menu.Items.Add('​Журнал запуска/​остановки'​)
 +$miEngine ​ = $Menu.Items.Add('​Папка логов движка'​)
 +[void]$Menu.Items.Add('​-'​)
 +$miAuto ​   = New-Object System.Windows.Forms.ToolStripMenuItem('​Запускать вместе с Windows'​)
 +$miAuto.CheckOnClick = $true
 +$miAuto.Checked = (Test-Path $StartupLnk)
 +[void]$Menu.Items.Add($miAuto)
 +$miExit ​   = $Menu.Items.Add('​Выход (Strata продолжит работать)'​)
 +$miStart.Font = New-Object System.Drawing.Font($miStart.Font,​ [System.Drawing.FontStyle]::​Bold)
 +$Notify.ContextMenuStrip = $Menu
 +
 +function Show-Balloon([string]$title,​ [string]$text,​ [string]$kind = '​Info'​) {
 +    if (-not $text) { $text = ' ' }
 +    $Notify.ShowBalloonTip(5000,​ $title, $text, [System.Windows.Forms.ToolTipIcon]::​$kind)
 +}
 +
 +function Update-Ui {
 +    try {
 +        $s = Get-StrataStatus
 +        $script:​LastStatus = $s
 +
 +        # завершилась команда запуска/​остановки
 +        if ($script:​Pending -and $script:​Proc -and $script:​Proc.HasExited) {
 +            $code = $script:​Proc.ExitCode
 +            if ($script:​Pending -eq '​start'​) {
 +                if ($s.Up) { Show-Balloon '​Strata запущена'​ "​$($s.Model) — $BaseUrl"​ }
 +                else { Show-Balloon "​Strata не запустилась (код $code)"​ (Get-LogTail 3) '​Error'​ }
 +            } else {
 +                if (-not $s.Up) { Show-Balloon '​Strata остановлена'​ '​Видеопамять и RAM освобождены'​ }
 +                else { Show-Balloon "​Strata не остановилась (код $code)"​ (Get-LogTail 3) '​Warning'​ }
 +            }
 +            $script:​Pending = $null
 +            $script:​Proc = $null
 +        }
 +
 +        $state = if ($script:​Pending -eq '​start'​) { '​starting'​ }
 +                 ​elseif ($script:​Pending -eq '​stop'​) { '​stopping'​ }
 +                 ​elseif ($s.Up -and $s.Loaded) { '​running'​ }
 +                 ​elseif ($s.Up) { '​loading'​ }
 +                 else { '​stopped'​ }
 +
 +        # неожиданная остановка (упала или остановлена из консоли)
 +        if ($script:​LastState -eq '​running'​ -and $state -eq '​stopped'​) {
 +            Show-Balloon '​Strata остановилась'​ '​Сервер больше не отвечает'​ '​Warning'​
 +        }
 +        $script:​LastState = $state
 +
 +        $Notify.Icon = $Icons[$state]
 +        $tip = "​Strata:​ $($StateText[$state])"​
 +        if ($s.Up -and $s.Model) { $tip += " · порт $($Cfg.Port)"​ }
 +        $Notify.Text = if ($tip.Length -gt 63) { $tip.Substring(0,​ 63) } else { $tip }
 +
 +        $miStatus.Text = "​Strata:​ $($StateText[$state])"​
 +        $miModel.Text = if ($s.Up) { "​$($s.Model) · $BaseUrl"​ } else { "​пресет:​ $($Cfg.Preset)"​ }
 +        $busy = [bool]$script:​Pending
 +        $miStart.Enabled = (-not $busy) -and (-not $s.Up)
 +        $miStop.Enabled ​ = (-not $busy) -and $s.Up
 +        $miAuto.Checked ​ = (Test-Path $StartupLnk)
 +    } catch {
 +        Write-ActionLog "​[tray] ошибка обновления:​ $($_.Exception.Message)"​
 +    }
 +}
 +
 +$miStart.add_Click({
 +    $err = Start-Strata
 +    if ($err) { Show-Balloon '​Strata'​ $err '​Error'​ } else { Show-Balloon '​Strata'​ '​Запуск… загрузка модели занимает около минуты'​ }
 +    Update-Ui
 +})
 +$miStop.add_Click({
 +    $err = Stop-Strata
 +    if ($err) { Show-Balloon '​Strata'​ $err '​Error'​ }
 +    Update-Ui
 +})
 +$miMonitor.add_Click({
 +    if (Test-Path $Strata) {
 +        Start-Process -FilePath $env:​ComSpec -ArgumentList ('/k ""'​ + $Strata + '"​ monitor --url ' + $BaseUrl + '"'​)
 +    }
 +})
 +$miLog.add_Click({
 +    if (-not (Test-Path $ActionLog)) { Write-ActionLog ''​ }
 +    Start-Process notepad.exe -ArgumentList ('"'​ + $ActionLog + '"'​)
 +})
 +$miEngine.add_Click({ if (Test-Path $EngineLogDir) { Start-Process explorer.exe -ArgumentList ('"'​ + $EngineLogDir + '"'​) } })
 +$miAuto.add_Click({
 +    try { Set-Autostart $miAuto.Checked } catch { Show-Balloon '​Strata Tray' "Не удалось изменить автозапуск:​ $($_.Exception.Message)"​ '​Error'​ }
 +    $miAuto.Checked = (Test-Path $StartupLnk)
 +})
 +$miExit.add_Click({
 +    $Timer.Stop()
 +    $Notify.Visible = $false
 +    $Notify.Dispose()
 +    [System.Windows.Forms.Application]::​Exit()
 +})
 +$Notify.add_DoubleClick({
 +    $s = $script:​LastStatus
 +    if ($s.Up) { Show-Balloon "​Strata:​ $($StateText[$script:​LastState])"​ "​$($s.Model)`n$BaseUrl/​v1 ​ ·  контекст $($s.Ctx)"​ }
 +    else { Show-Balloon "​Strata:​ $($StateText[$script:​LastState])"​ "​Пресет:​ $($Cfg.Preset)`nПравый клик → Запустить"​ }
 +})
 +$Menu.add_Opening({ Update-Ui })
 +
 +$Timer = New-Object System.Windows.Forms.Timer
 +$Timer.Interval = [int]$Cfg.PollSeconds * 1000
 +$Timer.add_Tick({ Update-Ui })
 +$Timer.Start()
 +Update-Ui
 +
 +[System.Windows.Forms.Application]::​Run()
 +$script:​Mutex.ReleaseMutex()
 +</​code>​
 +
 +==== StrataTray.vbs — запуск без окна консоли ====
 +
 +<code vb StrataTray.vbs>​
 +' Runs StrataTray.ps1 without a console window (used by the autostart shortcut).
 +Set fso = CreateObject("​Scripting.FileSystemObject"​)
 +dir = fso.GetParentFolderName(WScript.ScriptFullName)
 +CreateObject("​WScript.Shell"​).Run "​powershell.exe -NoProfile -ExecutionPolicy Bypass -STA -WindowStyle Hidden -File """​ & dir & "​\StrataTray.ps1""",​ 0, False
 +</​code>​
 +
 +==== install.ps1 — установка и автозапуск ====
 +
 +<code powershell install.ps1>​
 +# Установка Strata Tray: копирует программу в %LOCALAPPDATA%\StrataTray\app,​ включает автозапуск и запускает значок.
 +# Запуск:​ powershell -ExecutionPolicy Bypass -File install.ps1
 +$ErrorActionPreference = '​Stop'​
 +$src = Split-Path -Parent $MyInvocation.MyCommand.Path
 +$dst = Join-Path $env:​LOCALAPPDATA '​StrataTray\app'​
 +$lnk = Join-Path ([Environment]::​GetFolderPath('​Startup'​)) '​Strata Tray.lnk'​
 +
 +# остановить уже запущенный значок
 +Get-CimInstance Win32_Process -Filter "​Name='​powershell.exe'"​ |
 +    Where-Object { $_.CommandLine -like '​*StrataTray.ps1*'​ } |
 +    ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
 +
 +New-Item -ItemType Directory -Force $dst | Out-Null
 +Copy-Item (Join-Path $src '​StrataTray.ps1'​),​ (Join-Path $src '​StrataTray.vbs'​) $dst -Force
 +if (-not (Test-Path (Join-Path $dst '​settings.json'​))) { Copy-Item (Join-Path $src '​settings.json'​) $dst }
 +
 +$sh = New-Object -ComObject WScript.Shell
 +$l = $sh.CreateShortcut($lnk)
 +$l.TargetPath = Join-Path $env:WINDIR '​System32\wscript.exe'​
 +$l.Arguments = '"'​ + (Join-Path $dst '​StrataTray.vbs'​) + '"'​
 +$l.WorkingDirectory = $dst
 +$l.Description = '​Strata Tray — статус и запуск Strata'​
 +$l.Save()
 +
 +Start-Process (Join-Path $env:WINDIR '​System32\wscript.exe'​) -ArgumentList ('"'​ + (Join-Path $dst '​StrataTray.vbs'​) + '"'​)
 +Write-Host "​Установлено в $dst"
 +Write-Host "​Автозапуск:​ $lnk"
 +Write-Host '​Значок S появится в трее (может быть скрыт под стрелкой ^).'
 +</​code>​
 +
 +==== uninstall.ps1 — удаление ====
 +
 +<code powershell uninstall.ps1>​
 +# Удаление Strata Tray (сама Strata и модель не затрагиваются).
 +$app = Join-Path $env:​LOCALAPPDATA '​StrataTray'​
 +$lnk = Join-Path ([Environment]::​GetFolderPath('​Startup'​)) '​Strata Tray.lnk'​
 +Get-CimInstance Win32_Process -Filter "​Name='​powershell.exe'"​ |
 +    Where-Object { $_.CommandLine -like '​*StrataTray.ps1*'​ } |
 +    ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
 +if (Test-Path $lnk) { Remove-Item $lnk -Force }
 +if (Test-Path $app) { Remove-Item $app -Recurse -Force }
 +Write-Host '​Strata Tray удалён. Strata не остановлена и не удалена.'​
 +</​code>​
 +
 +
 +===== Шаг 2: Установка =====
 +
 +В PowerShell, в папке с файлами:​
 +
 +<code powershell>​
 +powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
 +</​code>​
 +
 +<​code>​
 +Установлено в C:​\Users\<​пользователь>​\AppData\Local\StrataTray\app
 +Автозапуск:​ C:​\Users\<​пользователь>​\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\Strata Tray.lnk
 +Значок S появится в трее (может быть скрыт под стрелкой ^).
 +</​code>​
 +
 +Установщик:​
 +  - останавливает уже запущенный значок (если был);
 +  - копирует ''​StrataTray.ps1''​ и ''​StrataTray.vbs''​ в ''​%LOCALAPPDATA%\StrataTray\app''​ (''​settings.json''​ — только если его там ещё нет, ваши настройки не затираются);​
 +  - создаёт ярлык автозапуска;​
 +  - запускает значок.
 +
 +Чтобы значок всегда был виден, а не прятался под стрелкой:​ Параметры → Персонализация → Панель задач → Другие значки в области уведомлений → включить ''​Windows PowerShell''​ (значок запускается от имени PowerShell).
 +
 +===== Шаг 3: Проверка без интерфейса =====
 +
 +Режим самопроверки печатает статус в консоль (Strata не трогается):​
 +
 +<code powershell>​
 +powershell -NoProfile -ExecutionPolicy Bypass -File "​$env:​LOCALAPPDATA\StrataTray\app\StrataTray.ps1"​ -SelfTest
 +</​code>​
 +
 +<​code>​
 +Статус:​ Up=True Model=qwen3.8-flash-next-iq3_s Loaded=True Ctx=131072 ​ (http://​127.0.0.1:​8081,​ пресет 'Goose IQ3_S official 8081')
 +Автозапуск:​ True  (C:​\Users\...\Startup\Strata Tray.lnk)
 +</​code>​
 +
 +С ключом ''​-Cycle''​ программа **останавливает и снова запускает Strata** теми же командами,​ что и меню (проверено:​ остановка — код 0, запуск — код 0, модель ответила через 66 с):
 +
 +<code powershell>​
 +powershell -NoProfile -ExecutionPolicy Bypass -File "​$env:​LOCALAPPDATA\StrataTray\app\StrataTray.ps1"​ -SelfTest -Cycle
 +</​code>​
 +
 +===== Удаление =====
 +
 +<code powershell>​
 +powershell -NoProfile -ExecutionPolicy Bypass -File .\uninstall.ps1
 +</​code>​
 +
 +Удаляет ярлык автозапуска и папку ''​%LOCALAPPDATA%\StrataTray''​. Strata и модель не затрагиваются.
 +
 +===== Проблемы и решения =====
 +
 +==== Значок не появился ====
 +
 +  * Посмотрите под стрелкой ''​^''​ в трее.
 +  * Проверьте,​ что процесс запущен:<​code powershell>​
 +Get-CimInstance Win32_Process -Filter "​Name='​powershell.exe'"​ | Where-Object { $_.CommandLine -like '​*StrataTray.ps1*'​ } | Select-Object ProcessId
 +</​code>​
 +  * Запустите вручную с окном, чтобы увидеть ошибку:<​code powershell>​
 +powershell -NoProfile -ExecutionPolicy Bypass -STA -File "​$env:​LOCALAPPDATA\StrataTray\app\StrataTray.ps1"​
 +</​code>​
 +
 +==== Русские надписи — кракозябры ====
 +
 +Файл ''​.ps1''​ сохранён без BOM — см. «Шаг 1», пересохраните в UTF-8 с BOM.
 +
 +==== «Strata не запустилась» ====
 +
 +Откройте «Журнал запуска/​остановки» в меню — там полный вывод ''​strata launch''​. Частые причины:​ порт занят другим процессом,​ не хватает свободной RAM (закройте тяжёлые программы),​ неверное имя пресета в ''​settings.json''​. Подробнее — раздел «Проблемы и решения» на странице [[:​truenas:​application:​strata_qwen_goose|Strata + Goose]].
 +
 +==== Сменил модель / пресет ====
 +
 +Поменяйте ''​Preset''​ и ''​Port''​ в ''​%LOCALAPPDATA%\StrataTray\app\settings.json'',​ затем «Выход» в меню значка и запустите заново:​
 +
 +<code powershell>​
 +wscript.exe "​$env:​LOCALAPPDATA\StrataTray\app\StrataTray.vbs"​
 +</​code>​
 +
 +===== Шпаргалка =====
 +
 +^ Что ^ Где ^
 +| Программа | ''​%LOCALAPPDATA%\StrataTray\app\''​ |
 +| Настройки | ''​%LOCALAPPDATA%\StrataTray\app\settings.json''​ |
 +| Журнал запуска/​остановки | ''​%LOCALAPPDATA%\StrataTray\actions.log''​ |
 +| Автозапуск | ''​shell:​startup\Strata Tray.lnk''​ |
 +| Исходники | ''​F:​\projects\inst\strata-tray\''​ |
  
truenas/application/strata_tray.txt · Последние изменения: 2026/11/12 17:41 — werwolf