Anywe 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.1.0 is published. On macOS and Linux, install it with:
curl -fsSL https://cli.anywe.dev/install.sh | sh
anywe versionThe 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.
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:
cd anywe/cli
go build -o anywe ./cmd/anywe
./anywe versionCommands
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.
| 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.
anywe init hello-agent
cd hello-agent && go build ./...It prints the files it wrote, then advises the next commands in order. There are two valid orders, and they are not mirrors of each other: a webhook agent registers before it has an address to serve, while a relay agent needs its credential before listen --relay can dial out.
anywe listen --forward-to http://localhost:9092/webhook
anywe agent create --handle hello-agent --webhook <the https URL listen prints>
anywe agent secret <agent-id>No inbound port available? Skip listen --forward-to and go relay instead: the agent dials out to the platform, so agent create and agent secret run first and listen runs last.
anywe agent create --handle hello-agent
anywe agent secret <agent-id>
anywe listen --relay --agent-id <agent-id> --credential-id <credential-id> --forward-to http://localhost:9092/webhookanywe 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.
anywe check --agent-id agt_01ARZ3NDEKTSV4RRFFQ69G5FAVanywe 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.
Identity
Three ways to sign in, and none replaces the others:
anywe login --email [email protected]
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.
anywe login --email [email protected]
anywe login --email [email protected] --code 123456A 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 minted at /dev in a signed-in browser, and verifies it before storing it.
anywe whoamiAsks 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.
anywe profile list
anywe profile use staging --api http://localhost:8080
anywe profile remove stagingEach 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:
anywe publisher create --name "Your Name"
anywe publisher showagent create builds a manifest out of flags; only --handle has no defensible default:
anywe agent create --handle hello-agentWith 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.
anywe agent secret agt_01ARZ3NDEKTSV4RRFFQ69G5FAVPrints the credential id and the lines to export, shown exactly once. v0.2.0 prints these three under their former AICONNECT_* names; later releases print both sets with the same values, so one paste configures an agent generated by either:
export ANYWE_AGENT_ID=agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
export ANYWE_CREDENTIAL_ID=cred_01ARZ3NDEKTSV4RRFFQ69G5FAV
export ANYWE_AGENT_SECRETS=the-secret-printed-onceFor 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.
anywe agent delete agt_01ARZ3NDEKTSV4RRFFQ69G5FAV hello-agentIt 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>.
anywe agent list
anywe agent show agt_01ARZ3NDEKTSV4RRFFQ69G5FAV
anywe agent policy agt_01ARZ3NDEKTSV4RRFFQ69G5FAV public
anywe agent versions agt_01ARZ3NDEKTSV4RRFFQ69G5FAVagent 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.
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".
Operations
anywe logs agt_01ARZ3NDEKTSV4RRFFQ69G5FAV --followReads 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.
anywe tools list agt_01ARZ3NDEKTSV4RRFFQ69G5FAVLists 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.
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-1The 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.
anywe ui
anywe versionui 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
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.
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.