Skip to content

Run ​

CommandDoes
ahpdRun in this terminal. Ctrl+C stops it
ahpd startRun in the background, waiting up to readyTimeoutMs (60 s) for it to say where it listens
ahpd statusSay if it runs, and where
ahpd doctorSay what is wrong, and how to fix it
ahpd stopStop it, and kill it after stopWaitMs (5 s, as VS Code's CLI)
ahpd restartStop and start again with the same flags, once no turn runs
ahpd restart --forceRestart at once, ending the running turns
ahpd updateMove to a newer ahpd and restart it. See Upgrade

start takes the same flags as ahpd. A bad flag is refused before anything starts.

Files ​

All in ~/.config/ahpd/ (or $XDG_CONFIG_HOME/ahpd/):

FileHolds
config.jsonThe configuration
daemon.logWhat a background daemon prints. Moved aside at 5 MiB
daemon.jsonThe running daemon's pid, URL and flags. Mode 0600
sessions/Each session's settings and owner
automations/One Markdown file per automation
vault.jsonSecrets. See Vault
bash
tail -f ~/.config/ahpd/daemon.log

Each line of the log says when and how much it matters, as VS Code's logs do:

2026-11-09 14:02:11.123 [info] created ahp-session:/1f2e in /home/me/app
2026-11-09 14:02:12.004 [warning] control: refused a request with the wrong token
2026-11-09 14:02:15.310 [error] could not remove /home/me/.config/ahpd/outputs/1f2e: EBUSY: resource busy or locked

The levels are trace, debug, info, warning and error. In a terminal the level is in colour: an error red, a warning yellow, trace and debug grey. daemon.log holds plain text, and NO_COLOR=1 turns the colour off in a terminal too.

logLevel (--log-level) is the lowest level the daemon writes: info by default, debug or trace for more, warn or error for less, and off for none. A client that reads the log as OTLP, on ahp-otlp://logs/{level}, chooses its own level and is not bound by it. logTime (--log-time) is the clock of each line: local by default, or utc.

Restart ​

ahpd restart keeps the sessions, and the port when it is fixed. While a turn runs, the restart waits for the turns to end, as VS Code waits for its turns before it restarts its agent runtime, and says which sessions it waits for. Turns go on meanwhile, and the last one to end starts the restart, which the daemon log says. --force restarts at once, as VS Code's CLI replaces a running agent host:

bash
ahpd restart            # once the running turns end
ahpd restart --force    # at once, ending the running turns

Before it stops, the daemon checks that the new code accepts the recorded flags. If it does not, the restart is refused and the old daemon keeps running. A restart that waited checks again when the turns end.

The daemon goes down 3 seconds after it answers, VS Code's delay before it stops an agent host it is upgrading, so the answer reaches a client behind a tunnel or a proxy before the connection drops.

Environment and folder

The new daemon keeps the old one's environment variables and working folder. To change either, use ahpd stop and ahpd start.

From another machine: ahpd --remote <url> restart. See Remote access.

A client of the daemon's AHP restarts it with the command x-ahpd/restart, which runs the same checks and takes force as the API does. It takes config:write, and only the host's own connection may send it. The answer is { "restarting": true, "pid": <pid>, "restartDelayMs": 3000 }, as VS Code's upgrade answer says how long until the server goes, or { "restarting": false, "pid": <pid>, "parked": [<session>, ...] } while the restart waits for the turns in those sessions.

Doctor ​

ahpd doctor runs every check on this machine and prints one line for each, with the fix when something is wrong. It reads the configuration, the daemon's record, the service file and the plugins, and it starts nothing.

The checks, in this order:

CheckSays
RuntimeThe Node version that runs ahpd, and the other runtimes on PATH
ConfigEach configuration file that is read, and whether it parses
DaemonThe record, then whether the daemon answers on its address within 5 s
ServiceThe service file, on a system with a service manager
PluginsWhether each plugin in the configuration resolves, and has a whole manifest
AgentsThe agent commands in the configuration, against the ones on PATH
UpdateWhether a newer ahpd is on record

ok is a state that needs nothing, warn is a state a person may want to change, and fail is a fault. Every warn and fail names its fix on the next line.

bash
ahpd doctor
ok Runtime: Node v22.19.0 runs ahpd
ok Config: read /home/you/.config/ahpd/config.json
warn Daemon: no daemon is running
  fix: ahpd start
warn Service: no systemd file is at /home/you/.config/systemd/user/ahpd.service
  fix: ahpd service install
ok Plugins: 2 plugins load
ok Agents: every agent command on PATH is configured
ok Update: no newer @ahpd/server is on record
5 ok, 2 warn, 0 fail

ahpd doctor --json gives the same checks as an array, and is the output for a support ticket. It holds no token.

The exit code is 1 when a check is fail, and 0 otherwise, so a script can ask doctor whether the machine is well. With --json, the array stays on stdout and the failure is one JSON line on stderr, as with every command.

Service ​

ahpd service install hands the daemon to the service manager of this machine, so it starts at login and is started again ten seconds after it ends, as VS Code's CLI keeps its own service running. On Linux that is a systemd user unit, and on macOS a LaunchAgent loaded in the graphical login. Neither needs sudo: both files belong to this user. ahpd stop and ahpd restart go through the manager, so a stop stays stopped.

bash
ahpd service install
ahpd status
ahpd on ws://127.0.0.1:8787 (pid 48213), started 2026-10-16T09:12:04.881Z, under systemd

What it writes is one file that runs the same daemon as ahpd with no command, for the configuration file of this install. A service starts in your home folder, so a run with no paths serves the home folder, the way a terminal opened there would.

PlatformFile
Linux~/.config/systemd/user/ahpd.service
macOS~/Library/LaunchAgents/dev.ahpd.daemon.plist

While the service holds the daemon, ahpd stop and ahpd restart go through the manager rather than sending a signal: a daemon a signal ends is one the manager starts again. The log is where the manager puts it: the journal on Linux, and daemon.log on macOS.

bash
journalctl --user -u ahpd -f        # Linux
tail -f ~/.config/ahpd/daemon.log   # macOS

Environment

The file gets the PATH of the shell that ran service install, so the agents this host starts find their own runtimes. "service": { "path": "manager" } writes none, and the daemon runs on the manager's own PATH, as VS Code's CLI writes its unit. The daemon's own interpreter is written in full, so a node from nvm needs no PATH of its own.

A configuration home of its own is an XDG_CONFIG_HOME that is not ~/.config. An install from one writes that home into the file too, so the daemon the manager starts keeps to the folder under it. Which of the daemon's environment an agent's shells get is shellEnv's to say.

After an upgrade, run ahpd service install again: it writes the file for the new binary and starts the daemon from it. ahpd service uninstall stops the daemon, takes the service out of the list this machine starts at login, and removes the file.

service install is refused while a daemon from ahpd start runs - stop that one first.

More than one daemon ​

A second daemon on the same machine has a configuration home of its own, and a port of its own. Name its service in its configuration file, $XDG_CONFIG_HOME/ahpd/config.json, so it installs beside the first one:

json
{ "port": 9188, "service": { "name": "two" } }
bash
XDG_CONFIG_HOME=~/.config/ahpd-two ahpd service install
PlatformServiceFile
Linuxahpd@two$XDG_CONFIG_HOME/systemd/user/ahpd@two.service, linked into the folder systemd reads units from
macOSdev.ahpd.daemon.two~/Library/LaunchAgents/dev.ahpd.daemon.two.plist

Run every command for that daemon with its XDG_CONFIG_HOME. Its record names its service, so ahpd stop, ahpd restart and ahpd service uninstall reach that service and not the first one. service install is refused for a daemon that a service of another name already holds.

Served folders ​

The daemon serves the folders in paths. With none, it serves the folder it started in.

bash
ahpd paths list
ahpd paths add /work/web
ahpd paths remove /work/web

A running daemon with the HTTP API on takes the change at once. Otherwise, run ahpd restart.

Started at a terminal in a folder it does not serve, ahpd asks:

Serve /work/new? [y/N]:

Yes adds it to paths. To never ask, pass --no-cwd. It serves only paths and --path.

TIP

The first path is where a session is created when a client names no folder. It is not a fence: a client may still open a session in any folder on the machine. The connection token decides who may connect.

Output for scripts ​

Every command takes these:

FlagDoes
--jsonPrint the answer as JSON on stdout
--quietPrint only the id the answer names
--verboseAdd diagnostics on stderr
--no-colorNo colour. Also off for a non-terminal or with NO_COLOR
--yesRemove without asking. A script with no terminal needs it
bash
ahpd status --json | jq .url
ahpd plugin list --json | jq '.[].state'

Under --json, a failure is one JSON line on stderr, so stdout holds only the answer. ahpd --json plugin writes:

json
{"message":"plugin takes list, update, config, install, remove, enable or disable.","kind":"argument","exitCode":2}

kind names the sort of failure, and exitCode is the code the process leaves, for a script that never reads stderr:

kindExit codeMeans
argument2The words typed are wrong.
configuration3The program is not configured, or is configured wrongly.
authorization3The request was refused, and missing names the scopes that would allow it.
unavailable4Something the command needed did not answer.
conflict1The machine's state does not allow it, such as a daemon that is not running.
internal1An unexpected failure.

Without --json, each failure stays the one ahpd: <sentence> line on stderr.

Shell completion ​

bash
ahpd completion bash > /etc/bash_completion.d/ahpd
bash
ahpd completion zsh > "${fpath[1]}/_ahpd"
bash
ahpd completion fish > ~/.config/fish/completions/ahpd.fish