AppXen CLI
Manage your knowledge base and run Agent Orchestrator workflows from the terminal. The CLI is a client for your own AppXen Platform appliance: it sends your key, documents and queries only to the appliance you point it at.
Version: this page describes CLI 0.2.7. appxen status prints the CLI version on its first line, and appxen --version prints it too. If yours is older, upgrade with pip install --upgrade appxen.
Install
One command to install:
curl -fsSL https://appxen.ai/install.sh | sh Or install directly with pip:
pip install appxen Requires Python 3.10+. The installer prefers pipx for isolated installs.
Log In to Your Appliance
Create an API key in your appliance's console (https://<your-appliance-host>) under API Keys in the sidebar. The key looks like axgw_live_k1_... and is shown once. Then:
appxen login In a terminal you are prompted for your key (input is hidden). In a script, pipe it: when stdin is not a terminal, the key is the first line of stdin and nothing is echoed.
printf '%s\n' "$APPXEN_KEY" | appxen login --endpoint https://your-appliance-host Which endpoint?
The endpoint is your appliance's own origin. The CLI derives the gateway and orchestrator paths from it. By default it is https://localhost: the appliance's own proxy, which is what you reach when you run the CLI on the appliance itself. For your appliance, the endpoint is the ConsoleUrl output of your CloudFormation stack, or your domain after appxenctl set-domain.
No domain set (the default)
The appliance serves a self-signed certificate, and the CLI refuses a certificate it cannot verify, and says so. Pass --insecure (or set APPXEN_INSECURE=1) to skip the verification. Use it for your own appliance, over a network path you trust, until it has a domain; never against a host you do not control. Every command that skips the verification prints one warning line on stderr.
# on the appliance itself
appxen login --insecure
# from your own machine
appxen login --endpoint https://your-appliance-host --insecure A domain is set
If the appliance has a domain name (DomainName at launch, or sudo appxenctl set-domain <your-domain>), always pass --endpoint https://<your-domain>, on the appliance itself too. The proxy then serves only that domain, with its Let's Encrypt certificate, so the default https://localhost fails the TLS handshake and --insecure does not help.
appxen login --endpoint https://appliance.example.com What login does
The CLI checks the key's format (axgw_*), then verifies it with the appliance on an authenticated route. Only a key the appliance accepted is saved. A refused key, or one that could not be verified (the appliance is unreachable, its certificate is refused), is not saved and the command exits 1. For an appliance that cannot be reached yet, appxen login --no-verify saves the key unchecked.
Your key is stored in ~/.config/appxen/config.toml with 0600 permissions, in a directory with 0700.
| Option | Default | Description |
|---|---|---|
--profile, -p | the global appxen --profile, then APPXEN_PROFILE, then default | Profile to save to |
--endpoint, -e | https://localhost | Your appliance's origin |
--insecure | false | Skip TLS verification, for the appliance's self-signed certificate (also APPXEN_INSECURE=1) |
--verify / --no-verify | --verify | Verify the key with the appliance before saving it; --no-verify saves it unchecked |
Exit status of appxen login
| 0 | The key was saved (verified first, unless --no-verify). |
| 1 | The key was refused, could not be verified or was not given. Nothing was saved. |
| 2 | Usage: a malformed endpoint, or one that carries a user name or password (checked before the key is read), or an unknown option. |
Check Connectivity
Check connectivity and the API key, and show the gateway and RAG stats. The key is verified on an authenticated route, because the gateway's /health answers anyone.
appxen status
appxen status --json # machine-readable output CLI: 0.2.7
Endpoint: https://localhost
Service: mcp-gateway-pro
Gateway: 0.1.0
Status: Connected
RAG: 12 sources, 347 chunks CLI is the version of the CLI you run. Gateway is the version of the gateway package on your appliance, which is a separate number from the appliance release.
Exit status of appxen status
| 0 | Connected, with a key the appliance accepted. |
| 1 | The gateway is unreachable, or the key was refused (401, 403) or could not be verified (the authenticated route failed another way). Nothing is printed on stdout. |
| 2 | Usage: a malformed endpoint or API key, or an unknown option. |
Ingest Documents
Upload a file or an entire directory to the knowledge base:
# Single file
appxen ingest ./report.pdf
# Entire directory (recursive by default)
appxen ingest ./docs/
# Only markdown files
appxen ingest ./docs/ --glob "*.md"
# Top-level files only
appxen ingest ./docs/ --no-recursive
# Upload and exit without waiting
appxen ingest ./docs/ --no-wait
# Wait up to 30 minutes for each file
appxen ingest ./scans/ --timeout 1800
# JSON output: waits, then prints the final state
appxen ingest ./docs/ --json
appxen ingest ./docs/ --json --no-wait # the accepted state (status: queued) The CLI uploads each file, then polls until ingestion completes (chunking, embedding, indexing). Progress bars show both stages:
Found 14 file(s) to ingest.
Uploading architecture.pdf ━━━━━━━━━━━━━━━━━━━━━━━━━ 14/14
Waiting for 14 file(s) to process...
Processing architecture.pdf ━━━━━━━━━━━━━━━━━━━━━━━━ 14/14
OK 14 file(s) ingested successfully. | Option | Default | Description |
|---|---|---|
--glob, -g | * | Glob pattern for filtering files in a directory |
--recursive / --no-recursive | --recursive | Recurse into subdirectories |
--wait / --no-wait | --wait | Wait until every file is ingested, with --json too. --no-wait returns once the uploads are accepted. (--poll / --no-poll are the same option.) |
--timeout | 300 | Seconds to wait for EACH file before reporting it as not ready (the appliance goes on ingesting it) |
--json | false | Output raw JSON: one array, an entry per file |
With --json, stdout is the array and nothing else; the progress goes to stderr. An entry is file, source_id, filename, status and chunk_count; a file that failed also has error with the reason. A file that is not ready when its --timeout passes counts as a failure, and so does one whose source disappears during the wait (someone deleted it). The command goes on waiting for the other files. Only a refused API key ends it at once, because no other file can succeed.
Supported file types
Documents
.pdf, .docx, .html, .htm, .md, .txt, .csv, .json, .xml, .yaml, .yml
Code
.py, .js, .ts, .jsx, .tsx, .css, .sql, .sh, .rs, .go, .java, .c, .cpp, .h, .rb, .php
Images (OCR)
.png, .jpg, .jpeg, .tiff, .bmp
Unsupported file types are silently skipped when scanning a directory. The maximum file size is 100 MB. See Supported Formats for how each type is parsed.
Exit status of appxen ingest
| 0 | Every file was ingested (with --no-wait: accepted). |
| 1 | Nothing could be ingested at all: no supported file, not logged in, or the API key was refused. |
| 2 | Some or all files failed (upload refused, ingestion failed, not ready within --timeout, the source disappeared during the wait). The other files are still waited for, and with --json the document says which failed. Also a usage error (an unknown option, a --timeout that is not a positive number, a malformed endpoint or API key), which prints no document. |
| 130 | Interrupted (Ctrl-C). stderr says how many files were uploaded and each one's last status, and the appliance goes on ingesting what it accepted. |
A script tells a status 2 for failed files from a usage error by the output: with --json, failed files come with the document, each failed entry carrying error; a usage error prints nothing on stdout.
Search
Semantic search over your knowledge base:
appxen search "refund policy for enterprise customers"
appxen search "error handling" --top-k 10
appxen search "deployment steps" --json ╭──────────── #1 refund-policy.md (score: 0.847) ──────────╮
│ Enterprise customers can request a refund within 30 │
│ days of the invoice date. Requests are sent to the │
│ account team, who confirm the amount... │
╰──────────────────────── chunk: a1b2c3d4 ─────────────────╯
╭──────────── #2 support-handbook.md (score: 0.723) ───────╮
│ Refund requests that arrive through the support queue │
│ are tagged "billing" and routed to the finance team... │
╰──────────────────────── chunk: f6g7h8i9 ─────────────────╯ | Option | Default | Description |
|---|---|---|
--top-k, -k | 5 | Number of results to return |
--json | false | Output raw JSON |
Sources and Stats
appxen sources
List and manage the sources in your knowledge base:
appxen sources # list all sources
appxen sources --status ready # filter by status
appxen sources --search "report" # filter by filename
appxen sources --limit 50 # more results
appxen sources --json # JSON output | Option | Default | Description |
|---|---|---|
--status, -s | Filter by status: ready, processing, queued, failed | |
--search, -q | Filter by filename | |
--limit, -n | 20 | Results per page |
--offset | 0 | Pagination offset |
--json | false | Output raw JSON |
appxen sources delete <source_id>
Delete a source and all its chunks. You are asked to confirm unless you pass --yes (-y):
appxen sources delete a1b2c3d4-e5f6-7890-abcd-1234567890ab
appxen sources delete a1b2c3d4-e5f6-7890-abcd-1234567890ab --yes # skip confirmation appxen stats
Show knowledge base statistics (add --json for JSON):
╭──────────────────────── RAG Engine Stats ─────────────────────────╮
│ Sources: 47 │
│ Chunks: 1,203 │
│ Storage: 24.7 MB │
╰───────────────────────────────────────────────────────────────────╯ All Commands
| Command | Description |
|---|---|
appxen login | Save your API key, verified with the appliance first |
appxen status | Check connectivity, the key and gateway info |
appxen ingest <path> | Upload files or directories to the knowledge base |
appxen search <query> | Semantic search over your documents |
appxen sources | List all indexed sources |
appxen sources delete <id> | Delete a source and its chunks |
appxen stats | Show knowledge base statistics |
| Workflow Commands | |
appxen workflow list | List all workflows |
appxen workflow compile <file> | Preview a compiled workflow without saving |
appxen workflow create <file> | Compile and save a markdown workflow |
appxen workflow get <id> | Get workflow details |
appxen workflow delete <id> | Delete a workflow |
appxen workflow run <id> | Start an execution and wait for it to end |
appxen workflow status <exec_id> | Get execution status and step results (--poll waits for the end) |
appxen workflow output <exec_id> | Get the output of an execution |
appxen workflow stop <exec_id> | Stop a running execution |
Every command takes --help for usage info. The commands that read from your appliance take --json for scripting (see JSON Output below).
Run a Workflow
Create a workflow from a markdown file, run it with inputs, and save the output:
# Compile and save a workflow
appxen workflow create ./blog-post.md --name "Blog Post Pipeline"
# Run with inputs; starts it and waits for the end
appxen workflow run wf_abc123 \
--input topic="AI in healthcare" \
--save result.md
# Start it and return the execution id
appxen workflow run wf_abc123 -i topic=security --no-poll --json
# Wait for a running execution, up to 30 minutes
appxen workflow status exec_xyz789 --poll --timeout 1800
# Get the output, or one step's output
appxen workflow output exec_xyz789 --save result.md
appxen workflow output exec_xyz789 --step research
# Stop an execution
appxen workflow stop exec_xyz789 workflow run (unless --no-poll) and workflow status --poll wait until the execution ends: completed, failed, rejected, timed_out or aborted. An execution held at an approval gate (awaiting_approval) is approved or rejected in the console.
| Option | Default | Description |
|---|---|---|
--timeout | 600 | Seconds to wait before giving up; the execution keeps running on the appliance (workflow run and workflow status --poll) |
--save FILE, -o | Write the output to a file (workflow run and workflow output), with --json too (the "saved" line is then on stderr) | |
--input key=value, -i | An input for the run, repeated for each input (workflow run) |
--save is refused with --no-poll (exit 2: there is no output to save yet), and with no output to write it is an error, never an empty file. With --json, stdout is one document, {"execution": ..., "steps": [...]}: the final state, or at the deadline the state as last seen; nothing after an interrupt. workflow stop on an execution that had already ended changes nothing, says so, and exits 0.
Exit status of appxen workflow run
| 0 | The execution completed (with --no-poll: it was started). |
| 1 | It could not be started, it ended failed, rejected, timed_out or aborted (its error is printed), or it was still running when --timeout passed. |
| 2 | Usage: --save together with --no-poll, an unknown option, a malformed endpoint or API key. |
| 130 | Interrupted (Ctrl-C) during the wait. stderr says what status the execution was last seen in, and it keeps running on the appliance. |
Exit status of appxen workflow status
| 0 | The status was read (with --poll: the execution completed). |
| 1 | The execution could not be read, or with --poll it ended failed, rejected, timed_out or aborted, or it was still running when --timeout passed. |
| 2 | Usage: an unknown option, a --timeout that is not a positive number, a malformed endpoint or API key. |
| 130 | Interrupted (Ctrl-C) during --poll. The execution keeps running on the appliance. |
Configuration
Config file
Stored at ~/.config/appxen/config.toml:
[default]
api_key = "axgw_live_k1_..."
endpoint = "https://localhost"
insecure = true
[staging]
api_key = "axgw_test_k1_..."
endpoint = "https://staging-appliance.example.com"
insecure = false endpoint is your appliance's origin (https://<host>[:port]). One that carries a user name or password (https://user:pass@host) is refused. Plain http:// to a host other than the machine you are on works, with a warning on stderr that the API key travels unencrypted. insecure skips TLS verification only as the boolean true. The file is replaced atomically on every save.
Switch profiles with --profile or APPXEN_PROFILE:
appxen --profile staging sources
appxen -p staging search "test query"
APPXEN_PROFILE=staging appxen stats
appxen --profile staging login # saves to [staging]
Which profile is used, nearest first: the command's own --profile (only login has one), the global appxen --profile, APPXEN_PROFILE, then default.
Environment variables
Environment variables override the config file, for one-off commands or CI/CD:
| Variable | Description |
|---|---|
APPXEN_API_KEY | API key. When set, no profile is read: the endpoint is APPXEN_ENDPOINT (default https://localhost) |
APPXEN_ENDPOINT | Your appliance's origin, used with APPXEN_API_KEY |
APPXEN_INSECURE | 1, true, yes or on skips TLS verification, for the appliance's self-signed certificate. Any other value (0, false, empty) keeps it on. Overrides the profile's insecure |
APPXEN_PROFILE | Profile to use when --profile is not given (default: default) |
# One-off command against the appliance box itself
APPXEN_API_KEY=axgw_test_k1_abc123 APPXEN_INSECURE=1 appxen status JSON Output and Scripting
Commands that read from your appliance take --json. Stdout is then exactly one JSON document, written as it is (never wrapped to the terminal width, text outside ASCII escaped). Progress, warnings and errors go to stderr. A command that fails prints no document, except a wait that ended (see workflow) and ingest, whose array says which file failed. Combine it with jq:
# Get source IDs for all ready sources
appxen sources --json | jq '.sources[] | select(.status == "ready") | .source_id'
# Count chunks across all sources
appxen stats --json | jq '.chunk_count'
# Ingest and capture results
appxen ingest ./docs/ --json | jq '.[] | {file, status, source_id}'
# Search and get raw JSON for processing
appxen search "error handling patterns" --top-k 20 --json
# Delete all failed sources
appxen sources --status failed --json \
| jq -r '.sources[].source_id' \
| xargs -I{} appxen sources delete {} --yes CI/CD
Set the key and your appliance's origin in the environment, then run commands as usual:
export APPXEN_API_KEY=${{ secrets.APPXEN_API_KEY }}
export APPXEN_ENDPOINT=https://your-appliance-host # your appliance's origin
# Sync docs on every deploy
appxen ingest ./docs/ --glob "*.md" Exit Status
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | The command failed: not logged in, the appliance is unreachable or refused the API key, an execution did not complete |
| 2 | Usage: an unknown option, a malformed endpoint (https://host:abc, https://user:pass@host), an API key that cannot be sent, --save with --no-poll. For ingest also: some or all files failed (see above) |
| 130 | Ctrl-C during a wait (ingest, workflow run, workflow status --poll) |
Errors and warnings go to stderr, each message on one unwrapped line, and never contain the API key. A certificate that cannot be verified, a refused key and a malformed endpoint each say what to do next.
appxen status (without --json) and appxen login also look for a newer CLI at https://appxen.ai/releases/latest.json (3 seconds at most; a failure is silent, a success is remembered for a day).