Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions dream-server/installers/windows/dream.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,19 @@ function Invoke-Status {
}
}

# Host agent status
try {
$resp = Invoke-WebRequest -Uri $script:DREAM_AGENT_HEALTH_URL `
-TimeoutSec 3 -UseBasicParsing -ErrorAction SilentlyContinue
if ($resp.StatusCode -eq 200) {
Write-AISuccess "Host Agent: running (port $($script:DREAM_AGENT_PORT))"
} else {
Write-AIWarn "Host Agent: responded with $($resp.StatusCode)"
}
} catch {
Write-AIWarn "Host Agent: not responding (port $($script:DREAM_AGENT_PORT))"
}

# Docker services
Write-Host ""
& docker compose @flags ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" 2>$null
Expand Down Expand Up @@ -514,6 +527,11 @@ function Invoke-Start {
Start-NativeInferenceServer
}

# Start host agent (if not already running)
if (-not $Service) {
Invoke-Agent -Action "start"
}

$flags = Get-ComposeFlags
if ($Service) {
Write-AI "Starting $Service..."
Expand Down Expand Up @@ -574,6 +592,9 @@ function Invoke-Stop {
Stop-NativeInferenceServer
}

# Stop host agent
Invoke-Agent -Action "stop"

Write-AISuccess "All services stopped"
}
} finally {
Expand Down Expand Up @@ -739,6 +760,118 @@ function Invoke-Report {
}
}

function Invoke-Agent {
param([string]$Action = "status")

$agentScript = Join-Path (Join-Path $InstallDir "bin") "dream-host-agent.py"
$pidFile = $script:DREAM_AGENT_PID_FILE
$logFile = $script:DREAM_AGENT_LOG_FILE
$port = $script:DREAM_AGENT_PORT
$healthUrl = $script:DREAM_AGENT_HEALTH_URL

switch ($Action.ToLower()) {
"status" {
try {
$resp = Invoke-WebRequest -Uri $healthUrl -TimeoutSec 3 `
-UseBasicParsing -ErrorAction SilentlyContinue
if ($resp.StatusCode -eq 200) {
Write-AISuccess "Host agent: running (port $port)"
} else {
Write-AIWarn "Host agent: responded with status $($resp.StatusCode)"
}
} catch {
Write-AIWarn "Host agent: not responding (port $port)"
}
}
"start" {
# Check if already running
try {
$resp = Invoke-WebRequest -Uri $healthUrl -TimeoutSec 2 `
-UseBasicParsing -ErrorAction SilentlyContinue
if ($resp.StatusCode -eq 200) {
Write-AISuccess "Host agent already running (port $port)"
return
}
} catch { }

# Find Python
$_python3 = Get-Command python3 -ErrorAction SilentlyContinue
if (-not $_python3) { $_python3 = Get-Command python -ErrorAction SilentlyContinue }
if (-not $_python3) {
Write-AIError "Python not found in PATH -- install Python 3 and try again"
return
}
if (-not (Test-Path $agentScript)) {
Write-AIError "Agent script not found: $agentScript"
return
}

# Clean stale PID
if (Test-Path $pidFile) {
try {
$_oldPid = [int](Get-Content $pidFile -Raw).Trim()
Stop-Process -Id $_oldPid -Force -ErrorAction SilentlyContinue
} catch { }
Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
}

$pidDir = Split-Path $pidFile
New-Item -ItemType Directory -Path $pidDir -Force -ErrorAction SilentlyContinue | Out-Null

# Prepend Docker to PATH so the agent can find docker.exe
# (Docker Desktop may not be in the system PATH yet after fresh install)
$_dockerBin = "C:\Program Files\Docker\Docker\resources\bin"
$_agentArgs = "set `"PATH=$_dockerBin;%PATH%`" && `"$($_python3.Source)`" `"$agentScript`" --port $port --pid-file `"$pidFile`" --install-dir `"$InstallDir`" 2>> `"$logFile`""
Start-Process -FilePath "cmd.exe" -ArgumentList "/c", $_agentArgs `
-WindowStyle Hidden -WorkingDirectory $InstallDir

Start-Sleep -Seconds 3
try {
$resp = Invoke-WebRequest -Uri $healthUrl -TimeoutSec 3 `
-UseBasicParsing -ErrorAction SilentlyContinue
if ($resp.StatusCode -eq 200) {
Write-AISuccess "Host agent started (port $port)"
} else {
Write-AIWarn "Host agent started but health check returned $($resp.StatusCode)"
}
} catch {
Write-AIWarn "Host agent started but not yet responding -- check: .\dream.ps1 agent status"
}
}
"stop" {
if (Test-Path $pidFile) {
try {
$_pid = [int](Get-Content $pidFile -Raw).Trim()
Stop-Process -Id $_pid -Force -ErrorAction SilentlyContinue
Write-AISuccess "Host agent stopped (PID $_pid)"
} catch {
Write-AIWarn "Could not stop agent PID: $_"
}
Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
} else {
Write-AI "Host agent not running (no PID file)"
}
}
"restart" {
Invoke-Agent -Action "stop"
Start-Sleep -Seconds 1
Invoke-Agent -Action "start"
}
"logs" {
if (Test-Path $logFile) {
Get-Content $logFile -Tail 100 -Wait
} else {
Write-AIWarn "No log file at $logFile"
}
}
default {
Write-Host ""
Write-Host " Usage: .\dream.ps1 agent [status|start|stop|restart|logs]" -ForegroundColor DarkGray
Write-Host ""
}
}
}

function Show-Help {
Write-Host ""
Write-Host " Dream Server CLI (Windows)" -ForegroundColor Green
Expand Down Expand Up @@ -766,6 +899,8 @@ function Show-Help {
Write-Host "Quick chat via API" -ForegroundColor DarkGray
Write-Host " update " -ForegroundColor Cyan -NoNewline
Write-Host "Pull latest images and restart" -ForegroundColor DarkGray
Write-Host " agent [action] " -ForegroundColor Cyan -NoNewline
Write-Host "Host agent: status|start|stop|restart|logs" -ForegroundColor DarkGray
Write-Host " report " -ForegroundColor Cyan -NoNewline
Write-Host "Generate Windows diagnostics bundle" -ForegroundColor DarkGray
Write-Host " version " -ForegroundColor Cyan -NoNewline
Expand Down Expand Up @@ -807,6 +942,11 @@ switch ($Command.ToLower()) {
"chat" { Invoke-Chat -Message ($Arguments -join " ") }
"update" { Invoke-Update }
"report" { Invoke-Report }
"agent" {
$action = ($Arguments | Select-Object -First 1)
if (-not $action) { $action = "status" }
Invoke-Agent -Action $action
}
"version" { Write-Host "Dream Server v$($script:DS_VERSION) (Windows)" -ForegroundColor Green }
"help" { Show-Help }
default {
Expand Down
7 changes: 7 additions & 0 deletions dream-server/installers/windows/lib/constants.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ $script:OPENCODE_EXE = Join-Path (Join-Path $env:USERPROFILE ".opencode") "bin\o
$script:OPENCODE_CONFIG_DIR = Join-Path (Join-Path $env:USERPROFILE ".config") "opencode"
$script:OPENCODE_PORT = 3003

# Dream Host Agent (host-level extension lifecycle manager)
$script:DREAM_AGENT_PORT = 7710
$script:DREAM_AGENT_PID_FILE = Join-Path (Join-Path $script:DS_INSTALL_DIR "data") "dream-host-agent.pid"
$script:DREAM_AGENT_LOG_FILE = Join-Path (Join-Path $script:DS_INSTALL_DIR "data") "dream-host-agent.log"
$script:DREAM_AGENT_HEALTH_URL = "http://127.0.0.1:7710/health"
$script:DREAM_AGENT_TASK_NAME = "DreamServerHostAgent"

# Timing
$script:INSTALL_START = Get-Date

Expand Down
75 changes: 75 additions & 0 deletions dream-server/installers/windows/phases/07-devtools.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ if ($dryRun) {
if (-not $cloudMode) {
Write-AI "[DRY RUN] Would check for Node.js and install Claude Code + Codex CLI via npm"
}
Write-AI "[DRY RUN] Would start Dream Host Agent on port $($script:DREAM_AGENT_PORT)"
Write-AI "[DRY RUN] Would register $($script:DREAM_AGENT_TASK_NAME) scheduled task for login persistence"
return
}

Expand Down Expand Up @@ -227,4 +229,77 @@ if ($_npmCmd) {
}
}

# ── Dream Host Agent (extension lifecycle management) ────────────────────────
$_agentScript = Join-Path (Join-Path $installDir "bin") "dream-host-agent.py"
if (Test-Path $_agentScript) {
$_python3 = Get-Command python3 -ErrorAction SilentlyContinue
if (-not $_python3) { $_python3 = Get-Command python -ErrorAction SilentlyContinue }

if ($_python3) {
# Kill existing agent on reinstall (matches Linux force-restart pattern)
if (Test-Path $script:DREAM_AGENT_PID_FILE) {
$_oldPid = $null
try {
$_oldPid = [int](Get-Content $script:DREAM_AGENT_PID_FILE -Raw).Trim()
Stop-Process -Id $_oldPid -Force -ErrorAction SilentlyContinue
} catch { }
Remove-Item $script:DREAM_AGENT_PID_FILE -Force -ErrorAction SilentlyContinue
}

# Ensure data directory exists for PID and log files
$pidDir = Split-Path $script:DREAM_AGENT_PID_FILE
New-Item -ItemType Directory -Path $pidDir -Force -ErrorAction SilentlyContinue | Out-Null

# Start agent via cmd.exe wrapper for stderr→log redirect.
# Prepend Docker to PATH so the agent can find docker.exe
# (Docker Desktop may not be in the system PATH yet after fresh install).
$_dockerBin = "C:\Program Files\Docker\Docker\resources\bin"
$_agentArgs = "set `"PATH=$_dockerBin;%PATH%`" && `"$($_python3.Source)`" `"$_agentScript`" --port $($script:DREAM_AGENT_PORT) --pid-file `"$($script:DREAM_AGENT_PID_FILE)`" --install-dir `"$installDir`" 2>> `"$($script:DREAM_AGENT_LOG_FILE)`""
Start-Process -FilePath "cmd.exe" -ArgumentList "/c", $_agentArgs `
-WindowStyle Hidden -WorkingDirectory $installDir

# Brief health check
Start-Sleep -Seconds 3
try {
$resp = Invoke-WebRequest -Uri $script:DREAM_AGENT_HEALTH_URL `
-TimeoutSec 3 -UseBasicParsing -ErrorAction SilentlyContinue
if ($resp.StatusCode -eq 200) {
Write-AISuccess "Dream host agent started (port $($script:DREAM_AGENT_PORT))"
} else {
Write-AIWarn "Dream host agent started but health check returned $($resp.StatusCode)"
}
} catch {
Write-AIWarn "Dream host agent started but not yet responding -- check: .\dream.ps1 agent status"
}

# Register Windows Scheduled Task for login persistence
Unregister-ScheduledTask -TaskName $script:DREAM_AGENT_TASK_NAME `
-Confirm:$false -ErrorAction SilentlyContinue

$taskAction = New-ScheduledTaskAction -Execute "cmd.exe" `
-Argument "/c set `"PATH=$_dockerBin;%PATH%`" && `"$($_python3.Source)`" `"$_agentScript`" --port $($script:DREAM_AGENT_PORT) --pid-file `"$($script:DREAM_AGENT_PID_FILE)`" --install-dir `"$installDir`" 2>> `"$($script:DREAM_AGENT_LOG_FILE)`"" `
-WorkingDirectory $installDir
$taskTrigger = New-ScheduledTaskTrigger -AtLogOn
$taskSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
-StartWhenAvailable -ExecutionTimeLimit ([TimeSpan]::Zero)

Register-ScheduledTask -TaskName $script:DREAM_AGENT_TASK_NAME `
-Action $taskAction -Trigger $taskTrigger -Settings $taskSettings `
-Description "DreamServer Host Agent -- manages extensions and bridges dashboard to host" `
-ErrorAction SilentlyContinue | Out-Null

if ($?) {
Write-AISuccess "Host agent registered to start at login (Task: $($script:DREAM_AGENT_TASK_NAME))"
} else {
Write-AIWarn "Could not register login task -- start manually: .\dream.ps1 agent start"
}
} else {
Write-AIWarn "Python not found -- Dream host agent not started"
Write-AI " Install Python 3 and re-run the installer, or start manually: .\dream.ps1 agent start"
}
} else {
Write-AI "Dream host agent script not found -- skipping"
}

Write-AISuccess "Developer tools setup complete"
Loading