Aetherfy agent commands
Agents are the default noun in the Aetherfy CLI
A bare verb is an agent verb. afy list lists your Aetherfy agents, afy status <name> describes one, and afy stop <name> pauses one. Only the other kinds of
object keep a group of their own: afy secrets, afy workspaces, afy github,
and the vector groups afy collections, afy index and afy points.
Each of these commands is spelled exactly one way. There is no afy agents
group and no alias for one.
| Subcommand | Purpose |
|---|---|
afy list | List every agent in the account |
afy create <name> | Create an agent record |
afy delete <name> | Delete an agent permanently |
afy stop <name> | Pause an agent |
afy start <name> | Resume a paused agent |
afy archive <name> | Free the plan quota slot, keeping configuration and code |
afy restore <name> | Re-provision an archived agent |
afy cancel <name> | Cancel a pending deployment |
afy status <name> | Show detailed agent status |
afy rename <current> <new> | Rename an agent |
afy update <name> | Change workspace assignment or description |
afy pull <name> | Export the agent’s configuration as aetherfy.yaml |
afy diff | Compare local aetherfy.yaml against deployed state |
afy run <name> | Run an agent once, immediately |
afy runs <name> | Show run history |
afy schedule pause <name> | Pause the agent’s scheduled task |
afy schedule resume <name> | Resume the agent’s scheduled task |
Two things do not exist in the Aetherfy CLI and are commonly assumed:
- There is no
afy schedule set. A schedule is declared with theschedule:key inaetherfy.yamland applied byafy deploy. See /agents/scheduled-tasks. - There is no
afy pauseorafy resume. The pause and resume verbs for an agent areafy stopandafy start.
Listing Aetherfy agents
afy list prints every agent in the account. It takes no arguments and
no flags of its own.
afy list
afy list -o jsonThe default columns are:
| Column | Meaning |
|---|---|
| Name | The agent name |
| Type | SERVICE or JOB |
| Status | Current lifecycle status |
| Regions | The regions the agent is deployed in |
| ID | The Aetherfy agent ID |
When at least one agent in the account has a scheduled task, the table grows to eight columns: Name, Type, Status, Regions, Schedule, Next Run, Last Run, ID.
| Column | Rendering |
|---|---|
| Schedule | The declared schedule expression |
| Next Run | A UTC timestamp, or (paused) when the schedule is paused |
| Last Run | The most recent run’s outcome and how long ago it started, such as failed, 2m ago, or never. When the schedule’s last tick did not start a run, a second line says so: tick skipped (5m ago) or tick missed (5m ago). A tick that fired adds nothing — its run is the first line. The reason a run failed is printed by afy status |
A status of usage_paused renders as paused (usage limit) — the agent was
paused by the Aetherfy usage meter, not by afy stop.
Creating an Aetherfy agent
afy create <name> creates the agent record. It takes exactly one
positional argument, the name.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--description | -d | string | empty | Agent description |
--type | -t | string | SERVICE | Agent type: SERVICE or JOB |
--runtime | -r | string | python3.11 | Runtime: python3.11, python3.12, python3.13, node20, node22, node20-ts, node22-ts, bun, dockerfile |
--spawn-enabled | bool | false | Enable spawning for this agent |
--type accepts only SERVICE or JOB; anything else is rejected.
afy create scraper \
--type JOB \
--runtime python3.12 \
--description "Nightly catalogue scrape"afy create has no --workspace flag. To put a new Aetherfy agent in
a workspace, either run afy update <name> --workspace <ws> afterwards,
or declare the workspace: key in aetherfy.yaml before deploying.
This is not the only way to create an agent. afy deploy will also create the
agent it targets — from the type and runtime in aetherfy.yaml, and only
with explicit consent — so a first deploy need not be preceded by this command.
See /cli/deploy.
Deleting an Aetherfy agent
afy delete <name> permanently deletes the agent.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--force | -f | bool | false | Skip confirmation prompt |
Without --force the Aetherfy CLI asks for confirmation and requires you to type
the agent name exactly before it proceeds.
afy delete scraper
afy delete scraper --forceIf your goal is to free a plan quota slot rather than lose the agent, use
afy archive instead — it preserves the configuration and the stored code
bundle.
Pausing and resuming an Aetherfy agent with stop and start
afy stop <name> pauses an Aetherfy agent. It stops every machine and
prevents the proxy from re-waking the agent on incoming traffic. The pause is
reversible.
afy start <name> resumes an agent that was paused with stop.
afy stop api-gateway
afy start api-gatewayafy start returns once every machine has booted. For a service agent it also
waits, up to a bound, for your code to answer its health check, and says what it
found:
readiness | What afy start tells you |
|---|---|
serving | The agent resumed and is serving requests |
starting | The agent resumed; your code is still starting, and requests sent now are held until it is listening |
load_failed | The agent resumed, but your code failed to load, so its requests will fail — afy logs <name> has the error |
unconfirmed | The agent resumed and its machines started, but your code did not answer in the time allowed — afy logs <name> shows where it is |
slow_start | The agent resumed, but starting its machines took longer than it should, so your code was not checked — the delay was on Aetherfy’s side |
null | A job agent, which serves no requests: it says only that the agent resumed |
The agent is resumed in every case, and the command exits 0 — these describe your code, not the resume. The same field is in the API response, described in Stopping and starting.
Neither command takes flags. A stopped Aetherfy agent still bills at the base rate — stopping suspends execution, not billing. To stop billing for an agent you are not using, archive it.
Archiving and restoring an Aetherfy agent
afy archive <name> destroys the underlying application to free the plan
quota slot while preserving the agent’s configuration and its stored code bundle.
It is reversible.
afy restore <name> re-provisions an archived Aetherfy agent from the
preserved bundle.
afy archive old-worker
afy restore old-workerRestoring consumes a plan quota slot, and the quota is re-checked at restore time. If you are already at your plan limit the restore is rejected until you free a slot or upgrade. Plan quotas are documented at /platform/limits.
Cancelling a pending Aetherfy deployment
afy cancel <name> cancels a pending deployment for the agent. It takes
no flags.
afy cancel scraperOnly deployments in the QUEUED state are cancellable. A deployment whose build is
already in flight returns HTTP 409 from the Aetherfy API and cannot be cancelled;
wait for it to finish, then roll back with afy rollback if the result is wrong.
Inspecting an Aetherfy agent with status
afy status <name> prints the detailed state of a single Aetherfy agent.
It takes no flags of its own and honours -o json.
afy status scraper
afy status scraper -o jsonThe text output covers:
| Field | Notes |
|---|---|
| ID | The Aetherfy agent ID |
| Name | The agent name |
| Type | SERVICE or JOB |
| Status | Current lifecycle status, with health or degraded detail when present |
| Regions | The regions the agent is deployed in |
| Spawn Enabled | Whether this agent may start child agents |
| Workspace | The workspace the agent belongs to, if any |
| Schedule | The schedule expression, labelled UTC |
| Next run | Next scheduled fire time, or (paused) |
| Last tick | What the schedule last did when it did not start a run — skipped or missed — with the reason Aetherfy recorded. Absent when the last tick fired |
| Last run | The most recent scheduled or manual run: its outcome, the reason when it failed, and how long ago it started, as in “failed (run exited with code 1), 2m ago”. never for a scheduled agent that has not run |
| Created | Creation timestamp |
| Updated | Last-modified timestamp |
| Description | The agent description |
| Spawn relationships | Parent and child agents |
| Repo, Branch, Directory, Webhook id | The agent’s GitHub link, when it has one |
The GitHub link in status
An agent linked to a GitHub repository with
afy github link also gets its link printed: the repository, the
branch that is watched, the directory inside it — repository root when the
link has none — and the webhook id. An agent with no link prints none of this.
Two states leave that link intact and stop deploying anything, and afy status
is where the terminal can learn about either:
| What status says | What happened | What fixes it |
|---|---|---|
| GitHub disconnected — pushes are not deploying | The Aetherfy account is no longer connected to GitHub | afy github connect. The link is kept, so deploys resume on reconnect |
| Branch deleted — pushes are not deploying | The branch the link watches was deleted, and status names which and when | Recreate the branch and push. Relinking would succeed and change nothing |
Neither state is reported on GitHub. Aetherfy announces a skipped push as a commit status, which needs the installation token a disconnect removes, and a deleted branch has no commit to attach one to at all.
A tick that fired has started a run, and says nothing about how that run went:
the Last run line does. Read afy runs <name> for the runs before it.
afy status <name> -o json carries the last run under a last_run key — id,
trigger_source, state, created_at, error_message and release_version —
and the link state under a github key, with
the API’s own field names — linked, repo, branch, root_dir,
webhook_id, account_connected and branch_deleted_at. The link is part of
the agent record afy status reads rather than a separate request, so a status
that printed has no second read that could have failed.
Renaming an Aetherfy agent
afy rename <current-name> <new-name> changes the agent’s name. It takes
exactly two positional arguments, which must differ.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--force | -f | bool | false | Skip confirmation prompt |
afy rename scraper catalogue-scraper --forceAn Aetherfy agent’s URL is fixed when the agent is FIRST DEPLOYED, and a rename does not move it. Existing integrations, links, and webhooks keep working across a rename; only the name changes.
The practical rule: rename before your first deploy and the URL follows the new name, rename after and it stays as it was.
The rename also updates your local aetherfy.yaml. afy deploy finds its
target through the name: key in that file, so a rename that stopped at the
Aetherfy server would leave the file naming an agent that no longer exists — and
the next deploy from that directory would report the agent as not found, then
offer to create one under the old name.
afy rename closes that gap and tells you which branch it took:
| What is in the current directory | What happens |
|---|---|
An aetherfy.yaml whose name: is the OLD name | That one key is rewritten. Comments, key order, quoting and line endings are left alone |
An aetherfy.yaml naming a different agent | Nothing is touched. That file belongs to another project, and rewriting it would retarget its deploys |
No aetherfy.yaml, or one with no name: key | Nothing is touched |
In the last two cases the command names the file you still have to update. Set
name: to the new name in whichever aetherfy.yaml you deploy this agent from,
or that deploy will not find it.
The dashboard has no rename button, and that is the reason: a browser cannot reach the file on your machine, so it cannot finish the job.
Updating an Aetherfy agent’s workspace or description
afy update <name> changes the mutable metadata of an Aetherfy agent.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--workspace | string | empty | Assign the agent to this workspace | |
--no-workspace | bool | false | Clear the agent’s workspace (make it workspaceless) | |
--description | -d | string | empty | Set the agent’s description |
Rules enforced by the Aetherfy CLI:
- At least one flag is required; with none, the command errors.
--workspaceand--no-workspaceare mutually exclusive.--workspace ""is rejected. Use--no-workspaceto clear the assignment.
afy update scraper --workspace research
afy update scraper --no-workspace
afy update scraper -d "Nightly catalogue scrape"This is also the supported way to move an existing Aetherfy agent into a
workspace, because afy create has no --workspace flag.
Exporting an Aetherfy agent’s configuration with pull
afy pull <agent-name> exports the agent’s current configuration in
aetherfy.yaml form. By default it writes to stdout so it can be redirected.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--output | -o | string | empty | Write the YAML to this file instead of stdout |
Warning: on afy pull alone, -o means an output file path, not
the global Aetherfy output format. afy pull scraper -o json writes a
file named json. This subcommand shadows the global --output flag.
# To stdout
afy pull scraper
# Redirect
afy pull scraper > aetherfy.yaml
# Or write directly
afy pull scraper -o aetherfy.yamlThe emitted YAML is the declarative subset only — fields the Aetherfy server derives are excluded. Re-deploying the pulled file is therefore a no-op against the agent it came from.
Diffing local aetherfy.yaml against deployed Aetherfy state
afy diff compares the local aetherfy.yaml against the agent’s current
state on Aetherfy and prints what a deploy would change under merge-patch
semantics. It takes no positional arguments — the agent is identified from
the manifest.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--path | -p | string | . | Project directory containing aetherfy.yaml |
afy diff
afy diff --path ./services/scraperThe output legend:
| Marker | Meaning |
|---|---|
~ field: old → new | The deploy would change this field |
+ field: value | The deploy would set this field |
- field | The deploy would clear this field |
= field: value | No-op — local and deployed values match |
field: value | Preserved — not present locally, left untouched |
A runtime change is annotated (immutable — deploy will reject), because the
Aetherfy runtime cannot be changed on an existing agent.
afy diff exits non-zero when there are changes, which makes it a
drift gate in CI:
afy diff --path ./services/scraper
if [ $? -ne 0 ]; then
echo "Deployed Aetherfy state has drifted from aetherfy.yaml"
exit 1
fiRunning an Aetherfy task agent on demand
afy run <name> runs an agent once, immediately, outside its schedule. A
type: job agent runs its entrypoint; a type: service agent is sent one
request to its POST /aetherfy/run route — see
/agents/task-contract.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--payload | -p | string | empty | JSON payload to pass to the run |
--payload-file | -f | string | empty | Read the JSON payload from a file |
--wait | bool | false | Wait for the run to finish (exit 1 if it fails) |
--payload and --payload-file are mutually exclusive, and the payload must be
a JSON object.
# Fire and forget
afy run scraper
# With an inline payload
afy run scraper --payload '{"since":"2026-08-01","full":true}'
# With a payload file
afy run scraper --payload-file ./payload.json
# Block until the run finishes; non-zero exit if it fails
afy run scraper --waitWithout --wait, the Aetherfy CLI prints the Run ID and suggests
afy logs <name> --run <id>. With --wait, it polls for up to 30 minutes; a
failed run exits 1, and on timeout the run keeps going on Aetherfy — only the
waiting stops.
Error hints the Aetherfy CLI prints:
| Error code | Meaning and fix |
|---|---|
AGENT_NOT_DEPLOYED | The agent exists but has no deployment. Deploy it first: afy deploy. |
Listing Aetherfy run history
afy runs <name> shows the run history for an agent, newest first.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
--limit | int | 20 | Maximum number of runs to show (max 100) |
afy runs scraper
afy runs scraper --limit 100
afy runs scraper -o jsonColumns:
| Column | Meaning |
|---|---|
| When | When the run was started, in UTC and labelled so — the same instant -o json gives as created_at |
| Trigger | What started it — cron for a scheduled fire, or a manual trigger |
| State | Run outcome |
| Release | The release the run executed, as afy deployments numbers it (v3). A dash when none was recorded: a service run that was never dispatched, or a run older than the field |
| Duration | Wall-clock run time |
| Run ID | Pass to afy logs <name> --run <id> |
Only scheduled and manual runs appear here. Runs started by a parent agent
through afy spawn belong to the parent’s history and are deliberately excluded
from the child’s list — see /cli/spawn.
Pausing and resuming an Aetherfy scheduled task
afy schedule pause <name> pauses the agent’s scheduled task. No
scheduled runs fire until it is resumed. Manual runs via afy run are
unaffected. The command is idempotent — pausing an already-paused schedule
succeeds.
afy schedule resume <name> resumes a paused scheduled task, and is
likewise idempotent.
afy schedule pause scraper
afy schedule resume scraperBoth subcommands require the agent to have a schedule. Against one that does
not, Aetherfy answers 422 AGENT_SCHEDULE_NOT_SET and the CLI prints the fix:
add a schedule: key to aetherfy.yaml and deploy.
On resume the next run time is recomputed from now. Occurrences that elapsed while the schedule was paused are skipped, never backfilled — Aetherfy does not replay a missed window.
Both subcommands print whether anything actually changed, the resulting paused
flag, and the next run time (or (paused)). Both honour -o json.
Neither command edits aetherfy.yaml. The schedule expression itself is
declared there and applied by afy deploy; see
/agents/scheduled-tasks and
/agents/aetherfy-yaml.