feat: the "kiqr agent" — a persistent, inspectable background service - #22
Merged
Merged
Conversation
…rvice
The Traefik reverse proxy and the splash fallback page were previously a
standalone stack that was started on `kiqr up` and torn down whenever no
project was running, causing start/stop churn when switching between
projects.
This refactors that shared infrastructure into a single, named, persistent
background service — the "kiqr agent" — managed as the `kiqr-agent` Docker
Compose project. It is kiqr core, persists independently of any project,
and is the home for future shared infra (dashboard, mail catcher, the
collaboration tunnel client).
Changes:
- Rename src/lib/traefik.ts -> src/lib/agent.ts with agent-oriented API:
generateAgentCompose, writeAgentCompose, ensureAgentRunning, stopAgent,
restartAgent, ensureKiqrNetwork, isAgentRunning, and a testable
getAgentStatus(deps) returning {running, containers, port}.
- Container names (kiqr-traefik, kiqr-splash) and the `kiqr` network are
unchanged for backward compatibility; the compose file now carries a
`name: kiqr-agent` project identity.
- New `kiqr agent` command group: start, stop, restart, status, logs.
- Lifecycle change: `kiqr up`/`kiqr restart` call ensureAgentRunning(); the
agent runs with `restart: unless-stopped` and is no longer auto-stopped by
`kiqr down`/`kiqr destroy`. It persists across projects until the user runs
`kiqr agent stop`.
- Migrate/expand tests (tests/lib/agent.test.ts), update README and help.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
The kiqr agent
The Traefik reverse proxy and the splash fallback page used to be a standalone stack: started on
kiqr up, torn down viastopTraefikIfIdle()whenever no project was running. Switching between projects (or runningkiqr downthenkiqr up) churned the proxy up and down.This PR promotes that shared infrastructure into a first-class, named, persistent background service: the "kiqr agent." It is kiqr core, managed explicitly, and runs as a single Docker Compose project (
kiqr-agent) that persists independently of any individual project. It's the natural home for the shared infra we want to grow into next: a dashboard, a mail catcher, and the future collaboration tunnel client.Lifecycle change (the key behavior fix)
The agent is now persistent by default:
kiqr up/kiqr restartcallensureAgentRunning()(idempotent; step label is now "Starting kiqr agent...").restart: unless-stoppedpolicy.kiqr downandkiqr destroyno longer stop the shared infra — they only affect the current project. The previousstopTraefikIfIdle()calls have been removed.kiqr upand stays up across projects until you explicitly runkiqr agent stop.This eliminates the start/stop churn and matches how a background service should behave.
New command group:
kiqr agentkiqr agent start— ensure the agent is running (shows the proxy URL).kiqr agent stop— stop the agent.kiqr agent restart— restart the agent.kiqr agent status— rendergetAgentStatus(): running?, each container, and the port.kiqr agent logs— stream the agent containers'docker compose logs(stdio inherit).Refactor
src/lib/traefik.ts→src/lib/agent.ts, with an agent-oriented API:generateAgentCompose,writeAgentCompose,ensureAgentRunning,stopAgent,restartAgent,ensureKiqrNetwork,isAgentRunning, and a new dependency-injectedgetAgentStatus(deps)returning{running, containers, port}for testability.tests/lib/agent.test.tsand expanded (compose generation incl. thename: kiqr-agentproject identity + container-name compatibility, andgetAgentStatuslogic with a mocked container check).Backward compatibility
Container names (
kiqr-traefik,kiqr-splash) and thekiqrDocker network are deliberately unchanged, so existing setups keep working. Only the compose project gains aname: kiqr-agentidentity.Not in this PR (planned follow-up)
kiqr agent install(systemd/launchd daemon registration) is intentionally deferred — it needs a real host to verify. Persistence via Dockerrestart: unless-stoppedis sufficient here.Rebase note
This branch touches
src/commands/up.tsxandsrc/commands/restart.tsx, which are also edited by the open PR #17. It will need a trivial rebase relative to that PR (just the proxy/agent step in those two files).Verification
All gates pass locally:
npm run typecheck,npm test(117 tests),npm run build,npm run lint.node dist/cli.js agent --help,agent(group help), andagent statusall render correctly. The actualagent startDocker path could not be exercised here (no Docker daemon);agent statuscorrectly reports "stopped" with no daemon.🤖 Generated with Claude Code