Skip to content

[CLI]: Add owner lease or idle timeout for detached browser daemons #42428

Description

What's going on?

playwright-cli intentionally starts cliDaemon.js detached so a browser session survives individual CLI commands. That is useful, but there is currently no owner lease or idle policy. If the daemon's control socket directory disappears, the daemon and its Chromium tree remain alive indefinitely while every normal CLI lifecycle command reports that the browser is not open.

This is different from #41664. That issue was about MCP stdio disposal; its concrete long-lived process was later identified as cliDaemon.js, and the maintainer response correctly noted that a CLI daemon is expected to remain open until the session is closed. The gap here is that after control-socket loss, the session can no longer be closed through the CLI at all.

Standalone reproduction

Run this in a disposable environment with a uniquely named session:

SOCKET_ROOT="$(mktemp -d)"
PWTEST_SOCKETS_DIR="$SOCKET_ROOT" \
  playwright-cli -s=owner-lease-repro open about:blank

# Simulate an ephemeral owner/temp directory disappearing.
mv "$SOCKET_ROOT" "$SOCKET_ROOT.removed"

PWTEST_SOCKETS_DIR="$SOCKET_ROOT" \
  playwright-cli -s=owner-lease-repro snapshot
# Browser 'owner-lease-repro' is not open / run open first

# The detached daemon is still alive and still owns its browser tree.
ps auxww | grep '[c]liDaemon.js owner-lease-repro'

The .session metadata still names the old socket path, but Session._connect() cannot reach it. The daemon has no idle timeout or owner-liveness signal, so it cannot self-terminate. playwright-cli -s=owner-lease-repro close follows the same dead socket and cannot reclaim it.

Observed impact

On one long-running macOS automation host I measured:

  • 19 cliDaemon processes reparented to PID 1
  • oldest daemon: 4 days 8 hours
  • only 5 still reachable through the CLI registry/socket path
  • 14 had a TMPDIR that no longer existed
  • ~14.8 GB resident memory across daemon/Chromium trees
  • approximately 10.7 GB belonged to daemons no live automation session claimed
  • median ~0.81 GB per tree

Requested lifecycle option

Could the CLI daemon support an opt-in owner lifecycle, for example one of:

  1. --idle-timeout=<duration> / an equivalent environment or config option;
  2. an owner lease/heartbeat channel — when the lease disappears or expires, the daemon stops itself;
  3. a parent-death/owner-watch mode that is cross-platform and does not require a consumer to rediscover and kill the daemon by numeric PID later.

Desired safety semantics:

  • a CLI-launched browser/context is closed when the opted-in owner lease expires;
  • an attached operator browser is disconnected/detached, not closed;
  • explicitly persistent sessions can opt out or use a different timeout;
  • ordinary default behavior can remain unchanged for compatibility.

Why downstream cleanup is insufficient

A consumer can keep PWTEST_SOCKETS_DIR outside ephemeral scratch, which preserves manual close reachability and is a useful mitigation. It cannot safely implement complete crash-orphan reclamation after the fact on all platforms:

  • same-UID filesystem ownership records can be rewritten by the automation process;
  • multiple managers can share one host/data home;
  • numeric PID/process-group identities can be reused;
  • Linux has pidfds, but macOS has no equivalent identity handle that remains pinned through an external group signal.

A daemon-owned lease or timeout avoids these cross-process authority problems entirely.

Downstream context

Contribution intent

I intend to work on this if maintainers agree that an owner lease or idle policy belongs in Playwright CLI and approve/assign it for community contribution. I will wait for maintainer triage before opening a PR. Once approved, I can add the cross-platform regression tests and implement the agreed lifecycle API.

Version

  • @playwright/cli 0.1.18
  • Node.js 24.19.0
  • macOS arm64

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions