Skip to content

Add who/free, launcher attribution, and --mine/--orphaned scopes - #15

Merged
danfry1 merged 3 commits into
mainfrom
feat/who-free-launcher
Oct 9, 2026
Merged

danfry1 merged 3 commits into
mainfrom
feat/who-free-launcher

Conversation

@danfry1

@danfry1 danfry1 commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

Coding agents now start most local dev servers, often in the background and across several concurrent sessions, which makes a running server hard to identify. This change makes marina answer what is this, and who started it, and adds port-first commands for the EADDRINUSE moment.

marina who / marina free

  • who <port>… names whatever holds a port: a dev target (project · tool · pid · uptime · branch · cwd · launcher) or, for anything outside curation, the process name + pid. Exit 1 when free.
  • free <port>… stops the dev target on a port and waits until it is actually released. Idempotent (marina free 3000 && pnpm dev re-runs safely); refuses to touch non-dev holders such as macOS ControlCenter on :5000. Ports only — no substring matching.

Launcher attribution

  • Every target records its launcher: an agent session (claude, codex, opencode, cursor-agent, …), an editor, a terminal, or detached.
  • Resolved from the live parent chain; when a server has been reparented after &/nohup, falls back to an allowlist of agent env markers (CLAUDE_PID, CLAUDE_CODE_SESSION_ID, CLAUDECODE, AI_AGENT). A server whose agent session has ended is flagged orphaned (claude·ended).
  • TUI: VIA column (dropped after BRANCH/UP on narrow panes), launcher detail in the status line and inspect panel (with claude --resume <id>), / filter matches launcher (/claude, /ended).
  • JSON: additive launcher object {kind, name, pid, alive, orphaned, session, cwd}.
  • --mine / --orphaned scopes on ls/kill/restart/url: an agent can run marina kill --mine to stop exactly the servers it started, and marina kill --orphaned cleans up after finished sessions.

Resolution and verbs

  • Labels see through corepack's pnpm-native shim and node-hosted bin scripts (pnpm exec vp dev → vp dev); python -m http.server → http.server.
  • A server whose cwd is $HOME is named after its tool rather than the username.
  • CLI kill polls for exit instead of always sleeping the grace period (free/kill ~1.5s → ~30ms typical).

Privacy

Only the four allowlisted env keys are extracted; no other variable is kept, displayed, or serialized, and session ids are validated as hex/dashes. Claude Code's ~/.claude/sessions/* files are intentionally not read because they contain auth tokens. README privacy section updated accordingly.

Testing

  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test --all-targets: 101 tests pass (17 new), covering the env allowlist, launcher classification without over-matching (codesign ≠ code), chain vs. env precedence, orphan detection, detached vs. unknown supervisors, --mine session matching, port parsing, who/free exit codes against real sockets, label resolution, and TUI rendering of the VIA column / inspect panel / filter.
  • Verified manually on macOS against live servers: agent-launched Vite servers attribute to their exact Claude Code session; a nohup-backgrounded server still attributes via env; a server with a dead CLAUDE_PID shows claude·ended and is stopped by kill --orphaned; --mine excludes servers from other sessions; the TUI renders correctly at 130 columns.
  • Not yet verified on Linux beyond CI.

Session view and orphan notices (second commit)

  • Session view (v): groups rows by the agent session / editor / terminal that started them, across projects — claude·fal-app, claude·ended, tmux, detached. K on a session header stops everything that session started.
  • Orphan notices: when an agent session ends and leaves servers running, a single notice per session — ⚠ claude session in ~/dev/app ended — left 2 servers running (480MB) — in the status line (20s) and as a desktop notification. Disable the latter with [notify] desktop = false. Orphans present at startup get a status-line count only.
  • Fix: agent env markers without CLAUDE_PID are no longer treated as an ended session (no false orphan alarms).

Verified live on macOS: a stand-in agent process with two env-attributed servers grouped under claude·agent-pr… (2); killing the agent moved them to claude·ended (2) and raised the notice; /ended + K on the session header stopped exactly those two servers while other sessions' servers were untouched. 109 tests (8 new: session grouping and disambiguation, session-view kill scope, once-per-session notices, startup orphans, pid-less markers, header truncation, AppleScript escaping, notify config).

Multi-agent detection (third commit)

  • Marker parsing is table-driven and covers Codex (CODEX_THREAD_ID, CODEX_SANDBOX), opencode (OPENCODE_SESSION_ID, OPENCODE), Copilot, Cursor, Gemini, Amp, Goose, Cline, Roo, Qwen, Pi, Augment, Crush and Kiro, with AI_AGENT/AGENT as a fallback. Sources: Codex exec_env.rs, opencode's bash tool (read from the shipped binary), cross-checked against is-ai-agent.
  • --mine and session grouping work for Codex and opencode via their thread/session ids; inspect shows codex resume <id> / opencode --session <id>.
  • Editor-hosted agents (Cursor agent, Copilot agent mode) are attributed to the agent rather than the editor; terminals are deliberately not upgraded.
  • Only Claude Code exports a pid, so only Claude sessions can be proven ended; other agents' servers are never flagged orphaned.
  • Fix: the anchor climb stops at any launcher. Found by running a server through the real codex sandbox command: with no shell in between, the Codex process itself became the target's anchor (row labelled codex, Codex in the kill set).

Verified live: a listener started via codex sandbox reads nc · via codex (pid …); backgrounded servers carrying Codex / opencode markers read via codex (session 0199a213) / via opencode (session ses_3b0f); ls --mine run with each agent's session marker returns only that session's server. 117 tests.

Port commands:
- `marina who <port>` names whatever holds a port: a dev target (with
  project, tool, pid, uptime, branch, cwd, launcher) or, for anything
  outside curation, the process name + pid. Exits 1 when the port is free.
- `marina free <port>` stops the dev target on a port and waits until the
  port is actually released. Idempotent (already free = exit 0) and refuses
  to touch holders that are not dev targets. Both accept ports only.

Launcher attribution (new `launcher` module):
- Each target records who started it: an agent session (claude, codex,
  opencode, cursor-agent, ...), an editor, a terminal, or detached.
- Resolved from the live parent chain, falling back to an allowlist of agent
  env markers (CLAUDE_PID, CLAUDE_CODE_SESSION_ID, CLAUDECODE, AI_AGENT)
  that survive reparenting after `&`/`nohup`. A server whose agent session
  has ended is flagged orphaned. No other env vars are retained.
- TUI: VIA column, launcher details in the status line and inspect panel
  (including `claude --resume <id>`), `/` filter matches launcher.
- JSON: new `launcher` object on every target.
- CLI: `--mine` (targets started by the agent session running marina) and
  `--orphaned` (targets whose agent session ended) on ls/kill/restart/url.

Resolution and verbs:
- Labels see through corepack's pnpm-native shim and node-hosted bin
  scripts (`pnpm exec vp dev` -> `vp dev`); `python -m X` -> `X`.
- A server whose cwd is $HOME is named after its tool, not the username.
- CLI kill returns as soon as processes exit instead of always sleeping
  the full grace period (free/kill: ~1.5s -> ~30ms in the common case).
Session view (`v` in the TUI):
- Groups targets by launcher instead of project: one header per agent
  session (`claude·<dir>`, `claude·ended`), editor, terminal, `detached`, or
  `unattributed`, spanning projects. Distinct sessions that would share a
  label get a short id suffix. Every session gets a header, even with one
  member. `K`/`R` on a session header act on exactly that session's targets.
- Session headers are colored by launcher; the status line and inspect panel
  describe the session (with `claude --resume <id>` where known).
- Long group labels are truncated with `…` so the member count stays visible.

Orphan notices:
- The snapshot diff reports each agent session whose servers go from alive
  to orphaned, once per session: `claude session in ~/dev/app ended — left
  2 servers running (480MB)`, as a 20s status notice and a desktop
  notification (osascript on macOS, notify-send on Linux; AppleScript
  string escaping prevents breaking out of the literal).
- Orphans already present at startup get a status-line count only.
- `[notify] desktop = false` in config disables the desktop notification.

Fix: a server carrying agent env markers without CLAUDE_PID is no longer
reported as orphaned — liveness can't be proven either way, so no alarm.
Launcher attribution previously traced backgrounded servers only for Claude
Code. Agent marker parsing is now table-driven (`MARKERS` in launcher.rs),
covering the variables each agent exports to the commands it runs:

- Codex: CODEX_THREAD_ID (session), CODEX_SANDBOX
- opencode: OPENCODE_SESSION_ID (session), OPENCODE
- Copilot: COPILOT_AGENT_SESSION_ID, COPILOT_AGENT, COPILOT_CLI
- Amp, Cline, Roo, Qwen, Pi: their thread/task/session ids
- Cursor, Gemini, Goose, Augment, Crush, Kiro: presence flags
- AI_AGENT / AGENT as a fallback when no specific marker is present

Sources: Codex exec_env.rs, opencode's bash tool (from the shipped binary),
cross-checked against the is-ai-agent table. Only pids and validated session
ids are kept; flag values are never read. Session ids may now contain
letters and underscores (opencode `ses_…`, Amp `T-…`).

Attribution:
- A live agent in the parent chain picks up its own session id from the
  markers, so `--mine` works for Codex/opencode sessions by thread id.
- An editor whose server carries agent markers is attributed to the
  editor's agent (Cursor agent, Copilot agent mode). Terminals are not
  upgraded, to avoid markers leaked into tmux panes.
- Agents without a pid marker are attributed and grouped but never
  reported as ended.
- Inspect shows `codex resume <id>` / `opencode --session <id>`.
- More agent process names recognized (qwen, auggie, kiro-cli, cline).

Fix: the anchor climb now stops at any launcher. An agent running in the
project directory that spawned a server without an intervening shell was
absorbed as the target's anchor — mislabeling the row and including the
agent process in the kill set.
@danfry1
danfry1 merged commit 973c291 into main Oct 9, 2026
4 checks passed
@danfry1
danfry1 deleted the feat/who-free-launcher branch October 9, 2026 10:13
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