Add actionable version mismatch alerts and watcher management#47
Add actionable version mismatch alerts and watcher management#47
Conversation
Compare git commit timestamps between service and watcher to show "Service is outdated" or "Watcher is outdated" with the correct action button (rebuild service vs restart watcher). Bake timestamps into Docker images alongside the existing git hash. Also adds: - Stop individual watchers when multiple are connected - Restart Watcher button in dashboard - Stop watchers before workspace deletion to prevent zombies - Auto-start watcher after container start in setup wizard - Per-project scan progress display in setup workspace cards Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hutdown The watcher stop mechanism relied entirely on heartbeat-based signaling (up to 15s delay), which was fragile — especially when the service was down (ECONNREFUSED). Now setup-gui tracks spawned watcher PIDs and kills them directly via SIGTERM, with heartbeat fallback for watchers started outside setup-gui. Also fixes: - stopAndRestartWatcher() finding the wrong status element when called from the watcher card vs version alert banner - Service-unreachable errors during status polling after stop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /api/docker/stop and DELETE /api/workspaces/:name routes now kill the watcher process (via tracked PID) before stopping/removing the container. This prevents zombie watchers that keep reconnecting with ECONNREFUSED loops after the service goes down. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tracked PID map only works for watchers spawned by the current setup-gui session. Watchers that survived a setup-gui restart are orphans with no tracked PID. Now all stop paths (watcher/stop, docker/stop, workspace delete) also scan the OS process list via wmic for node processes matching "watcher-client.js --workspace <name>" and kill them directly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…er termination process.kill(pid, 'SIGTERM') is unreliable for detached Node.js processes on Windows. Replace with taskkill /F /PID which forcefully terminates the process via the Windows API. All kill paths now go through a killPid() helper that uses taskkill on Windows and SIGTERM on Linux/macOS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kills any existing setup-gui and watcher-client Node processes via wmic, then opens the browser and starts a fresh setup-gui. Useful for picking up code changes without manually finding and killing processes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Good feature overall. The directional alerts and per-watcher stop buttons are solid UX improvements. A few issues to address before merging:
1. wmic deprecated on Windows 11 24H2+
start.bat uses wmic to terminate old processes, and killWatcherProcesses() in setup-gui.js uses it for the Windows orphan process scan. wmic was deprecated in Windows 10 and removed in Windows 11 24H2 (released Oct 2024).
The try/catch in killWatcherProcesses degrades gracefully (falls through to PID tracking + heartbeat), but start.bat is silent on failure — old processes simply are not killed. Replace with PowerShell equivalents.
For start.bat, replace the two wmic lines with:
powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $.CommandLine -like 'setup-gui.js' -or $.CommandLine -like 'watcher-client.js' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"
For the Windows branch in killWatcherProcesses, replace the wmic CSV command with a PowerShell equivalent that outputs CommandLine + PID pairs.
2. No tests for new complex logic
The multi-strategy watcher kill (tracked PID -> OS scan -> heartbeat), stopSpecificWatcher() polling, and the timestamp comparison direction logic are all untested. A unit test covering the kill-strategy selection in setup-gui.js would catch regressions without requiring integration setup.
3. loadWorkspaces monkey-patch is fragile (setup.html)
const _origLoadWorkspaces = loadWorkspaces;
loadWorkspaces = async function() {
await _origLoadWorkspaces();
stopWatcherPolling();
startWatcherPolling();
};
This breaks silently if anything else re-assigns loadWorkspaces. Better to call stopWatcherPolling()/startWatcherPolling() at the explicit callsites of loadWorkspaces instead of wrapping it.
4. Dual-context escaping in onclick (minor / low risk)
onclick="stopSpecificWatcher('${esc(id)}', this)"
esc() applies HTML entity escaping but the value is also a JS string literal. A watcherId containing a single quote or backslash produces broken JS — esc() does not apply JS-string escaping. Since watcherId is server-generated this is unlikely to trigger in practice, but using a data attribute + addEventListener avoids the dual-escaping problem entirely.
Summary
BUILD_GIT_TIMESTAMPinto Docker images alongside the existing git hash, with fallback to.git-timestampfileFiles changed
Dockerfile— bake.git-timestampalongside.git-hashsrc/service/api.js— addSERVICE_GIT_TIMESTAMP, include in/healthand/watcher-statusresponsessrc/watcher/watcher-client.js— addwatcherGitTimestampandscanProgresstracking, include in heartbeatsrc/setup-gui.js— passBUILD_GIT_TIMESTAMPbuild-arg, forwardwatcherIdin stop requestspublic/index.html— timestamp comparison UI,overviewRebuildAndRestart(),stopSpecificWatcher(), restart buttonpublic/setup.html— stop watcher on workspace delete, auto-start watcher after container start, watcher progress pollingTest plan
🤖 Generated with Claude Code