Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [v0.5.5] - 2026-09-25

### Fixed

- **`make serve` where `ps` may not run**: in a sandbox that refuses `ps`, as Codex's `workspace-write` sandbox does on macOS, the web app template's `make serve` now refuses with `refused: no-ps` before starting anything, where it used to start the server, stop it again and report `failed: exited` as if it had exited at once.
- **`make stop` keeps the record of a server it cannot identify**: while the recorded server's first process runs where `ps` may not, or its group runs where `lsof` sees none of its processes, `make stop` now refuses and keeps `.serve/state.json`, where it used to take the server for another process or for one that had ended, drop its record and report `not-running` while it still listened. The record of a server that has ended is still cleared.
- **`failed: still-running`**: a server that `make serve` or `make stop` meant to stop and could not, because the system refused the signal or it outlived `SIGKILL`, is now reported as `failed: still-running` with its record kept, where it used to be reported as stopped and its record dropped.
- **`make all` where `ps` may not run**: a project's tests now skip the cases that start a server where `ps` may not run, so its `make all` passes in such a sandbox instead of failing.

## [v0.5.4] - 2026-09-25

### Changed
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.4
0.5.5
2 changes: 1 addition & 1 deletion initializers/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipelex/create-method-app",
"version": "0.5.4",
"version": "0.5.5",
"description": "Start a Pipelex method app: npm create @pipelex/method-app@latest my-app -- --method <bundle | mt_… | address>",
"keywords": [
"pipelex",
Expand Down
2 changes: 1 addition & 1 deletion webapp-js/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ Other targets that matter:
- **The servers listen on loopback by default, and that default is a security property — never drop the `-H`.** The Server Actions run methods with the `PIPELEX_API_KEY` in the server's environment and nothing authenticates the browser calling them, so a server anyone on the network can reach spends the developer's key for them. `next dev` and `next start` given no host bind every interface, which is why both scripts pass `-H ${APP_HOST:-127.0.0.1}`. The host is declared once, as `APP_HOST` in the `Makefile`, beside `APP_PORT` and exported with it; `make dev APP_HOST=0.0.0.0` widens it for a container or another device, and `make run` and `make start` print a warning whenever the host is not loopback. Unlike a gesture's variables, the two are taken from the shell too, so a container can set them in its environment. `scripts/lib/makefile.test.mts` pins the default, the scripts' expansion and the warning.
- **The dev server runs on port 4300, and it must not go back to 4100.** The number is declared once, as `APP_PORT` in the `Makefile`, which exports it; `package.json`'s `dev`/`start` scripts and `playwright.config.ts` each read it and each default to 4300 on their own, so `npm run dev` outside make still works. Override it per invocation — `make run APP_PORT=4301` — which is what lets a second checkout run beside one that already holds the port. The variables are deliberately not the ambient `HOST`, `HOSTNAME` or `PORT`: the shell sets `HOSTNAME` to the machine's name, and hosting platforms, other dev servers and shell profiles export the others, so inheriting one would widen or move this server without saying so. 4100 is avoided because the Pipelex server's local stack publishes its build-chatbot sandbox on `127.0.0.1:4100`. On the loopback default that collision is loud: `next dev` fails with `EADDRINUSE`. With `APP_HOST` widened it is silent: Docker holds IPv4 loopback, so `next dev` still binds the wildcard and prints `Ready`, while Playwright's health check on `127.0.0.1` reaches the container's 404 forever and fails with `Timed out waiting 120000ms from config.webServer`. When e2e times out with the app apparently up, run `lsof -nP -iTCP:4300 -sTCP:LISTEN` before believing anything else.
- **`make run`, `make start` and `make test-e2e` refuse a port held by another checkout, and that guard is worth keeping.** Several checkouts of the same app all want 4300, so the holder is routinely another checkout — which answers on `http://127.0.0.1:4300` and looks entirely right in a browser. The `port-check` target reads the holder's own working directory (`lsof -a -p <pid> -d cwd`) and compares it against `$(CURDIR)`, so the refusal names the directory actually serving the port instead of printing Node's bare `EADDRINUSE`; when the holder is this checkout, it also names the address that server listens on. The e2e targets pass `ALLOW_OWN=1`, which accepts a server started from **this** directory (Playwright's `reuseExistingServer` is meant to reuse it) and still refuses a foreign one.
- **`make serve` is the dev server an agent starts; `make dev` holds the terminal.** `scripts/lib/serve.mts` spawns `npm run dev` detached, in a process group of its own, records the group and its first process's start time in `.serve/state.json` and the server's output in `.serve/server.log` (all of `.serve/` is gitignored), and ends with one verdict line whose first word is stable: `serving` or `already-serving` with the URL and the page's title, `refused: not-loopback | port-held | no-lsof | bad-port | busy`, `failed: not-listening | exited | page <status> | interrupted`, and `stopped` or `not-running` for `make stop`. Read the verdict, not the exit code, and when a refusal comes from make, the last line is make's own `Error` line, so the verdict is the line before it. Its rules, each tested in `scripts/lib/serve.test.mts` against a fake dev server: loopback is checked before the start (the host, and the `dev` script's `-H`) and after it (every listening socket of the group, on any port); the listener is recognised by its process group, so no path is compared for the server serve started; a server a person started here is recognised by its working directory, reported, and never stopped; anything serve started that is not proven, interrupted runs and errors included, is stopped with every process under it; a recorded group is signalled only while its first process, if it still runs, started when the record says and one of its processes runs in this checkout, so a stale record whose id was reused is never signalled; and a serve or a stop holds `.serve/lock` from its first look to its verdict, so a second run waits for the first rather than racing it, while a lock a killed run left behind is refused as `busy`, naming the file, and never broken. It refuses an `lsof` that cannot find its own process, as BusyBox's, which Alpine images ship under the name, cannot, and the cases that start a server are skipped where no usable `lsof` is found, so `make all` stays green in a slim image. Interruptions include SIGHUP, a terminal closing during the start, and an `lsof` or `ps` a signal killed counts as one rather than as an empty answer, so a Ctrl-C landing in one never takes a running server for gone and drops its record. A process's start time is read in UTC, so shells with different time zones agree on it. The port is `APP_PORT` only when the command line or the shell gives it (the Makefile reads its origin), otherwise the first of 4300 to 4309 that no other directory holds. It refuses without `lsof` instead of passing silently as `port-check` does.
- **`make serve` is the dev server an agent starts; `make dev` holds the terminal.** `scripts/lib/serve.mts` spawns `npm run dev` detached, in a process group of its own, records the group and its first process's start time in `.serve/state.json` and the server's output in `.serve/server.log` (all of `.serve/` is gitignored), and ends with one verdict line whose first word is stable: `serving` or `already-serving` with the URL and the page's title, `refused: not-loopback | port-held | no-lsof | no-ps | bad-port | busy`, `failed: not-listening | exited | page <status> | no-ps | interrupted`, `failed: still-running` when a server of its own that it meant to stop still runs, and `stopped` or `not-running` for `make stop`. Read the verdict, not the exit code, and when a refusal comes from make, the last line is make's own `Error` line, so the verdict is the line before it. Its rules, each tested in `scripts/lib/serve.test.mts` against a fake dev server: loopback is checked before the start (the host, and the `dev` script's `-H`) and after it (every listening socket of the group, on any port); the listener is recognised by its process group, so no path is compared for the server serve started; a server a person started here is recognised by its working directory, reported, and never stopped; anything serve started that is not proven, interrupted runs and errors included, is stopped with every process under it, or reported as `failed: still-running` when it outlives the stop, with its record kept if it was recorded; a recorded group is signalled only while its first process, if it still runs, started when the record says and one of its processes runs in this checkout, so a stale record whose id was reused is never signalled; a start time that cannot be read proves neither way, and nor does a running group none of whose processes `lsof` can see, so the group is left alone and its record kept; a record is removed only once its group has ended or is proven another's, never after a stop the system refused, as a sandbox refuses to signal a process started outside it; and a serve or a stop holds `.serve/lock` from its first look to its verdict, so a second run waits for the first rather than racing it, while a lock a killed run left behind is refused as `busy`, naming the file, and never broken. It refuses an `lsof` that cannot find its own process, as BusyBox's, which Alpine images ship under the name, cannot, and the cases that start a server are skipped where no usable `lsof` is found, so `make all` stays green in a slim image. `make serve` refuses as `no-ps`, before anything starts and with any record kept, when it cannot read its own process's start time, which Linux keeps in `/proc` and `ps` reads elsewhere: Codex's `workspace-write` sandbox on macOS will not run `ps`, a setuid program, and a server serve started there could never be told from a process given its id later. `make stop` refuses so only while the recorded group's first process still runs, the one process whose start time is compared. The cases that start a server are skipped there too. Interruptions include SIGHUP, a terminal closing during the start, and an `lsof` or `ps` a signal killed counts as one rather than as an empty answer, so a Ctrl-C landing in one never takes a running server for gone and drops its record. A process's start time is read in UTC, so shells with different time zones agree on it. The port is `APP_PORT` only when the command line or the shell gives it (the Makefile reads its origin), otherwise the first of 4300 to 4309 that no other directory holds. It refuses without `lsof` instead of passing silently as `port-check` does.
- **Husky `prepare` warning**: `npm install` prints `.git can't be found` when this directory is not the root of a git repository — before `git init`, for instance. Harmless — just re-run `npm install` after `git init` to wire `.husky/_/`.
- **Renaming App Router directories**: delete `.next/` before running `make check` — stale type references in `.next/types/` will fail typecheck.
- **`next-env.d.ts` is generated** (gitignored). Next regenerates it on dev/build. Don't edit by hand.
Expand Down
2 changes: 1 addition & 1 deletion webapp-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ A variable already exported in your shell wins over `.env.local`.

Widen the host only on a network you trust, for a container or to open the app on another device: `make dev APP_HOST=0.0.0.0`. The Makefile prints a warning each time a server starts beyond loopback. `npm run dev` and `npm run start` read the same two variables and fall back to the same defaults.

`make serve` runs the dev server in the background and never beyond loopback: it refuses an `APP_HOST` that is not, and it stops a server that turns out to listen anywhere else before a page can compile. It takes `APP_PORT` when you give one, and otherwise the first port from 4300 to 4309 that no other directory holds. It then checks that the listener is the server it started, requests the page, and ends with one line: `serving http://127.0.0.1:4300/ — "<title>"`, or a refusal or a failure naming its cause. The server's log is `.serve/server.log`. Running it again reports the same server as `already-serving`, and a server you started here with `make dev` is reported too and left alone. Two runs in one checkout take turns, the second waiting for the first. `make stop` stops only what `make serve` started. It needs `lsof`, which macOS ships; on Linux, install it from your distribution's packages if `make serve` says it is missing, BusyBox's included.
`make serve` runs the dev server in the background and never beyond loopback: it refuses an `APP_HOST` that is not, and it stops a server that turns out to listen anywhere else before a page can compile. It takes `APP_PORT` when you give one, and otherwise the first port from 4300 to 4309 that no other directory holds. It then checks that the listener is the server it started, requests the page, and ends with one line: `serving http://127.0.0.1:4300/ — "<title>"`, or a refusal or a failure naming its cause. The server's log is `.serve/server.log`. Running it again reports the same server as `already-serving`, and a server you started here with `make dev` is reported too and left alone. Two runs in one checkout take turns, the second waiting for the first. `make stop` stops only what `make serve` started. It needs `lsof`, which macOS ships; on Linux, install it from your distribution's packages if `make serve` says it is missing, BusyBox's included. Outside Linux it also needs to run `ps`, which a sandbox may refuse, as Codex's does on macOS: `make serve` then says `refused: no-ps` before starting anything, and so does `make stop` while the first process of the server it recorded still runs, leaving that server and its record as they were; both work once run outside the sandbox.

## Make targets

Expand Down
4 changes: 2 additions & 2 deletions webapp-js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webapp-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pipelex-method-webapp-js",
"version": "0.5.4",
"version": "0.5.5",
"private": true,
"description": "A Next.js app that runs MTHDS methods through the Pipelex API, with each method's input form and result view generated from its own contract.",
"scripts": {
Expand Down
Loading
Loading