Skip to content

feat: add daemon mode with web UI for persistent background sessions#2275

Open
wenshao wants to merge 4 commits intoQwenLM:mainfrom
wenshao:feat/daemon-mode
Open

feat: add daemon mode with web UI for persistent background sessions#2275
wenshao wants to merge 4 commits intoQwenLM:mainfrom
wenshao:feat/daemon-mode

Conversation

@wenshao
Copy link
Collaborator

@wenshao wenshao commented Mar 11, 2026

Implements daemon mode allowing Qwen Code to run persistently in the background with a web interface for interaction:

  • CLI commands: qwen daemon start/stop/status/sessions
  • HTTP server with WebSocket for real-time streaming
  • Self-contained web UI with dark theme chat interface
  • Session management with unique URLs and auth tokens
  • Background process via fork() with IPC for startup coordination
  • Lock file (~/.qwen/daemon/daemon.lock) for process discovery
  • Foreground mode (--foreground) for debugging
  • Auto-reconnect WebSocket with connection status indicator

Closes #2271

TLDR

Dive Deeper

Reviewer Test Plan

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Implements daemon mode allowing Qwen Code to run persistently in the
background with a web interface for interaction:

- CLI commands: qwen daemon start/stop/status/sessions
- HTTP server with WebSocket for real-time streaming
- Self-contained web UI with dark theme chat interface
- Session management with unique URLs and auth tokens
- Background process via fork() with IPC for startup coordination
- Lock file (~/.qwen/daemon/daemon.lock) for process discovery
- Foreground mode (--foreground) for debugging
- Auto-reconnect WebSocket with connection status indicator

Closes QwenLM#2271

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wenshao and others added 3 commits March 11, 2026 12:04
…ctness

- Add mutex to serialize session execution, preventing stdout/stderr cross-contamination
- Enforce POST method on /api/stop endpoint (return 405 for other methods)
- Decouple process.exit from DaemonServer via onStop callback
- Return 404 for non-existent sessions instead of auto-creating them
- Fix stop command race condition by polling until process actually exits
- Use passed sessionId instead of generating a new randomUUID
- Log startup errors to stderr before exit in daemon-entry
- Add single-quote escaping to escapeHtml
- Add server.test.ts (11 tests) and session-runner.test.ts (6 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add constant-time token comparison via crypto.timingSafeEqual (QwenLM#6)
- Validate lock file fields before trusting parsed JSON (QwenLM#4)
- Verify daemon identity via /health API before sending SIGTERM (QwenLM#5)
- Add session idle timeout (30min) to auto-cleanup unused sessions (#1)
- Reject concurrent prompts on same session instead of overwriting (QwenLM#8)
- Add max session limit (50) to prevent resource exhaustion (QwenLM#7)
- Use server.closeAllConnections() for prompt stop() resolution (QwenLM#15)
- Register onStop callback in foreground mode (QwenLM#10)
- Fix unhandled promise in onStop callback with void (#3)
- Respect encoding parameter in captureWrite (QwenLM#14)
- Remove unnecessary env spread in fork options (QwenLM#9)
- Add tests for lock file validation and session page serving

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The package-lock.json was out of sync after adding ws to packages/cli,
causing npm ci to fail on Node.js 24.x with "Missing: @google/gemini-cli-test-utils".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Daemon Mode

1 participant