Skip to content

feat(cli): add version, update, status, stop and open commands (#1139) - #1154

Merged
agegr merged 3 commits into
mainfrom
feat/cli-commands
Oct 10, 2026
Merged

agegr merged 3 commits into
mainfrom
feat/cli-commands

Conversation

@agegr

@agegr agegr commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Closes #1139 (the subset we want; the other proposed commands are out of scope).

Commands

pi-web version          # also -v, --version
pi-web status           # list running servers
pi-web stop [--port N]
pi-web open [--port N]  # open a running server, start none
pi-web update [--check]

Bare pi-web [options] is unchanged. A command is recognized only as the first argument.

How it works

  • Run records. Once Next.js is ready, the launcher writes <agentDir>/pi-web-run/<launcher pid>.json ({ pid, nextPid, port, hostname, url, version, startedAt }) atomically, and removes it when it exits. The agent dir is resolved as pi resolves it (PI_CODING_AGENT_DIR, else ~/.pi/agent), next to pi-web's other state files; pi-web writes nothing under ~/.pi-web. A failed write only warns. Port 0 gets no record. Keying by pid means two servers on one port with different hostnames keep a record each.
  • What counts as running. A record counts only while one of its processes is still pi-web and its URL answers over HTTP (any status, so a password-protected server counts). After a crash or reboot a recorded pid can belong to an unrelated process, so the pid must still look like ours: the launcher's command line names pi-web, the Next.js server's reads next-server (vX) (/proc on Linux, ps elsewhere; on Windows only the image name, node.exe). When neither tool is available, the URL check alone decides. A record none of whose processes is ours, or with unreadable JSON, is deleted.
  • Orphaned Next.js. If the launcher is killed with SIGKILL, Next.js keeps serving the port. The record's nextPid keeps such a server visible: status lists it as "launcher gone", stop signals it directly, update refuses.
  • stop. On Unix, stop sends SIGTERM to the launcher, which forwards it to Next.js. On Windows it runs taskkill /T /F on the launcher's tree, because process.kill() there would end only the launcher and leave Next.js on the port. It waits up to 10 s for the server to exit. With several servers running and no --port, stop and open list them and exit 1.
  • update.
    • Reads latest from the npm registry (the URL /api/app-update already uses) and compares stable versions (ported from lib/app-update.ts). --check only reports.
    • Refuses while a server is running (next start keeps .next/ open, and on Windows the loaded node-pty binary is locked) and says to run pi-web stop first.
    • Installs only a global npm install: the realpath of <npm root -g>/@agegr/pi-web must match the package dir. From the npx cache it says to run npx @agegr/pi-web@latest; for any other layout (pnpm, bun, a checkout) it prints the manual command.
    • Runs npm through its own npm-cli.js with no shell, as lib/node-cli.ts does.
  • The browser opener moved into bin/browser-opener.js. bin/pi-web.js wraps the start path in startServer(), so read that diff with -w.
  • Help text and all four READMEs are updated.

Checked

  • lib/pi-web-cli-commands.test.mjs plus lib/pi-web-options.test.mjs: 33 pass. Full npm test (2868), tsc --noEmit and eslint pass.
  • Real next start servers in a temp HOME and agent dir (macOS):
    • -v, status, update --check work; records land in <agentDir>/pi-web-run/.
    • Two servers: stop without --port lists both and exits 1; stop --port ends one and frees the port; Ctrl+C removes the record.
    • A stale record pointing at an unrelated sleep, while another server answers on its port: not listed, stop exits 1, the sleep survives.
    • kill -9 of a launcher: status shows its Next.js as "launcher gone", update refuses, stop ends it and frees the port.
    • update with a lower installed version refuses while a server runs, and from a checkout prints the manual command.
  • Not checked: a real npm install -g update, and Windows.
  • Known limit: npm masks UUID-like path segments in npm root -g output, so a global root path containing a UUID is reported as "not a global npm install" (the manual command is printed).

🤖 Generated with Claude Code

agegr and others added 2 commits October 10, 2026 01:08
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A run record outlives a crash or reboot, and its pid may then belong to an
unrelated process: stop would SIGTERM it. A record now counts as running only
while its URL answers over HTTP (any status); a live pid that does not answer
is skipped, and its record kept until the pid dies. On Windows process.kill()
ended the launcher alone and left Next.js serving the port, so stop ends the
launcher's process tree with taskkill there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the agent dir

- A recycled pid passed the old check whenever something answered on the
  record's port, so stop could SIGTERM an unrelated process. A record now
  counts only while its pid still is pi-web: the launcher's command line names
  pi-web, the server's is "next-server (vX)" (/proc on Linux, ps elsewhere,
  the node image name on Windows). When nothing can tell, the URL check
  decides as before.
- A launcher killed with SIGKILL left Next.js serving the port while status
  said nothing ran. The record keeps nextPid; status lists such a server as
  "launcher gone", stop signals it directly and update refuses.
- Records were keyed by port, so a second server on the same port with
  another hostname overwrote the first's. They are keyed by launcher pid now.
- Records move from ~/.pi-web/run to <agentDir>/pi-web-run (default
  ~/.pi/agent/pi-web-run), where pi-web keeps its other state; pi-web wrote
  nothing under ~/.pi-web before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@agegr
agegr merged commit f98c088 into main Oct 10, 2026
2 checks passed
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: CLI subcommands — version, update, doctor, status/stop, serve

1 participant