Skip to content

feat: stop all servers button#2323

Open
janburzinski wants to merge 5 commits into
mainfrom
jan/eng-1309-add-a-stop-all-servers-feature
Open

feat: stop all servers button#2323
janburzinski wants to merge 5 commits into
mainfrom
jan/eng-1309-add-a-stop-all-servers-feature

Conversation

@janburzinski
Copy link
Copy Markdown
Collaborator

Description

  • task-scoped stop all control next to dev server pill in task titlebar
  • tracks dev server entires with scopeid,terminalid,url so each can be killed reliablly
  • stop normal terminal-backend dev servers throuhg ctrl+c,keeping terminal session open
  • stop run lifecycle script server through the lifecycle coordinator

Screenshot/Recording (if applicable)

https://streamable.com/1i4wux

Checklist
  • I kept this PR small and focused
  • I ran a self-review before opening this PR
  • I ran the relevant local checks or explained why not
  • I updated docs when behavior or setup changed
  • I added or updated tests when behavior changed, or explained why not
  • I only added comments where the logic is not obvious
  • I used Conventional Commits for commit
    messages and, when possible, the PR title

@janburzinski janburzinski changed the title fix(terminals): avoid respawn after manual kill fix(terminals): stop all servers button May 31, 2026
@janburzinski janburzinski changed the title fix(terminals): stop all servers button feat: stop all servers button May 31, 2026
@janburzinski
Copy link
Copy Markdown
Collaborator Author

@arnestrickmann would love some design input aswell :'D

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR adds a task-scoped "Stop all servers" control to the dev server pill in the task titlebar, replacing the flat pill list with a Popover that shows individual stop buttons per server and a bulk stop action. It also fixes a pre-existing respawn bug in both LocalTerminalProvider and SshTerminalProvider where state maps were cleaned up after pty.kill(), allowing a synchronous exit callback to see stale session state and trigger an unintended respawn.

  • stopDevServers RPC: new backend handler that sends Ctrl+C for normal terminal servers or delegates to stopLifecycleScriptSession for workspace run scripts; teardown is intentionally optimistic (acknowledged via code comment).
  • dev-server-watcher cleanup registry: module-level activeDevServerCleanups map tracks per-terminal watcher closures so stopDevServers can stop the port probe and deduplicate the exit event in one call; the found flag inside each closure prevents double-emit if the natural exit and the manual stop race.
  • Terminal provider fix: sessions, respawnCounts, shellProfiles, and terminals maps are all cleared before pty.kill(), guarding against the synchronous-exit respawn loop; both local and SSH providers are updated and new tests verify the fix.

Confidence Score: 5/5

Safe to merge — the changes are well-scoped, covered by targeted tests, and the one intentional trade-off (optimistic pill removal on Ctrl+C) is documented in-code.

The stop flow is straightforward: Ctrl+C or lifecycle coordinator, then an optimistic exit event. The cleanup registry correctly deduplicates events via the found closure flag, and the terminal-provider pre-kill cleanup order is verified by the new synchronous-exit respawn tests. No unbounded side-effects or cross-session leakage were identified.

No files require special attention.

Important Files Changed

Filename Overview
src/main/core/terminals/stopDevServers.ts New RPC handler dispatching Ctrl+C for normal terminals or lifecycle coordinator for run scripts; optimistically hides pills via emitDevServerExit with intentional comment about SIGINT-ignoring processes
src/main/core/terminals/dev-server-watcher.ts Adds module-level activeDevServerCleanups map keyed by scopeId:terminalId and exports clearTerminalDevServer; cleanup is registered on URL detection and safely de-dupes exit events via the found flag
src/renderer/features/tasks/components/dev-server-pills.tsx Major UI rework replacing flat pill list with a Popover that has individual Stop buttons per server and a Stop all servers button; uses callback-ref mountedRef pattern to guard the stoppingKey setState after unmount
src/renderer/features/tasks/stores/dev-server-store.ts Map key upgraded from plain terminalId to scopeId:terminalId composite, entry type enriched with scopeId and terminalId fields needed for targeted stop RPC
src/main/core/terminals/impl/local-terminal-provider.ts Bug fix: session maps cleared before pty.kill() to prevent synchronous exit callback from seeing stale state and triggering an unintended respawn; respawnCounts is now also cleaned up
src/main/core/terminals/impl/ssh-terminal-provider.ts Same pre-kill cleanup fix as local-terminal-provider; terminals map and shellProfiles are now cleared before pty.kill() to prevent respawn on synchronous exit
src/main/core/terminals/stopDevServers.test.ts Comprehensive tests covering normal interrupt, watcher cleanup deduplication, lifecycle coordinator path, and PTY kill fallback
src/main/core/terminals/impl/local-terminal-provider.test.ts Adds session tracking to mock and a new test verifying that a synchronous kill exit does not trigger a respawn
src/main/core/terminals/impl/ssh-terminal-provider.test.ts Mirrors the local provider test changes: session array tracking in mock and a new no-respawn-on-kill test
src/main/core/terminals/controller.ts Registers stopDevServers in the RPC controller, minimal one-line change

Sequence Diagram

sequenceDiagram
    participant UI as DevServerPills (Renderer)
    participant RPC as rpc.terminals
    participant SD as stopDevServers.ts
    participant DSW as dev-server-watcher.ts
    participant LC as lifecycle-script-coordinator
    participant PTY as PTY / ptySessionRegistry
    participant Store as DevServerStore

    UI->>RPC: "stopDevServers({ projectId, taskId, workspaceId, servers })"
    RPC->>SD: stopDevServers(...)

    loop for each server
        alt "scopeId === workspaceId && terminalId === run script"
            SD->>LC: stopLifecycleScriptSession(...)
            LC-->>SD: stopped: boolean
            alt not stopped
                SD->>PTY: pty.kill() + unregister
            end
        else normal terminal
            SD->>PTY: pty.write(Ctrl+C)
        end

        SD->>DSW: clearTerminalDevServer(scopeId, terminalId)
        alt watcher cleanup found
            DSW->>DSW: cleanup() - stopProbe, delete map entry
            DSW-->>Store: emit hostPreviewEvent type exit
            DSW-->>SD: true (skip manual emit)
        else no cleanup registered
            SD-->>Store: emit hostPreviewEvent type exit
        end
    end

    Store->>UI: entries updated (pill removed)
    UI->>UI: setStoppingKey(null) if still mounted
Loading

Reviews (2): Last reviewed commit: "feat(terminals): add dev servers menu" | Re-trigger Greptile

Comment thread src/renderer/features/tasks/components/dev-server-pills.tsx Outdated
Comment thread src/renderer/features/tasks/components/dev-server-pills.tsx Outdated
Comment thread src/main/core/terminals/stopDevServers.ts
@janburzinski
Copy link
Copy Markdown
Collaborator Author

update: https://streamable.com/4cpji0

@janburzinski
Copy link
Copy Markdown
Collaborator Author

@greptileai

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.

1 participant