param( [ValidateSet("start", "stop", "restart", "status", "dashboard")] [string]$Action = "status" ) $ErrorActionPreference = "Stop" function Get-OpenClawCmd { $cmd = Get-Command openclaw.cmd -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } $candidate = Join-Path $env:APPDATA "npm\openclaw.cmd" if (Test-Path $candidate) { return $candidate } throw "openclaw.cmd not found" } function Get-DashboardUrlFromOutput { param([string]$Text) if ([string]::IsNullOrWhiteSpace($Text)) { return "" } $match = [regex]::Match($Text, 'https?://\S+') if ($match.Success) { return $match.Value.Trim() } return "" } function Get-TokenizedDashboardUrlFromClipboard { param([string]$BaseUrl) try { $clipboard = Get-Clipboard -Raw -ErrorAction Stop } catch { return "" } if ([string]::IsNullOrWhiteSpace($clipboard)) { return "" } $text = [string]$clipboard $match = [regex]::Match($text, 'https?://\S+#token=[^\s]+') if (-not $match.Success) { return "" } $tokenizedUrl = $match.Value.Trim() if ([string]::IsNullOrWhiteSpace($BaseUrl)) { return $tokenizedUrl } try { $baseUri = [Uri]$BaseUrl $tokenUri = [Uri]$tokenizedUrl if ($baseUri.Scheme -eq $tokenUri.Scheme -and $baseUri.Host -eq $tokenUri.Host -and $baseUri.Port -eq $tokenUri.Port) { return $tokenizedUrl } } catch {} return "" } function Wait-DashboardReady { param([string]$OpenClaw) for ($attempt = 1; $attempt -le 30; $attempt++) { try { $output = & $OpenClaw dashboard --no-open 2>&1 if ($LASTEXITCODE -eq 0) { $text = ($output | Out-String).Trim() $url = Get-DashboardUrlFromOutput -Text $text $tokenizedUrl = Get-TokenizedDashboardUrlFromClipboard -BaseUrl $url if (-not [string]::IsNullOrWhiteSpace($tokenizedUrl)) { return $tokenizedUrl } if (-not [string]::IsNullOrWhiteSpace($url)) { return $url } } } catch {} Start-Sleep -Seconds 2 } return "" } function Open-Url { param([string]$Url) if ([string]::IsNullOrWhiteSpace($Url)) { return $false } try { Start-Process $Url | Out-Null return $true } catch { Write-Warning "opening URL failed: $($_.Exception.Message)" return $false } } $openclaw = Get-OpenClawCmd switch ($Action) { "start" { & $openclaw gateway start & $openclaw gateway status --json } "stop" { & $openclaw gateway stop & $openclaw gateway status --json } "restart" { & $openclaw gateway restart & $openclaw gateway status --json } "status" { & $openclaw gateway status --json } "dashboard" { $dashboardUrl = Wait-DashboardReady -OpenClaw $openclaw if (-not [string]::IsNullOrWhiteSpace($dashboardUrl)) { Write-Host $dashboardUrl if (-not (Open-Url -Url $dashboardUrl)) { Write-Warning "failed to auto-open dashboard; use the printed URL manually" } break } Write-Warning "dashboard is not ready yet" Write-Host "Run this after a bit: openclaw dashboard --no-open" } }