* feat: make relay opt-in for device pairing
* fix(relay): keep pairing state authoritative
* fix(relay): refresh pairing state across transports
* fix(onboard): honor explicit relay opt-out
* fix(relay): surface transport startup failures
* fix(pairing): gate offers for older daemons
* test(cli): rely on real pairing transport coverage
* fix(cli): report live relay without a pid file
* fix(relay): synchronize config during daemon startup
* fix(relay): preserve mutability for ignored overrides
* fix(cli): use reachable daemon for pairing
* fix(app): redirect invalid pair device routes
* fix(pairing): support remote host device pairing
Keep Pair Device host-relative through the existing settings and daemon RPC flow. Route CLI output through an injected adapter so workflow tests do not mutate process globals.
* fix(pairing): validate pairing destinations
Expose the host-relative Pair Device flow on every supported app platform. Reject a reachable daemon whose identity does not match the requested Paseo home before reading or mutating its pairing state.
* fix(pairing): render QR codes across platforms
Generate SVG text without DOM or canvas and render it through the existing cross-platform SVG stack.
* fix(cli): pair from persisted relay state
Allow explicit relay pairing while the daemon is stopped when relay is already enabled in persisted configuration.
* Serve the web client from the daemon
Keep the bundled browser UI opt-in and exclude it from desktop packaging so desktop builds do not ship a duplicate renderer.
* Escape daemon web UI bootstrap hint
* Fix bundled web UI dist path
* refactor: rename allowedHosts to hostnames for DNS rebinding config
The old name confused users who thought it was related to CORS.
"hostnames" communicates that you're declaring the daemon's own
hostnames, not granting access to external parties.
Backward compatible: old config files (daemon.allowedHosts), env var
(PASEO_ALLOWED_HOSTS), CLI flag (--allowed-hosts), and Nix option
(allowedHosts) all still work as deprecated aliases.
* fix(nix): update npmDepsHash to match current package-lock.json
Merge agent-management and agent MCP into a unified server with shared
utilities. Remove the voice-mcp-bridge in favor of direct MCP tool
registration. Add schedule, terminal, and worktree management tools to
the MCP server for CLI parity. Include e2e parity tests.
What changed:
- Added a shared utility `getErrorMessage(error: unknown)` in `packages/cli/src/utils/errors.ts`.
- Refactored `packages/cli/src/commands/daemon/start.ts` to use `getErrorMessage` and a local `exitWithError` helper instead of repeating inline error-message extraction + exit logic in each catch block.
- Added `packages/cli/tests/19-errors-utils.test.ts` to lock utility behavior for Error and non-Error throw values.
Reasoning:
- `runStart` had repeated branching (`err instanceof Error ? err.message : String(err)`) across multiple catch sites.
- Centralizing this keeps behavior stable while reducing duplication and cognitive overhead, aligned with the refactor skill guidance to simplify structure without changing user-visible behavior.
Verification:
- Ran `npx tsx packages/cli/tests/18-local-daemon-utils.test.ts` (pass).
- Ran `npx tsx packages/cli/tests/19-errors-utils.test.ts` (pass).
- Ran `npm run -w @getpaseo/cli typecheck` (pass).
Notes for next agent:
- Existing `scripts/codex-refactor-loop.sh` was already modified before this change and is intentionally not included.
- `packages/cli/tests/03-daemon.test.ts` currently failed in this environment because Test 3 expected daemon status failure, but command exited successfully (likely environment state dependent). This refactor does not touch that path; worth deflakifying or isolating in a follow-up.
- Install zx as devDependency for E2E testing
- Create tests/ directory with setup utilities
- Add setup.ts with test context helpers:
- Random port generation (never 6767)
- Temp directory creation for PASEO_HOME and workDir
- Daemon startup/waitForDaemon helper (WebSocket only)
- Cleanup function
- paseo() helper to run CLI commands against test daemon
- Add Phase 1 foundation tests (01-foundation.test.mts):
- Test paseo --version outputs version
- Test paseo --help shows commands
- Add test runner (run-all.mts) that discovers and runs all tests
- Add test:e2e script to package.json