Skip to Content
CLIAgent commands
Raw

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.

SubcommandPurpose
afy listList 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 access <name>Show or change the workspaces the agent’s key can use
afy pull <name>Export the agent’s configuration as aetherfy.yaml
afy diffCompare 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 the schedule: key in aetherfy.yaml and applied by afy deploy. See /agents/scheduled-tasks.
  • There is no afy pause or afy resume. The pause and resume verbs for an agent are afy stop and afy 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 json

The default columns are:

ColumnMeaning
NameThe agent name
TypeSERVICE or JOB
StatusCurrent lifecycle status
RegionsThe regions the agent is deployed in
IDThe 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.

ColumnRendering
ScheduleThe declared schedule expression
Next RunA UTC timestamp, or (paused) when the schedule is paused
Last RunThe 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.

FlagShortTypeDefaultDescription
--description-dstringemptyAgent description
--type-tstringSERVICEAgent type: SERVICE or JOB
--runtime-rstringpython3.11Runtime: python3.11, python3.12, python3.13, node20, node22, node20-ts, node22-ts, bun, dockerfile
--spawn-enabledboolfalseEnable spawning for this agent. Requires --spawn-workers
--spawn-workersstringsnoneAgents this agent may spawn, comma-separated, or '*' (quoted, so the shell does not expand it) for any of your agents

--spawn-enabled without --spawn-workers is refused with AGENT_SPAWN_WORKERS_REQUIRED; why the workers are listed is on the aetherfy.yaml page.

--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.

FlagShortTypeDefaultDescription
--force-fboolfalseSkip 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 --force

If 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-gateway

For a service agent, afy start returns once every machine has booted and waits, up to a bound, for your code to answer its health check, and says what it found. A job agent is resumed without starting a machine — its machines wake for runs — so afy start says only that it resumed:

readinessWhat afy start tells you
servingThe agent resumed and is serving requests
startingThe agent resumed; your code is still starting, and requests sent now are held until it is listening
load_failedThe agent resumed, but your code failed to load, so its requests will fail — afy logs <name> has the error
unconfirmedThe agent resumed and its machines started, but your code was not confirmed serving: it did not answer in the time allowed — afy logs <name> shows where it is — or you archived, deleted or paused the agent while afy start was answering, and its machines were being stopped
slow_startThe agent resumed, but starting its machines took longer than it should, so your code was not checked — the delay was on Aetherfy’s side
nullA 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.

When a machine’s host has no room to resume it, the start is accepted too: afy start says the agent is being started on a new machine, how long that can take at most, and exits 0. With --wait it follows the agent as below, and once the agent is running it says it was a cold start — a new machine from the agent’s current release, not a resume of the old one. The details are in Starting when a machine’s host is full.

Run afy start right after afy stop and the stop may still be finishing — your server gets its shutdown grace. The start is then accepted, not refused: the command says the agent will start once its previous stop finishes, how long that can take at most, and exits 0. Aetherfy starts the agent itself when the stop completes, so there is nothing to re-run, and interrupting the command loses nothing. afy status <name> shows the pending start until the agent is running, or why it was dropped. With --wait, afy start follows the agent until it is running — for as long as the server says the start can take — and exits 1 with the reason if the start was dropped (an afy stop, an archive or a delete, a billing hold, or a stop or start that failed), so a script can go on to send the agent requests. If that time passes with the start still pending, it says Aetherfy is still retrying and that afy status <name> shows the result, and exits 4: the start has not failed, and Aetherfy carries it out on its own. The details are in Starting while the previous stop is still finishing.

Waiting for an Aetherfy agent to start with —wait

--wait matters only when the start is accepted rather than carried out at once; a start that boots the agent straight away answers as in the table above, with or without it. A script that has to send the agent requests next stops it, starts it with --wait, and branches on the exit code:

afy stop api-gateway afy start api-gateway --wait case $? in 0) echo "running" ;; 4) echo "still being started by Aetherfy" ;; *) echo "not started" ;; esac

A start accepted while the stop was still finishing, carried out within the bound (exit 0). The bound is the server’s own wait_bound_seconds for this start, so the number you see differs:

✓ Agent 'api-gateway' will start once its previous stop finishes (at most 279s). ✓ Agent 'api-gateway' resumed.

A start on a full host, carried out on a new machine (exit 0):

✓ Agent 'api-gateway' is being started on a new machine: its machine's host has no room to resume it, so Aetherfy recreates it from the current release (at most 413s). ✓ Agent 'api-gateway' resumed. Its machine's host had no room to resume it, so it was started on a new machine from its current release: a cold start.

A start still pending when the bound has passed (exit 4). The agent is read every 5 seconds, so the time it names is the bound rounded up to the next read:

✓ Agent 'api-gateway' will start once its previous stop finishes (at most 279s). Warning: Agent 'api-gateway' has not started within 280s. Aetherfy is still retrying the start; 'afy status api-gateway' shows the result. Error: agent 'api-gateway' has not started within 280s; Aetherfy is still retrying

A start that was dropped exits 1, with the reason, as described above.

afy stop takes no flags; afy start takes only --wait. 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-worker

Restoring 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.

afy restore returns once the restore is accepted. It is checked again when it runs, and a restore refused then leaves the agent archived: afy status <name> prints a Reason line saying why and what to do. afy list shows the status alone, which reads the same for a restore still running and a refused one.

Cancelling a pending Aetherfy deployment

afy cancel <name> cancels a pending deployment for the agent. It takes no flags.

afy cancel scraper

Only 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 json

The text output covers:

FieldNotes
IDThe Aetherfy agent ID
NameThe agent name
TypeSERVICE or JOB
StatusCurrent lifecycle status, with health or degraded detail when present
ReasonWhy the agent is not serving, when Aetherfy recorded a reason: its app or its image is no longer on the compute plane, every running machine of it is failing its health check (with the error the machine reports), or the compute plane no longer runs any of its machines. Absent otherwise
RegionsThe regions the agent is deployed in
Spawn EnabledWhether this agent may start child agents
WorkspaceThe workspace the agent belongs to, if any
ScheduleThe schedule expression, labelled UTC
Next runNext scheduled fire time, or (paused)
Last tickWhat 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 runThe 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
CreatedCreation timestamp
UpdatedLast-modified timestamp
DescriptionThe agent description
Spawn relationshipsParent and child agents
Repo, Branch, Directory, Webhook idThe agent’s GitHub link, when it has one

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 saysWhat happenedWhat fixes it
GitHub disconnected — pushes are not deployingThe Aetherfy account is no longer connected to GitHubafy github connect. The link is kept, so deploys resume on reconnect
Branch deleted — pushes are not deployingThe branch the link watches was deleted, and status names which and whenRecreate the branch and push. Relinking would succeed and change nothing

Neither state is reported on GitHub. Aetherfy announces a skipped push as a check on the commit, which needs the installation token a disconnect removes, and a deleted branch has no commit to attach one to at all.

A push that arrived while a deploy of the agent was in progress waits for it, and has no deployment until it starts (pushing while a deploy is running). While one waits, status prints it:

Push waiting: 9f8e7d6, received 2026-09-26 12:00 UTC — deploys when the deploy in progress ends

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, branch_deleted_at and waiting_push (sha and received_at, or null when no push waits). 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.

FlagShortTypeDefaultDescription
--force-fboolfalseSkip confirmation prompt
afy rename scraper catalogue-scraper --force

An 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 directoryWhat happens
An aetherfy.yaml whose name: is the OLD nameThat one key is rewritten. Comments, key order, quoting and line endings are left alone
An aetherfy.yaml naming a different agentNothing is touched. That file belongs to another project, and rewriting it would retarget its deploys
No aetherfy.yaml, or one with no name: keyNothing 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 (what the dashboard does not do), 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.

FlagShortTypeDefaultDescription
--workspacestringemptyAssign the agent to this workspace
--no-workspaceboolfalseClear the agent’s workspace (make it workspaceless)
--description-dstringemptySet the agent’s description

Rules enforced by the Aetherfy CLI:

  • At least one flag is required; with none, the command errors.
  • --workspace and --no-workspace are mutually exclusive.
  • --workspace "" is rejected. Use --no-workspace to 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.

Granting an Aetherfy agent another workspace with access

afy access <agent> shows, and changes, which workspaces an agent’s key can use on the Aetherfy vector API: its own workspace, plus the ones you grant it. Any other workspace is refused to the agent. See what an agent’s key reaches.

# What can the agent reach? afy access support-bot # Grant it a second workspace; the command shows what becomes reachable and asks first afy access support-bot --add research # From a script: grant the collections in no workspace and revoke another workspace afy access support-bot --add "" --remove archive --yes --json
FlagDefaultEffect
--add—Grant a workspace. Repeat it, or give a comma-separated list. "" names the collections in no workspace; Windows PowerShell 5.1 drops an empty argument, so there write --add=
--remove—Revoke a granted workspace, the same way; --remove= for the collections in no workspace on Windows PowerShell 5.1
--yes, -yfalseGrant without asking
--jsonfalsePrint one JSON object: the agent’s resulting access, and what was added and removed

Granting widens what the agent’s code can read and write, so --add first prints what would become reachable and asks Proceed? [y/N]. Without a terminal, or with --json, --yes is required: the command exits 2 and sends nothing otherwise. Removing narrows access and asks nothing, and a change that changes nothing says so and asks nothing. The agent’s own workspace is not a grant: adding or removing it is refused, exit 2, before anything is sent. So is an --add or --remove value starting with -: it is what a dropped "" leaves, the next argument read as the value, and the refusal names the = form.

A change takes effect within 60 seconds, and a grant survives the agent’s redeploys. Only your own key can run this command: an agent’s key is refused it.

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.

FlagShortTypeDefaultDescription
--output-ostringemptyWrite 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.yaml

The 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.

FlagShortTypeDefaultDescription
--path-pstring.Project directory containing aetherfy.yaml
afy diff afy diff --path ./services/scraper

The output legend:

MarkerMeaning
~ field: old → newThe deploy would change this field
+ field: valueThe deploy would set this field
- fieldThe deploy would clear this field
= field: valueNo-op — local and deployed values match
field: valuePreserved — 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 fi

Running 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.

FlagShortTypeDefaultDescription
--payload-pstringemptyJSON payload to pass to the run
--payload-file-fstringemptyRead the JSON payload from a file
--waitboolfalseWait 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 --wait

Without --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 codeMeaning and fix
AGENT_NOT_DEPLOYEDThe 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.

FlagShortTypeDefaultDescription
--limitint20Maximum number of runs to show (max 100)
afy runs scraper afy runs scraper --limit 100 afy runs scraper -o json

Columns:

ColumnMeaning
WhenWhen the run was started, in UTC and labelled so — the same instant -o json gives as created_at
TriggerWhat started it — cron for a scheduled fire, or a manual trigger
StateRun outcome
ReleaseThe 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
DurationWall-clock run time
Run IDPass 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 scraper

Both 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.

Last updated on