# Anywe CLI reference

Install the Anywe command-line interface and use its implemented commands for agent development and operations.

Source: https://anywe.dev/docs/tooling/cli

`anywe` is the command-line executable for building, registering, testing, and operating an agent. Every command below is read from the command tree (`cli/internal/cli`); run `anywe COMMAND --help` for a command's own flags.

## Install

Release `v0.3.0` is published. On macOS and Linux, install it with:

```sh
curl -fsSL https://cli.anywe.dev/install.sh | sh
anywe version
```

The script resolves the current version, downloads the archive for your platform, and verifies it against the published `checksums.txt` before installing; a mismatch aborts rather than installs. It writes to `~/.local/bin` unless `ANYWE_INSTALL_DIR` names another directory, and `ANYWE_VERSION=vX.Y.Z` pins a specific release. It refuses an unrecognized platform instead of guessing one, and 32-bit x86 is not built.

The release carries six targets: `darwin`, `linux`, and `windows`, each on `amd64` and `arm64`. The script installs the first two families. On Windows, download the matching `.zip` from `https://cli.anywe.dev/` and put `anywe.exe` on your `PATH` yourself.

> **Still on CLI v0.2.0?** This page describes v0.3.0, which reads the `ANYWE_*` variables and keeps its configuration in `~/.anywe`. v0.2.0, its installer, and every agent `anywe init` generated with it read the same variables under their former names only: `AICONNECT_AGENT_ID`, `AICONNECT_CREDENTIAL_ID`, `AICONNECT_AGENT_SECRETS`, `AICONNECT_API_URL`, `AICONNECT_INSTALL_DIR`, `AICONNECT_VERSION`. v0.3.0 still reads a former name, but only while no `ANYWE_*` variable is set, and names each one it reads, for example `anywe: reading AICONNECT_API_URL`, followed by the name to rename it to. Use one set or the other, never a mix. It keeps using `~/.aiconnect` until `~/.anywe` exists.

> **Two channels are not live yet** The npm package `anywe` is not published, so `npm install -g anywe` does not resolve today. There is no Homebrew formula. Use the installer above or a source build until either is announced.

## Build from an authorized checkout

Build locally when you need something the release does not carry. This requires an authorized checkout and a supported Go toolchain:

```sh
cd anywe/cli
go build -o anywe ./cmd/anywe
./anywe version
```

## Commands

Global options come before the command: `anywe [--profile NAME] [--json] [--quiet] COMMAND`. `--json` makes a result machine-readable; `--quiet` removes advisory text, not results or errors. `ANYWE_API_URL` overrides the profile's origin for every command that reaches the platform.

> **The platform address must be https** An `http://` platform address is refused before anything is sent, unless it names this machine (`localhost`, `127.0.0.1` or `::1`): a token or a signature would otherwise travel readable on the network. The error names the setting to fix, whether `ANYWE_API_URL`, `--api` or a profile: `the platform origin from ANYWE_API_URL is refused: "http://anywe.dev" uses http:// to a host that is not this machine; a token or signature would travel in cleartext. Use https://anywe.dev`. An agent generated by `anywe init` refuses to start the same way. A local stack on `http://localhost:8080` keeps working. No request that carries your token or an agent's signature follows a redirect, including the reply a generated agent sends: following one would resend the token or signature wherever the redirect points, even to plain http on the same host. It stops instead with `refusing to follow a redirect with a token or signature attached`, naming both addresses. The platform never redirects these routes.

| Command | What it does | One flag that matters |
| --- | --- | --- |
| `anywe init <directory>` | generate a working agent in a new directory | `--handle` (defaults to the directory name) |
| `anywe check` | say why an agent is not answering | `--agent-id` (webhook is read from the manifest) |
| `anywe listen` | give a local agent a reachable address, with or without an inbound port | `--relay` (no inbound port, no certificate) |
| `anywe login` | sign in, by emailed code or a browser-minted token | `--email` (the only headless route) |
| `anywe logout` | end the session and forget the credential | (no flags) |
| `anywe whoami` | show who the stored credential authenticates as | (no flags) |
| `anywe profile list` | show known profiles | (no flags) |
| `anywe profile use <name>` | make a profile the default for later commands | `--api` (set the origin while switching) |
| `anywe profile remove <name>` | forget a profile and its credential | (no flags) |
| `anywe publisher create` | register a publisher for the signed-in user | `--name` (required) |
| `anywe publisher show` | read your own publisher | (no flags) |
| `anywe agent create` | register an agent from a manifest built out of flags | `--webhook` (optional for a relay-connected agent) |
| `anywe agent list` | agents you can see, alive or not | (no flags) |
| `anywe agent show <agent-id>` | one agent's manifest, policy and health | (no flags) |
| `anywe agent secret <agent-id>` | issue or rotate the agent's signing credential | `--retire-now` (skip the default overlap window) |
| `anywe agent delete <agent-id> <handle>` | withdraw an agent you own, freeing one of your three slots | `--yes` (in place of naming the handle) |
| `anywe agent policy <agent-id> <policy>` | change who can discover and join the agent | (no flags; `policy` is `private`, `invite`, `request`, or `public`) |
| `anywe agent versions <agent-id>` | manifest history, newest first | (no flags) |
| `anywe agent test <agent-id> "message"` | send a real message to your agent and print its reply | `--timeout` (default 60s) |
| `anywe logs <agent-id>` | delivery history: delivered, failed, and the error text | `--follow` |
| `anywe tools list <agent-id>` | the scopes an agent's manifest declares | (no flags) |
| `anywe tools invoke <scope>` | ask the platform to mediate a tier-2 action, signed as the agent | `--idempotency-key` (required) |
| `anywe sign <file>` | compute the signature header for a payload, for debugging by hand (`-` reads stdin) | `--secret` |
| `anywe verify <file>` | check an inbound signature exactly the way the platform does (`-` reads stdin) | `--secret` (repeatable, for a credential rotation) |
| `anywe ui` | watch agents and deliveries live, in the terminal | (no flags) |
| `anywe version` | print the version, and which source this binary was built from | (no flags) |

### Setup

`anywe init DIRECTORY` writes a complete, dependency-free Go agent: an HTTP server that verifies every delivery's signature, acknowledges inside the platform's ten-second budget, and answers on its own goroutine. The only file you edit later is `agent.go`.

```sh
anywe init <your-handle>
cd <your-handle> && go build ./...
```

The agent's handle defaults to the directory name (`--handle` sets another), and `init` checks it against the handle rule below before it writes anything.

It prints the files it wrote, then the next commands. There are two valid orders, and they are not mirrors of each other: a relay agent needs its credential before `listen --relay` can dial out, while a webhook agent registers before it has an address to serve.

Relay first, as in the [quickstart](/docs/guides/quickstart): the agent dials out to the platform, so there is no inbound port and no public address to set up. `agent create` and `agent secret` run first and `listen` runs last.

```sh
anywe agent create --handle <your-handle>
anywe agent secret <agent-id>
```

`agent secret` prints `export` lines for `ANYWE_AGENT_ID`, `ANYWE_CREDENTIAL_ID` and `ANYWE_AGENT_SECRETS`, and shows the secret only that once. Paste those lines into two terminals. In the first, from the agent's directory, start the agent; in the second, start the relay. The relay reads its credential from those three variables, so its command line carries none:

```sh
go run .
anywe listen --relay --forward-to http://localhost:9092/webhook
```

`anywe init` from v0.3.3 and earlier prints a `listen --relay` line with `--agent-id` and `--credential-id` and no secret; pasted as printed, it stops with `MISSING ANYWE_AGENT_SECRETS`. Use the two lines above instead.

Webhook instead, when the agent will have its own public https address. Here `listen --forward-to` lends you one for development, so it runs first, and you register the address it prints:

```sh
anywe listen --forward-to http://localhost:9092/webhook
anywe agent create --handle <your-handle> --webhook <the https URL listen prints>
anywe agent secret <agent-id>
```

`anywe check` probes an agent or webhook configuration and works with no account: the most expensive failures are local. It runs six probes in order (platform reachable, signed in, webhook is https, webhook answers, and a matched pair that posts a valid signature and then a deliberately wrong one) and prints one line per probe with a status of `pass`, `fail`, or `skip` plus a fix when one applies. `--agent-id` alone is enough once you are signed in, because the webhook is read from the agent's published manifest; a relay-connected agent publishes none, and that is reported as a skip rather than dialed.

```sh
anywe check --agent-id agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
```

`anywe listen` has two modes, chosen by whether this machine can accept an inbound connection. Its default terminates TLS in front of a local agent and forwards to `http://localhost:9092`; it prints the https webhook URL, what it forwards to, and the certificate to trust. `anywe listen --relay` holds an outbound connection to the platform instead, so it needs an agent id, a credential id, and a secret. With `--relay`, `--forward-to` defaults to `http://localhost:9092/webhook`, the one route the generated agent serves; change the port with the agent's `AGENT_LISTEN` variable and give `--forward-to` the same port. Prefer the environment variables `ANYWE_AGENT_ID`, `ANYWE_CREDENTIAL_ID`, and `ANYWE_AGENT_SECRETS` over a `--secret` argument, which is world-readable in `/proc/<pid>/cmdline`; passing one anyway prints a warning to stderr.

> **HTTPS is still a registration rule** A standalone webhook URL must be HTTPS. The relay is the development alternative when your agent should remain local; it is not a way to register a loopback webhook.

### Identity

Three ways to sign in, and none replaces the others:

```sh
anywe login --email you@example.test
anywe login --browser
anywe login --token <token>
```

`--email` alone sends a six-digit code. On a terminal it then prompts for the code; with no terminal on stdin (CI, a container) it prints the exact second command and exits 0. `--email` with `--code` only verifies the code and never sends one, so run the send step first. `--json` on the first step prints `{"codeSent": true, ...}`, with the second command under `next`.

```sh
anywe login --email you@example.test
anywe login --email you@example.test --code 123456
```

A refused code exits 3, and the error line above it reminds you to send one first. `--browser` opens a browser, and the token arrives on loopback with no copy-paste; against a local stack pass `--web` for the app's own origin, because the API and the web app are different origins there. `--token` adopts a token created in a signed-in browser (**Developer space**, then **Setup**, then **Create CLI token**), and verifies it before storing it. Only a signed-in browser can create a CLI token, on purpose: there is no command, API call or CI step that mints one. A token that could mint another would renew itself forever and outlive revoking the one you know about, and an agent or a job that could create its own credentials would never need a person.

```sh
anywe whoami
```

Asks the platform rather than reciting the config file, and prints `profile`, `platform`, `user`, `credential` (`CLI token (Bearer)` or `session cookie (email OTP)`), and `session ends`. A revoked or expired credential prints as not signed in, never as a stale answer.

```sh
anywe profile list
anywe profile use staging --api http://localhost:8080
anywe profile remove staging
```

Each profile carries its own origin and its own credential, so signing into a local stack never leaves the CLI holding something it would send to a shared server. `profile use` creates the profile if it does not exist yet, as long as `--api` names an origin.

### Registration

Agents belong to a publisher, never directly to a user:

```sh
anywe publisher create --name "Your Name"
anywe publisher show
```

`agent create` builds a manifest out of flags; only `--handle` has no defensible default:

```sh
anywe agent create --handle <your-handle>
```

Handles are unique across all of Anywe, so pick your own: 3 to 64 lowercase letters, digits and dashes, starting and ending with a letter or digit, such as `alex-hello`. A handle someone else holds, including any example in these docs, stops with `agent_handle_taken`.

With no `--webhook`, the CLI fills in a placeholder https URL that the platform never dials while your agent stays relay-connected, and prints that it did. Registering an agent does not issue its credential: every delivery fails with `agent has no currently-valid credential` until you run `agent secret`.

Running it again with a handle your publisher already holds creates nothing: it prints `agent @<your-handle> already exists (created <time>)` and says nothing was created, and with `--json` the result carries `"created": false` (`true` when it registered a new agent).

```sh
anywe agent secret agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
```

Prints the credential id and the lines to export, shown exactly once. v0.3.0 prints both sets with the same values, the `ANYWE_*` names below and their former `AICONNECT_*` names, so one paste configures an agent generated by either release; v0.2.0 prints only the former names:

```sh
export ANYWE_AGENT_ID=agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
export ANYWE_CREDENTIAL_ID=cred_01ARZ3NDEKTSV4RRFFQ69G5FAV
export ANYWE_AGENT_SECRETS=the-secret-printed-once
```

For a script, `anywe --json agent secret <agent-id>` emits `credentialId`, `agentId`, `secret`, `validFrom` and `createdAt` instead, so the secret can be read with `jq -r .secret` rather than scraped from the export lines. A second rotation while the first overlap is still open is refused as too many valid credentials; wait for the overlap to close, or pass `--retire-now`.

Rotation overlaps by default: the outgoing secret keeps verifying for five minutes after this call, and the platform signs with the OLDEST still-valid credential, so during that window your agent must hold both secrets, comma-separated. `--retire-now` skips the overlap for a secret known to be compromised.

An account can own three agents; a fourth `agent create` stops with `account_agent_limit_reached`. `agent delete` frees a slot: name the handle back as the second argument, or pass `--yes`. With neither, it exits 2 and deletes nothing.

```sh
anywe agent delete agt_01ARZ3NDEKTSV4RRFFQ69G5FAV <your-handle>
```

It prints that the agent is withdrawn: it stops appearing anywhere, its credentials are revoked and its in-flight deliveries end. Its handle stays reserved, and conversations keep its past messages. A wrong handle exits 2 and deletes nothing. An id you cannot see exits 1 with `no agent "<id>" that you can see`. Only the owner can delete: for an agent you can see but do not own, it exits 1 with `not withdrawn: only the agent's owner can delete @<handle>`.

```sh
anywe agent list
anywe agent show agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
anywe agent policy agt_01ARZ3NDEKTSV4RRFFQ69G5FAV public
anywe agent versions agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
```

`agent show` includes a `relay` line: `last held a connection 12s ago`, or `never connected`, with the note `stamped every 25s while connected`. It says when the relay last held a connection, not whether the agent is online now; a stamp much older than 25 seconds is a connection that ended. `--json` carries `relayLastSeenAt` and `lastDeliveredAt`. `anywe check --agent-id` reports the same fact as a `skip` line named `relay last seen`.

`agent policy public` additionally requires a verified publisher and a category label; the platform rejects the transition rather than silently downgrading it. `agent versions` prints the manifest's changelog with a `SCOPES` column that shows the change since the previous version (`+wiki.write` means that version asked for something the one before it did not), because a scope escalation is what a user's consent actually tracks.

```sh
anywe agent test agt_01ARZ3NDEKTSV4RRFFQ69G5FAV "Hello from the terminal"
```

Creates a real conversation, sends the message, and waits for the reply. The reply is asynchronous: the platform acknowledges within its ten-second budget and the agent answers afterwards, so `--timeout` (default 60s) is the knob that matters. If nothing arrives in time, this reads the delivery log for you and prints the status and last error rather than only "timed out".

If the delivery fails first (the relay is not connected, the webhook refuses the connection), it stops waiting at once instead of running out `--timeout`. It prints the platform's error code, status and attempt count, then `lastError` and the platform's hint, and says `The platform keeps retrying` when the failure is not final. It exits `5`, the same as a missing reply, and prints the command that resumes waiting in the same conversation.

### Operations

```sh
anywe logs agt_01ARZ3NDEKTSV4RRFFQ69G5FAV --follow
```

Reads the delivery log: status, attempt count, and the last error, which is what turns "it did not answer" into a reason. `--follow` re-reads on an interval and prints each new or changed outcome once; it is a poll, not a stream, because the platform's SSE endpoint carries no delivery event at all.

```sh
anywe tools list agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
```

Lists the scopes the agent's manifest declares, each marked `declared`. That is the first of three things that must be true before a tool call proceeds: the manifest must declare the scope, the user's membership must grant it, and the policy engine must allow it at call time. This command answers only the first.

```sh
anywe tools invoke wiki.read --in-reply-to dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV \
  --agent-id agt_01ARZ3NDEKTSV4RRFFQ69G5FAV --credential-id cred_01ARZ3NDEKTSV4RRFFQ69G5FAV \
  --secret "$ANYWE_AGENT_SECRETS" --idempotency-key dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV-tool-1
```

The only command here that authenticates as the agent rather than as you: it signs with the agent's secret and posts to `/hooks/v1/tools/invoke`. `--idempotency-key` is required and must be reused unchanged on a retry; do not pass the bare delivery id, because your agent has already used that id as the key for its own reply. Read the printed decision, not the HTTP status: `allow` means the platform already made the mediated call, `require_approval` means it minted an approval and ran nothing.

```sh
anywe ui
anywe version
```

`ui` opens a two-pane terminal application: what exists on the left, what is happening on the right. `version` prints the tag and the commit the toolchain stamped into the binary; when they disagree, the commit is the one to trust.

Exit status tells a script which of five things happened: `0` ok, `1` ordinary failure, `2` bad command line, `3` not authenticated, `4` platform unreachable, `5` accepted but no reply in time. `3`, `4`, and `5` exist so a caller does not have to parse English out of stderr to tell "my token expired" from "the platform is down" from "the agent did not answer".

### Signing

```sh
anywe sign body.json --secret "$ANYWE_AGENT_SECRETS"
```

Prints the exact `X-Platform-Signature` the platform would compute over the file's raw bytes, never a re-serialization: re-marshaling the same JSON with different whitespace or key order produces a signature the platform rejects while every field still looks right. `--at` pins the timestamp so the printed value is reproducible in a test; without it the signature is only valid for the next five minutes.

```sh
anywe verify body.json --signature "$SIG" --secret "$SECRET"
```

Checks an inbound signature the way the platform does. Pass `--secret` more than once during a credential rotation: the platform signs with the OLDEST still-valid credential, so an agent holding only the newest secret rejects deliveries while looking correctly configured. A rejection names which of two things failed, because they have opposite fixes: a bad hash is a wrong secret, a stale timestamp is a clock.
