fix(daemon): lifecycle races and port-in-use handling#244
Merged
Conversation
Contributor
|
🎉 PR Validation ✅ PASSED Commit: Checks:
Ready to merge! ✨ 🔗 View workflow run |
vreshch
marked this pull request as ready for review
July 7, 2026 22:09
vreshch
force-pushed
the
fix/daemon-lifecycle-races
branch
from
July 7, 2026 22:18
78e9b60 to
b8ec96e
Compare
vreshch
added a commit
that referenced
this pull request
Jul 7, 2026
## What README overhaul plus a hand-authored architecture diagram, aligned to master after #238-#245. Ready as the npm landing page. Touches only `README.md` and `docs/**` - no `src/`, `package.json`, or workflows. ## Changes - **`docs/architecture.svg`** - hand-written SVG (no render tooling). Neutral background with dark text on light boxes, so it reads in both light and dark GitHub themes and on npm. Shows AI tools -> MCP (stdio `agentage mcp` + daemon HTTP `/mcp` on `127.0.0.1:4243`) -> CLI + local daemon (single writer) -> local vaults (git-per-vault, `@agentage/memory-core`) -> sync out to git remotes and account sync (labeled protocol-neutrally, no internal tech named). - **`README.md`** restructured for a first-time reader: What is this -> Install -> Quickstart (local-only first, then optional `agentage setup`) -> Architecture (diagram) -> Command reference -> Sync -> MCP integration -> Daemon -> Env vars -> Development. - **`docs/architecture.md`** - short walk-through of the diagram. ## Reality-check vs master Built the CLI (`npm run build`) and verified every command and flag against the live `--help` output. Corrected drift from the merged PRs: - `vault add` now defaults to an **account** vault; `--local [path]`, `--git <remote>`, `--path <dir>` are the alternatives. - `vault sync [name]` now covers git and the account channel; progress prints per vault. - Documented the `update` command and `--no-daemon` global flag. - Daemon section notes the loopback-only, token-guarded, cross-origin-rejecting API and port-in-use reporting (#243, #244). - Friendly memory errors + non-zero exits (#240); 64 KB read clamp and secret refusal. - Describes no specific `src/` file paths (a sibling PR is moving `src/sync/` paths). ## Verification - `npm run verify` green: 405 unit tests pass, type-check + lint + format:check + build clean. - Ran the offline quickstart end-to-end against the built `dist/cli.js` (vault add --local, write via stdin, list, search, read `@vault/path`) - all pass. - Rendered `docs/architecture.svg` to PNG and eyeballed legibility. Note: `format:check` only globs `{src,e2e}/**/*.ts`, so it does not lint markdown; README changes are prose-only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes four verified daemon-lifecycle bugs (plus a pid-reuse guard):
daemon-entrygates pid/port cleanup on an ownership flag set only after this process writes them; an EADDRINUSE loser now exits with a distinct code and touches nothing.vaults.jsonedits no longer kill the daemon. ThewatchFilereschedule runs each channel independently under a try/catch (safeReschedule), logging one line and keeping the last-good schedule. Added anuncaughtExceptionlast-resort guard that cleans up only owned state.server.stop()callscloseIdleConnections()and forcescloseAllConnections()after a short unref'd grace timer.daemon-entryexits with a distinct code on EADDRINUSE;spawnDaemonraces the childexitevent against the health wait and returns a typedSpawnOutcome, so a busy port short-circuits instead of burning the full ~4s.daemon startprints "port is in use by another process - set AGENTAGE_DAEMON_PORT to use a different port" and exits 1; memory verbs fall back to the DirectClient quickly.stopDaemonSafelyprobes/healthand only signals when the recorded port confirms our pid, else (no port recorded) falls back with a printed caveat, else refuses rather than kill a recycled pid.Why
Concurrent memory verbs racing autostart could orphan the live daemon; hand-editing config could crash it; SIGTERM could hang; and a stray port holder made every verb silently pay ~5s.
Scope
Server changes are limited to
start()/stop()lifecycle (no request-path / auth changes - sibling PR owns those). Unit tests added/updated for every behavior change.Verified: npm run verify green locally.