# DeepSeek Harness - 托盘应用 # 双击桌面快捷方式启动:拉起 dsh web 服务(若未运行),右下角托盘常驻, # 右键菜单可"打开主界面"(Edge 独立应用窗口)或"退出"(停止服务并退出)。 $ErrorActionPreference = 'Continue' Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $DshBin = 'C:\Users\a1703\AppData\Local\npm-cache\_npx\1e7f6d9597241db0\node_modules\@deepseek-ai\dsh\lib\bin.js' $IconFile = 'C:\Users\a1703\.dsh\assets\dsh.ico' $LogDir = 'C:\Users\a1703\.dsh\logs' $LogFile = Join-Path $LogDir 'dsh-app.log' $Url = 'http://127.0.0.1:3080' $Port = 3080 $WorkDir = 'C:\Users\a1703' $Edge = @('C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe', 'C:\Program Files\Microsoft\Edge\Application\msedge.exe') | Where-Object { Test-Path $_ } | Select-Object -First 1 New-Item -ItemType Directory -Path $LogDir -Force | Out-Null function Log([string]$msg) { try { Add-Content -Path $LogFile -Value ("{0} {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $msg) -Encoding UTF8 } catch {} } function Test-Port([int]$port) { try { $c = New-Object System.Net.Sockets.TcpClient $ar = $c.BeginConnect('127.0.0.1', $port, $null, $null) if (-not $ar.AsyncWaitHandle.WaitOne(500)) { $c.Close(); return $false } $c.EndConnect($ar); $c.Close(); return $true } catch { return $false } } function Stop-Dsh { Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like '*@deepseek-ai\dsh*lib*bin.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } } function Stop-EdgeApp { Get-CimInstance Win32_Process -Filter "Name='msedge.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like '*--app=http://127.0.0.1:3080*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } } function Open-Main { try { if ($Edge) { Start-Process $Edge -ArgumentList "--app=$Url" } else { Start-Process $Url } Log 'opened main window' } catch { Log "open main failed: $_" } } # 单实例保护:已有托盘在运行则直接弹出主界面并退出本进程 $mutex = New-Object System.Threading.Mutex($true, 'DSHWebTrayApp') if (-not $mutex.WaitOne(0)) { Log 'another instance is running; opening main window only' Open-Main exit } # 确保服务在跑 if (-not (Test-Port $Port)) { Log 'dsh not listening; starting it' try { Start-Process -FilePath 'node' -ArgumentList @($DshBin, 'web') -WorkingDirectory $WorkDir -WindowStyle Hidden ` -RedirectStandardOutput (Join-Path $LogDir 'dsh-web.out.log') -RedirectStandardError (Join-Path $LogDir 'dsh-web.err.log') } catch { Log "start dsh failed: $_" } $ready = $false for ($i = 0; $i -lt 120; $i++) { Start-Sleep -Milliseconds 500 if (Test-Port $Port) { $ready = $true; break } } if ($ready) { Log 'dsh is ready' } else { Log 'dsh did not become ready within 60s' } } else { Log 'dsh already listening' } # 托盘图标与菜单 $notify = New-Object System.Windows.Forms.NotifyIcon try { $notify.Icon = New-Object System.Drawing.Icon($IconFile) } catch { Log "icon load failed: $_; falling back" try { # 兜底1:同目录下尝试修复版/其他 .ico $alt = Get-ChildItem (Split-Path $IconFile) -Filter '*.ico' -ErrorAction SilentlyContinue | Select-Object -First 1 if ($alt) { $notify.Icon = New-Object System.Drawing.Icon($alt.FullName) } } catch {} if (-not $notify.Icon) { try { $notify.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path) } catch {} } } $notify.Text = 'DeepSeek Harness' $notify.Visible = $true $menu = New-Object System.Windows.Forms.ContextMenuStrip $itemOpen = New-Object System.Windows.Forms.ToolStripMenuItem('打开主界面') $itemOpen.Add_Click({ Open-Main }) $itemExit = New-Object System.Windows.Forms.ToolStripMenuItem('退出') $itemExit.Add_Click({ Log 'exit requested' try { $notify.Visible = $false } catch {} Stop-EdgeApp Stop-Dsh try { $ctx.ExitThread() } catch {} }) $menu.Items.Add($itemOpen) | Out-Null $menu.Items.Add($itemExit) | Out-Null $notify.ContextMenuStrip = $menu $notify.Add_MouseDoubleClick({ if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Left) { Open-Main } }) $ctx = New-Object System.Windows.Forms.ApplicationContext Log 'tray app running' Open-Main [System.Windows.Forms.Application]::Run($ctx)