feat(agent-email): one-command playground launcher + live hub launch card#1846
Conversation
|
Verdict: Request changes — one small robustness fix, then this is good to merge. This PR turns "try the email agent" into a single command ( The one thing to fix before merge: the "open the browser" helper claims to be non-fatal, but it isn't. When the OS browser-opener isn't installed — which is normal on headless servers, plain containers, and WSL without 🔍 Technical details🟡 Important —
|
|
Thanks — all four addressed in
npm: 46 tests + build clean. Website: 0 errors / 19 tests. |
|
A second deep pass (now that the launcher owns its own lifecycle) found one more release-blocking leak — fixed in Blocker: an orphaned sidecar in the 🔍 Fix + the other items
Verified clean: Merge-order: this conflicts with #1843 in |
…card Trying the agent meant a dance — fetch the binary, spawn the sidecar, find the localhost URL, open it. This makes it one command and surfaces it on the hub. - npm CLI: new `npx @amd-gaia/agent-email playground` — fetchBinary -> startSidecar -> open the browser to /v1/email/playground -> run until Ctrl+C (auto-reaped on exit). Flags: --port (default 8131), --out (binary cache), --no-open. - Hub agent page: a Playground card with the one-liner (copy button) + an 'Open playground' button gated by a live liveness probe of the LOCAL sidecar. A static page can't start a process, so the button activates the moment the sidecar is reachable (no-cors fetch resolves=up/rejects=down; 127.0.0.1 is allowed from the HTTPS hub). Only shown for npm agents that declare a playground_url. - Docs synced (README/SPEC/SKILL/CHANGELOG) per the doc-sync rule. npm 36 tests + build clean; website 0 errors + 19 tests.
- openBrowser: catch the child's async 'error' event. A missing opener
(headless/container/WSL) is reported asynchronously, not as a sync throw, so it
was escaping the try/catch -> uncaughtException -> the sidecar auto-reaper killed
the sidecar instead of harmlessly printing the URL.
- Own the lifecycle: startSidecar({ autoCleanup: false }) so the graceful shutdown
actually runs (the default reaper SIGKILLed the tree first); handle
SIGINT/SIGTERM/SIGHUP with a re-entry guard.
- Tighten --port: reject out-of-range and the reserved 4001 (extracted as
resolvePlaygroundPort) so bad input stays on the friendly exit-2 path instead of
surfacing spawnSidecar's RangeError.
- Guard the CLI entry (run main() only when invoked directly) so cli.ts is
importable; add cli.test.ts (10 tests) for the port validation + cache default.
npm 46 tests + build clean; bin verified (node dist/cli.js help still runs).
…ayground With autoCleanup:false the playground command installs no reaper until its SIGINT/SIGTERM/SIGHUP handlers are wired — so a throw in the window between startSidecar resolving and those handlers (realistically an EPIPE from a stdout write into a closed pipe, e.g. `npx … playground | head`) escaped to main().catch and orphaned the live sidecar on its port. Guard the whole post-start region and shut down on the way out; also .catch the shutdown promise before .finally so a future throwing shutdown can't surface as an unhandledRejection (no net, with autoCleanup off). SPEC.md no longer claims the playground is 'auto-reaped on exit' — it isn't with autoCleanup off; it stops on the three signals or a startup error. npm 46 tests + build clean; bin verified.
0.2.1 hasn't been released yet, so there's no separate [Unreleased] bucket — the one-command playground launcher and automatic sidecar cleanup ship in 0.2.1. Moved both Added bullets under 0.2.1 and updated its intro (it's no longer a docs-only release).
…ython' The Details card showed Language: Python for the email agent, which is misleading — it's written in Python but distributed as a self-contained PyInstaller binary with no Python runtime on the host. Add a one-line qualifier under the language for npm-distributed agents so installers know what they're getting.
8aae153 to
93847eb
Compare
|
@claude review this PR. |
Deep doc review (README/SPEC/SKILL/CHANGELOG cross-checked against the code) came back clean. Two small fixes: the README playground line listed only --port and --no-open while SPEC, CHANGELOG, and cli.ts all document --out; and the 'last updated' header was a day stale.
|
Verdict: Approve — every item from the prior round is genuinely fixed, the doc-sync holds, and the #1843 rebase kept both cards. This is good to merge. The launcher now owns its own lifecycle correctly: a missing browser-opener is non-fatal, bad 🔍 Technical detailsRe-reviewed against the four original findings + the self-reported blocker:
Doc-sync verified consistent: Website: both Couldn't run Minor, non-blocking: the page's |
Why this matters
Trying the email agent meant a multi-step dance — fetch the binary, spawn the sidecar, find the localhost URL, open it. This makes it one command (
npx @amd-gaia/agent-email playground) and surfaces it on the hub agent page with a copy-the-command card whose Open playground button goes live the moment the local sidecar is reachable.A static web page can't start a local process, so this is the meet-in-the-middle: a one-command launcher in the CLI + a smart button on the page that detects when the sidecar is up.
What's in it
playgroundcommand:fetchBinary→startSidecar→ open the default browser to/v1/email/playground→ run until Ctrl+C (auto-reaped on exit). Flags:--port(default 8131),--out(binary cache dir),--no-open.no-corsfetch resolves if it's up, rejects if not;127.0.0.1is a trustworthy origin so the HTTPS hub may probe/link it). Shown only for npm agents that declare aplayground_url(from feat(hub): production agent docs, changelog channel & richer hub rendering #1839).Test plan
cd hub/agents/npm/agent-email && npm run build && npm test(36);node dist/cli.js helplistsplaygroundcd website && npx astro check(0 errors) +npx vitest run(19) + buildnpx @amd-gaia/agent-email playgroundfetches, starts the sidecar, and opens the playground