From e05a065c4edc0777ad1da7c7d94c1e5f13d46e80 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 20:30:21 +0000 Subject: [PATCH 01/14] Fix UnicodeDecodeError reading dashboard index.html on Windows read_text() defaults to the platform locale encoding (cp1252 on Windows), which fails on non-ASCII bytes in index.html. Specify utf-8 explicitly. --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 839da22..9c538c8 100644 --- a/server.py +++ b/server.py @@ -1251,7 +1251,7 @@ if dashboard_dir.exists(): def index(): html_file = BASE_DIR / "dashboard" / "index.html" if html_file.exists(): - content = html_file.read_text() + content = html_file.read_text(encoding="utf-8") content = content.replace('href="styles.css"', 'href="/dashboard/styles.css"') content = content.replace('src="utils.js"', 'src="/dashboard/utils.js"') content = content.replace('src="api.js"', 'src="/dashboard/api.js"') From f22b44a0003416fb8dbfec7fd4661e313d7ed46c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 21:05:14 +0000 Subject: [PATCH 02/14] Auto-detect free port and add one-click launcher start.ps1/start.sh now fall back to the next free port instead of crashing when the configured port is already taken (e.g. by another local app), and auto-open the dashboard in the browser once it's up. install.ps1 also creates a desktop shortcut (Launch-Dashboard.bat) so the dashboard can be started without typing any commands. --- Launch-Dashboard.bat | 4 ++++ install.ps1 | 18 ++++++++++++++++-- start.ps1 | 29 +++++++++++++++++++++++++++-- start.sh | 23 ++++++++++++++++++++++- 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 Launch-Dashboard.bat diff --git a/Launch-Dashboard.bat b/Launch-Dashboard.bat new file mode 100644 index 0000000..adc52e1 --- /dev/null +++ b/Launch-Dashboard.bat @@ -0,0 +1,4 @@ +@echo off +cd /d "%~dp0" +powershell -NoLogo -ExecutionPolicy Bypass -File "%~dp0start.ps1" +pause diff --git a/install.ps1 b/install.ps1 index 75e4eec..0645188 100644 --- a/install.ps1 +++ b/install.ps1 @@ -75,10 +75,24 @@ if (-not (Test-Path .git)) { } } +$launcher = Join-Path $PSScriptRoot "Launch-Dashboard.bat" +$desktop = [Environment]::GetFolderPath("Desktop") +if ((Test-Path $launcher) -and (Test-Path $desktop)) { + $shortcutPath = Join-Path $desktop "Agentic OS Dashboard.lnk" + $shell = New-Object -ComObject WScript.Shell + $shortcut = $shell.CreateShortcut($shortcutPath) + $shortcut.TargetPath = $launcher + $shortcut.WorkingDirectory = $PSScriptRoot + $shortcut.Description = "Launch the Agentic OS dashboard" + $shortcut.Save() + Write-Host "Desktop shortcut created: $shortcutPath" +} + Write-Host "" Write-Host "=== Installation complete! ===" Write-Host "" Write-Host "Next steps:" Write-Host " 1. Edit data/settings.json with your API keys" -Write-Host " 2. Run .\start.ps1 to launch the dashboard" -Write-Host " 3. Open http://127.0.0.1:8080 in your browser" +Write-Host " 2. Double-click the 'Agentic OS Dashboard' shortcut on your Desktop" +Write-Host " (or run .\start.ps1 / .\Launch-Dashboard.bat manually)" +Write-Host " 3. Your browser will open automatically once the server is ready" diff --git a/start.ps1 b/start.ps1 index 700f52d..a30da8b 100644 --- a/start.ps1 +++ b/start.ps1 @@ -36,11 +36,36 @@ $pythonArgs = @($python.Args) & $pythonCommand @pythonArgs -m pip install -r requirements.txt --quiet -$port = & $pythonCommand @pythonArgs -c "import json; f=open('data/settings.json'); d=json.load(f); print(d.get('dashboard',{}).get('port',8080)); f.close()" 2>$null -if (-not $port) { $port = "8080" } +$configuredPort = & $pythonCommand @pythonArgs -c "import json; f=open('data/settings.json'); d=json.load(f); print(d.get('dashboard',{}).get('port',8080)); f.close()" 2>$null +if (-not $configuredPort) { $configuredPort = "8080" } +$configuredPort = [int]$configuredPort + +function Find-FreePort { + param([int]$StartPort, [int]$MaxAttempts = 20) + for ($p = $StartPort; $p -lt $StartPort + $MaxAttempts; $p++) { + $listener = $null + try { + $listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Parse("127.0.0.1"), $p) + $listener.Start() + return $p + } catch { + continue + } finally { + if ($listener) { $listener.Stop() } + } + } + throw "No free port found between $StartPort and $($StartPort + $MaxAttempts - 1). Close some other application or free a port and retry." +} + +$port = Find-FreePort -StartPort $configuredPort +if ($port -ne $configuredPort) { + Write-Warning "Port $configuredPort is already in use (another app, or a leftover Agentic OS process) - using $port instead." +} Write-Host "Dashboard: http://127.0.0.1:$port" Write-Host "Press Ctrl+C to stop" Write-Host "" +Start-Job -ScriptBlock { Start-Sleep -Seconds 2; Start-Process "http://127.0.0.1:$using:port" } | Out-Null + & $pythonCommand @pythonArgs server.py --port $port diff --git a/start.sh b/start.sh index f165161..a143397 100755 --- a/start.sh +++ b/start.sh @@ -24,11 +24,32 @@ fi $PYTHON -m pip install -r requirements.txt --quiet 2>/dev/null # Get port from settings or default -PORT=$($PYTHON -c "import json; f=open('data/settings.json'); d=json.load(f); print(d.get('dashboard',{}).get('port',8080)); f.close()" 2>/dev/null || echo "8080") +CONFIGURED_PORT=$($PYTHON -c "import json; f=open('data/settings.json'); d=json.load(f); print(d.get('dashboard',{}).get('port',8080)); f.close()" 2>/dev/null || echo "8080") + +# Find a free port starting at CONFIGURED_PORT (in case it's already taken by another app) +PORT="$CONFIGURED_PORT" +for _ in $(seq 1 20); do + if ! (exec 3<>"/dev/tcp/127.0.0.1/${PORT}") 2>/dev/null; then + break + fi + exec 3>&- 2>/dev/null || true + PORT=$((PORT + 1)) +done + +if [ "$PORT" != "$CONFIGURED_PORT" ]; then + echo "WARNING: Port ${CONFIGURED_PORT} is already in use - using ${PORT} instead." +fi echo "Dashboard: http://127.0.0.1:${PORT}" echo "Press Ctrl+C to stop" echo "" +# Best-effort auto-open browser once the server is up +( sleep 2 + if command -v xdg-open &>/dev/null; then xdg-open "http://127.0.0.1:${PORT}" &>/dev/null + elif command -v open &>/dev/null; then open "http://127.0.0.1:${PORT}" &>/dev/null + fi +) & + # Start server $PYTHON server.py --port "${PORT}" From 9c1cac130a5dcfe10c556b5e4c13a49128fa829e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 21:49:19 +0000 Subject: [PATCH 03/14] Address CodeRabbit review: harden port parsing and shortcut creation - install.ps1: wrap desktop shortcut creation in try/catch so a COM failure doesn't abort the whole installer (ErrorActionPreference=Stop) - start.ps1: coerce dashboard.port to int on the Python side so a float value in settings.json doesn't crash the [int] cast - start.sh: fail loudly with a clear error if no free port is found in 20 attempts, instead of silently launching on an unverified port --- install.ps1 | 20 ++++++++++++-------- start.ps1 | 2 +- start.sh | 7 +++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/install.ps1 b/install.ps1 index 0645188..a9bab85 100644 --- a/install.ps1 +++ b/install.ps1 @@ -78,14 +78,18 @@ if (-not (Test-Path .git)) { $launcher = Join-Path $PSScriptRoot "Launch-Dashboard.bat" $desktop = [Environment]::GetFolderPath("Desktop") if ((Test-Path $launcher) -and (Test-Path $desktop)) { - $shortcutPath = Join-Path $desktop "Agentic OS Dashboard.lnk" - $shell = New-Object -ComObject WScript.Shell - $shortcut = $shell.CreateShortcut($shortcutPath) - $shortcut.TargetPath = $launcher - $shortcut.WorkingDirectory = $PSScriptRoot - $shortcut.Description = "Launch the Agentic OS dashboard" - $shortcut.Save() - Write-Host "Desktop shortcut created: $shortcutPath" + try { + $shortcutPath = Join-Path $desktop "Agentic OS Dashboard.lnk" + $shell = New-Object -ComObject WScript.Shell + $shortcut = $shell.CreateShortcut($shortcutPath) + $shortcut.TargetPath = $launcher + $shortcut.WorkingDirectory = $PSScriptRoot + $shortcut.Description = "Launch the Agentic OS dashboard" + $shortcut.Save() + Write-Host "Desktop shortcut created: $shortcutPath" + } catch { + Write-Warning "Could not create Desktop shortcut: $_" + } } Write-Host "" diff --git a/start.ps1 b/start.ps1 index a30da8b..a6da799 100644 --- a/start.ps1 +++ b/start.ps1 @@ -36,7 +36,7 @@ $pythonArgs = @($python.Args) & $pythonCommand @pythonArgs -m pip install -r requirements.txt --quiet -$configuredPort = & $pythonCommand @pythonArgs -c "import json; f=open('data/settings.json'); d=json.load(f); print(d.get('dashboard',{}).get('port',8080)); f.close()" 2>$null +$configuredPort = & $pythonCommand @pythonArgs -c "import json; f=open('data/settings.json'); d=json.load(f); print(int(d.get('dashboard',{}).get('port',8080))); f.close()" 2>$null if (-not $configuredPort) { $configuredPort = "8080" } $configuredPort = [int]$configuredPort diff --git a/start.sh b/start.sh index a143397..4dd584d 100755 --- a/start.sh +++ b/start.sh @@ -28,14 +28,21 @@ CONFIGURED_PORT=$($PYTHON -c "import json; f=open('data/settings.json'); d=json. # Find a free port starting at CONFIGURED_PORT (in case it's already taken by another app) PORT="$CONFIGURED_PORT" +FOUND=0 for _ in $(seq 1 20); do if ! (exec 3<>"/dev/tcp/127.0.0.1/${PORT}") 2>/dev/null; then + FOUND=1 break fi exec 3>&- 2>/dev/null || true PORT=$((PORT + 1)) done +if [ "$FOUND" -eq 0 ]; then + echo "ERROR: Could not find a free port after 20 attempts starting at ${CONFIGURED_PORT}." + exit 1 +fi + if [ "$PORT" != "$CONFIGURED_PORT" ]; then echo "WARNING: Port ${CONFIGURED_PORT} is already in use - using ${PORT} instead." fi From fec0601722a6957c0490d96178cfe6c0aded2fb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 22:15:47 +0000 Subject: [PATCH 04/14] Add a Terminal page to the dashboard Adds a real shell terminal to the dashboard sidebar: POST /api/terminal/run executes a command via subprocess in a server-tracked working directory (with cd support), and GET /api/terminal/session returns the current cwd. The frontend renders a scrollback panel with command history (up/down arrows) styled to match the existing chat UI. Local-only power feature: it executes arbitrary shell commands, same trust model as the existing agent CLIs the dashboard already shells out to. --- dashboard/api.js | 3 + dashboard/index.html | 1 + dashboard/pages/terminal.js | 114 ++++++++++++++++++++++++++++++++++++ dashboard/styles.css | 32 ++++++++++ dashboard/utils.js | 1 + server.py | 37 ++++++++++++ 6 files changed, 188 insertions(+) create mode 100644 dashboard/pages/terminal.js diff --git a/dashboard/api.js b/dashboard/api.js index bdb2284..886e71f 100644 --- a/dashboard/api.js +++ b/dashboard/api.js @@ -52,6 +52,9 @@ const api = { discoverStandards: () => api.post('/api/standards/discover'), chat: (agent, message, controller) => api.post('/api/chat', { agent, message }, controller), getChatHistory: () => api.get('/api/chat/history'), + // Terminal + getTerminalSession: () => api.get('/api/terminal/session'), + runTerminalCommand: (command) => api.post('/api/terminal/run', { command }), // Kanban getKanbanBoard: (status) => api.get(status ? `/api/kanban/board?status=${encodeURIComponent(status)}` : '/api/kanban/board'), getKanbanTask: (id) => api.get(`/api/kanban/tasks/${encodeURIComponent(id)}`), diff --git a/dashboard/index.html b/dashboard/index.html index 4f7899e..6a600ba 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -33,6 +33,7 @@