@autifyhq/aximo-cli
v0.14.0
Published
Autify Aximo Command Line Interface (CLI)
Readme
@autifyhq/aximo-cli
CLI for the Autify Aximo API, built for coding agents. Every command returns a JSON envelope ({ok, data} or {ok: false, error}) so output pipes cleanly into scripts and other tools.
Install
npm i -g @autifyhq/aximo-cliQuick Start
# 1. Configure credentials (interactive) — or set env vars, see Configuration below
aximo configure
# 2. Verify the connection
aximo health check
# 3. Run a desktop test session
aximo project list
aximo session create \
--project-id PROJECT_ID \
--title "Login test" \
--prompt "Go to https://example.com and verify the page loads."
# 4. Poll session status
aximo session get SESSION_IDGet your API key from Settings → API Keys in the Aximo dashboard.
Want to run tests on your own hardware instead of the cloud? See Device (BYOD) to register this desktop (Windows/macOS/Linux), or Mobile to drive a USB-connected Android phone or iPhone — each section starts with the exact prerequisites for a fresh machine.
Configuration
Each credential resolves independently: the environment variable wins, then the value in $AXIMO_HOME/config.json (written by aximo configure), then the default.
| Variable | Required | Default | Notes |
| ---------------- | -------- | -------------------------- | ------------------------------------------------------------ |
| AXIMO_API_KEY | Yes | — | Organization API key |
| AXIMO_BASE_URL | No | https://aximo.autify.com | Override for sandbox / self-hosted |
| AXIMO_HOME | No | ~/.aximo | Data directory (config, device credentials, embedded Python) |
Set them interactively or pass flags — config is written to $AXIMO_HOME/config.json (mode 0600 where the OS supports it; Windows falls back to file ACLs), and an empty prompt keeps the existing value:
aximo configure # interactive prompts
aximo configure --base-url URL --api-key KEY # non-interactiveOr keep credentials out of a file (CI, ephemeral shells) with env vars:
export AXIMO_API_KEY=your-api-key
export AXIMO_BASE_URL=https://aximo-sbx.autify.dev # optional, prod is the defaultCredentials and device registrations are per-environment — to switch between production and sandbox (https://aximo-sbx.autify.dev), re-run aximo configure with the other --base-url, or export AXIMO_BASE_URL/AXIMO_API_KEY, which override the saved config.
Commands
Run aximo <command> --help for detailed usage. aximo schema [command.path] returns machine-readable argument introspection (e.g. aximo schema session.create).
| Command | Description |
| ---------------------------- | -------------------------------------------------------------- |
| aximo configure | Save credentials to $AXIMO_HOME/config.json |
| aximo health check | Check API reachability |
| aximo project list | List organization projects |
| aximo session create | Create and queue a test session |
| aximo session get | Get session status |
| aximo session save-as-case | Save a finished session as a reusable test case |
| aximo case create | Create a test case |
| aximo case update | Update a test case |
| aximo case archive | Archive a test case (hide it, keep its history) |
| aximo case restore | Restore an archived test case |
| aximo case delete | Permanently delete an archived test case (admin/owner) |
| aximo plan create | Create a test plan |
| aximo plan update | Update a test plan |
| aximo plan archive | Archive a test plan (hide it; schedule is kept) |
| aximo plan restore | Restore an archived test plan |
| aximo plan delete | Permanently delete an archived test plan (admin/owner) |
| aximo plan add-case | Add a case to a plan |
| aximo plan remove-case | Remove a case from a plan |
| aximo trigger case run | Trigger a dashboard-created test case |
| aximo trigger case get | Get triggered case status |
| aximo trigger plan run | Trigger a test plan |
| aximo trigger plan get | Get plan run status |
| aximo upload create | Create an upload slot for an APK/IPA |
| aximo upload list | List previously uploaded apps |
| aximo upload get | Poll upload processing status |
| aximo upload put | Upload binary file to the slot |
| aximo upload submit | Finalize upload and trigger processing |
| aximo device list | List mobile devices from Device Farm |
| aximo device setup | First-time BYOD onboarding (API + Python + register) |
| aximo device register | Register this machine as a BYOD device |
| aximo device connect | Start the BYOD agent and connect to the relay |
| aximo device status | List your organization's BYOD devices |
| aximo device ping | Check your BYOD device's server-side status |
| aximo device doctor | Diagnose BYOD setup (env, Python, relay, status) |
| aximo device logs | Show the BYOD agent log |
| aximo device unregister | Remove a BYOD device server-side + clear local creds |
| aximo device reset | Remove BYOD local state (Python, deps, credentials) |
| aximo mobile list | List physically-connected phones (Android adb, iOS devicectl) |
| aximo mobile setup | Install the Appium drivers used to drive phones |
| aximo mobile doctor | Diagnose prerequisites (adb/Xcode/devicectl, signing, driver) |
| aximo mobile register | Register a connected Android phone or iPhone |
| aximo mobile install | Install an app (.apk via adb, .ipa auto-re-signed + devicectl) |
| aximo mobile connect | Start managed Appium + open the tunnel for the phone |
| aximo mobile unregister | Remove a local mobile device server-side + local creds |
| aximo schema | Show CLI surface as JSON |
| aximo mcp | Run a stdio MCP server for agents (see below) |
Mutating commands support --json '<raw body>' for direct OpenAPI payloads and --dry-run to preview requests without executing.
Health
aximo health checkReturns {ok: true, data: {status: "ok", auth: "ok"}} when the API is reachable and your API key is valid — after the health endpoint responds, the command verifies the key with an authenticated probe. If the API is reachable but the key is rejected, it exits non-zero with an unauthorized error envelope.
Projects
aximo project listLists active workspaces in your organization. Grab the id to pass as --project-id for session creation.
Sessions
aximo session create --title <t> --prompt <p> --project-id <id> \
[--platform ANDROID|IOS] [--model <m>] [--start-url <url>] \
[--case-id <id>] [--tags <comma-separated>] \
[--device-arn <arn>] [--device-name <name>] \
[--upload-arn <arn>] [--upload-type ANDROID_APP|IOS_APP] \
[--app-name <name>] [--bundle-id <id>] \
[--json '<body>'] [--dry-run]
aximo session get <sessionId>Desktop sessions default to platform=desktop, model=sonnet. Mobile sessions require both --device-arn and --upload-arn.
Test cases
aximo case create --project-id <id> --scenario <text> \
[--type web|mobile|desktop] [--title <t>] [--description <d>] \
[--format gherkin|markdown|plain_text] [--start-url <url>] \
[--directory-id <id>] [--json '<body>'] [--dry-run]
aximo case update <caseId> [--title <t>] [--description <d>] [--scenario <text>] \
[--type web|mobile|desktop|multi_device] [--format gherkin|markdown|plain_text] \
[--start-url <url>] [--mobile-config '<json>'] [--desktop-device <userDeviceId>] \
[--multi-device-config '<json>'] [--web-config '<json>'] [--json '<body>'] [--dry-run]
aximo case archive <caseId> [--dry-run]
aximo case delete <caseId> [--dry-run]
aximo session save-as-case <sessionId> \
[--title <t>] [--type web|mobile|desktop] \
[--scenario <text>] [--format gherkin|markdown|plain_text] \
[--json '<body>'] [--dry-run]case update leaves omitted fields unchanged. Flags cannot express null, so clearing a description or start URL goes through the raw body: --json '{"description": null}'. In that raw body an empty string is stored as an empty description rather than a clear; the --description flag instead treats "" as "leave unchanged".
case create returns a caseId you can run with aximo trigger case run. session save-as-case persists a finished session (terminal status, not yet linked to a case) as a reusable case, carrying over its scenario and run config. Cases created either way have API access enabled by default, so they are triggerable immediately. case archive is the everyday removal verb: the case disappears from lists, plans, and new runs, while its run history is kept, and archiving an already-archived case succeeds. case delete is the permanent one, and it now requires the case to be archived first: deleting an unarchived case returns a conflict envelope telling you to archive it. Once deleted, the case is dropped from any plans it belongs to, its rows in past plan runs go with it, and the agent sessions themselves are kept but detached.
Test plans
aximo plan create --project-id <id> --title <t> [--description <d>] [--json '<body>'] [--dry-run]
aximo plan update <planId> [--title <t>] [--description <d>] \
[--execution-mode parallel|sequential] [--stop-on-failure true|false] \
[--json '<body>'] [--dry-run]
aximo plan archive <planId> [--dry-run]
aximo plan add-case <planId> --case-id <caseId> [--json '<body>'] [--dry-run]
aximo plan remove-case <planId> <caseId> [--dry-run]Build a runnable plan: plan create → plan add-case (repeat per case) → aximo trigger plan run. Plans created this way have API access enabled by default. Cases must belong to the same workspace as the plan. plan archive hides a plan from lists, blocks new runs, and disables its schedule, keeping its cases and run history. plan update changes only the flags you pass; --execution-mode sets the plan's stored Execution setting (sequential runs one case at a time in plan order) and --stop-on-failure marks the remaining cases as skipped after a failure, which only takes effect on sequential runs.
Triggers (test cases and plans)
aximo trigger case run <caseId> [...]
aximo trigger case get <caseId> <sessionId>
aximo trigger plan run <planId> [...]
aximo trigger plan get <planId> <planRunId>Triggers a test case or plan. API access must be enabled on the resource — on by default for cases/plans created via this CLI; for dashboard-created ones, enable it in the Aximo dashboard settings. trigger case run returns a sessionId you can poll; trigger plan run returns a per-case breakdown.
trigger plan run accepts --execution-mode parallel|sequential to override the plan's stored Execution setting for that run only (omitted = plan setting; plans default to parallel). Sequential runs one case at a time in plan order, and the plan's stop-on-failure option applies only when the effective mode is sequential.
Uploads
aximo upload create --file-name <name> [--file-size <bytes>] [--platform ANDROID|IOS]
aximo upload put <uploadId> --file <path> [--url <presignedUrl>]
aximo upload submit <uploadId>
aximo upload get <uploadId>
aximo upload list [--file-type ANDROID_APP|IOS_APP]Typical flow: create → take presignedUrl from the output → put <uploadId> --file app.apk --url "<presignedUrl>" → submit → poll get until status=succeeded → extract uploadArn for mobile sessions. upload list auto-re-uploads expired Device Farm entries.
When presignedUrl is an absolute S3 URL (cloud deployments) the CLI PUTs the bytes straight to it with no API-key header. When it is a relative API path (local-storage deployments) — or --url is omitted — the CLI falls back to an authenticated PUT through the API. If that fallback fails with HTTP 405, the deployment is S3-backed: re-run with --url.
Schema
aximo schema # all commands
aximo schema session.create # one command's arg schemaMachine-readable JSON view of the CLI surface — used by agent skills for discovery.
Device (BYOD — Bring Your Own Device)
The device commands let you register your own desktop as a test execution target. Aximo's agent controls it remotely over WebSocket to run tests against apps on your machine (useful when tests need access to your LAN, VPN, or a specific desktop environment).
Terminology note: aximo device list returns mobile devices from Device Farm; aximo device status returns BYOD devices you've registered. Don't confuse the two.
Prerequisites
- Node.js 20.18+, plus
aximo-cliinstalled globally (see Install). On-prem releases also ship a single-file executable that needs neither — see Air-gapped networks. - An Aximo API key, configured via
aximo configureor env vars. - Active desktop session — the agent uses
pyautoguifor screenshots and input, so the screen must be unlocked and awake while tests run. On macOS, grant the terminal Accessibility and Screen Recording permissions (System Settings → Privacy & Security) — without them actions silently do nothing. - Outbound WebSocket to port 443 — most corporate firewalls allow this; if yours doesn't, see Troubleshooting.
Python is not a prerequisite. On first
connect(or viadevice setup), the CLI downloads a self-contained CPython build into$AXIMO_HOME/python/(~35 MB download, ~130 MB installed) and installs the required packages. Your system Python is untouched.
First-time setup
One-command onboarding:
aximo device setup --name "My Windows PC"This verifies API reachability, installs the embedded Python runtime, installs Python dependencies, and registers this machine. If the device is already registered locally, it skips registration and reports alreadyRegistered: true.
Or step-by-step, if you'd rather see each stage:
aximo health check # confirm credentials + connectivity
aximo device register --name "My Windows PC" # creates server-side device, writes $AXIMO_HOME/device.json (0600)
aximo device connect # installs embedded Python on first run, then connectsOn success, device connect prints something like:
Preparing Python environment...
Fetching embedded Python 3.12.13 (x86_64-pc-windows-msvc)...
Extracting...
Installing Python dependencies...
Python ready: C:\Users\<you>\.aximo\python\python.exe
Agent version: 8f3a1c04
Starting desktop agent, relay: wss://aximo.autify.com/devices/tunnel
Device "My Windows PC" is online. Press Ctrl+C to disconnect.Subsequent connect runs skip the Python download and dep install — they re-run only when the server-hosted agent script changes.
Daily use
aximo device connect # start the agent; keep the terminal open while tests run
# Ctrl+C to disconnectYour device shows up in the Aximo UI under Settings → Devices with status online while connected. In the session form, pick your device as the execution target; Aximo routes the agent's actions through the tunnel to your machine.
Self-hosted servers (self-signed / custom CA)
On-prem Aximo servers often present a self-signed certificate (a private "Aximo CA"). BYOD has two TLS clients: the Node CLI (API calls) and the embedded Python desktop agent (the wss://…/devices/tunnel relay). They trust a custom CA differently, so configure both:
# 1. Save the server's CA on this machine (or copy aximo-ca.pem from your admin).
openssl s_client -connect YOUR_SERVER:8443 -showcerts </dev/null 2>/dev/null \
| awk '/BEGIN CERTIFICATE/{i++} i==2{print} /END CERTIFICATE/{if(i==2) exit}' > ~/aximo-ca.pem
# 2. Point the CLI (Node) at it. Used for API calls AND auto-forwarded to the agent.
export NODE_EXTRA_CA_CERTS="$HOME/aximo-ca.pem"
aximo device connectWith NODE_EXTRA_CA_CERTS set, device connect automatically builds a combined trust store (the embedded Python's certifi roots plus your CA) and points the agent at it via SSL_CERT_FILE, so the relay connection verifies without extra steps.
To override the agent's trust store explicitly, set SSL_CERT_FILE yourself. It must include public roots and your CA — SSL_CERT_FILE replaces the entire store, so your CA alone would break any other TLS the agent does (browser launch, uploads):
PYBIN="$HOME/.aximo/python/bin/python3"
cat "$($PYBIN -m certifi)" ~/aximo-ca.pem > ~/.aximo/agent-ca-bundle.pem
export SSL_CERT_FILE="$HOME/.aximo/agent-ca-bundle.pem"Persist whichever variables you use in your shell profile. Public-CA servers (e.g. aximo.autify.com) need none of this.
Air-gapped networks (no internet access)
Normally setup reaches the internet three times: npm for the CLI, GitHub for the embedded Python, and PyPI for the agent's packages. With no internet, all three fail.
On-prem releases carry all three, and the server hands out the last two — a test machine only needs to reach your Aximo server, which it must anyway to register and run tests. See the on-prem installation guide for the full walkthrough; in short:
# 1. Copy cli-bundle\cli\aximo-<version>-win32-x64.exe off the server onto the machine.
# One self-contained file: no npm, no Node.js install needed.
C:\Aximo\aximo.exe configure # base URL + API key
# 2. The interpreter and packages come from your server, not the internet.
C:\Aximo\aximo.exe device setup
C:\Aximo\aximo.exe device register --name "My PC"
C:\Aximo\aximo.exe device connectNothing declares "I am air-gapped" — the server advertises what it can supply, and the CLI uses GitHub and PyPI only when it cannot. Cloud and proxied setups are unaffected.
Staging the files by hand. If you need the interpreter and wheels on a machine that cannot reach the server either (preparing a golden image, for example), point AXIMO_OFFLINE_DIR at a directory holding them:
$AXIMO_OFFLINE_DIR/
├── cpython-3.12.13+20260414-<target>-install_only.tar.gz
└── wheels/*.whlStaged files take precedence over the server. The archive is verified against the same checksum the CLI uses for the GitHub download, so a wrong or corrupted file is rejected rather than installed. Set the variable to an empty value to force the normal online path.
Build the wheels on the platform they will run on — most of the agent's requirements are published as source only, so pip download --platform ... --only-binary=:all: cannot resolve them:
python -m pip wheel -r requirements.txt -w wheels # run on the target platform, Python 3.12Managing devices
aximo device status # list YOUR BYOD devices, with online/offline + lastSeenAt
aximo device ping # server-side status for the locally registered device
aximo device ping --device-id DEVICE_ID # same, for a specific device
aximo device unregister # delete server-side + clear local credentials
aximo device unregister --device-id DEVICE_ID # unregister an orphaned device (no local creds)device ping returns what the server knows, including lastSeenAgoSeconds and the HTTP round-trip latencyMs. It does not round-trip through the relay, so a passing ping tells you "the server thinks this device is online," not "the local agent is responding right now."
Re-registering
aximo device register refuses to overwrite an existing local registration. To move a registration to a different device name:
aximo device unregister # clean up the old one server-side
aximo device register --name "New Name"If you've lost the original local credentials and need to delete the orphaned server-side device:
aximo device status # find its deviceId
aximo device unregister --device-id DEVICE_IDOr use aximo device register --force — but that silently orphans the previous server-side device, so the explicit unregister + register path is safer.
Inspecting and resetting local state
aximo device logs # tail the last 50 lines of the agent log
aximo device logs --follow # stream new lines (not JSON-enveloped; human use)
aximo device logs --path # print the log file path and exit
aximo device logs --clear # truncate the log
aximo device doctor # run all checks: env, API, Python, relay, status
aximo device doctor --skip-relay # skip WebSocket reachability probe
aximo device doctor --skip-api # offline checks only
aximo device reset --python # remove embedded Python + its cache
aximo device reset --deps # reinstall deps on next setup
aximo device reset --device # delete local credentials (server-side stays; run `unregister` first to also delete there)
aximo device reset --all # wipe $AXIMO_HOME entirelydoctor is the first thing to run when anything's off — it covers the whole stack in one shot.
BYOD troubleshooting
"No API key configured."
Either run aximo configure, or set AXIMO_API_KEY in your environment. Verify with echo $AXIMO_API_KEY (or echo $env:AXIMO_API_KEY on PowerShell).
"Authentication failed." Your device token is invalid (deleted server-side, or the server URL changed). Clean up and re-register:
aximo device unregister
aximo device register --name "My PC"
aximo device connect"Disconnected. Reconnecting in Xs..." Normal for brief network blips — the agent retries with exponential backoff (1 s → 30 s max). If it keeps failing:
- Check your internet connection.
- Confirm
AXIMO_BASE_URLis correct and reachable:aximo health check. - Confirm outbound WebSocket on 443 isn't blocked:
aximo device doctorruns a handshake probe.
Python download or pip install fails (corporate proxy).
The embedded Python is fetched from github.com/astral-sh/python-build-standalone/releases and packages come from PyPI, so aximo device setup needs outbound access to both. On a network that only reaches the internet through a proxy, set the standard proxy variables — the CLI, pip, and the desktop agent all honor them:
# macOS / Linux
export HTTPS_PROXY=http://your-proxy:port
export HTTP_PROXY=http://your-proxy:port
# Windows (PowerShell)
$env:HTTPS_PROXY = "http://your-proxy:port"
$env:HTTP_PROXY = "http://your-proxy:port"Loopback and a private-range AXIMO_BASE_URL (e.g. an on-prem 172.30.x.x server) are always reached directly, so you don't have to list them in NO_PROXY. If your proxy performs TLS interception, also point NODE_EXTRA_CA_CERTS at the proxy's CA so the download's certificate verifies.
If the proxy cannot reach GitHub at all, ask your network team to allow-list both github.com and its release-asset CDN release-assets.githubusercontent.com — the download redirects from the former to the latter, so allow-listing only github.com is not enough.
To retry from a clean slate: aximo device reset --python && aximo device setup.
Device shows online, but tests fail on screenshot / click.
Your screen is locked or the session is inactive. pyautogui needs an active desktop session.
macOS: actions seem to do nothing. System Settings → Privacy & Security → Accessibility and Screen Recording — both need to be granted to the terminal (or the Python binary that spawned).
Linux: "missing: scrot, xclip".
aximo device doctor flags this when the tools aren't on PATH. Install via your package manager (apt install scrot xclip on Debian/Ubuntu).
Windows: coordinates off / scrolling too fast or too slow.
The agent disables DPI-awareness and uses a calibrated scroll amount — if you still see mismatches, run aximo device doctor and share the output.
Environment & file layout
| Path | Mode | Purpose |
| --------------------------------- | ---- | -------------------------------------------------------- |
| $AXIMO_HOME/config.json | 0600 | CLI credentials (from aximo configure) |
| $AXIMO_HOME/device.json | 0600 | BYOD registration + device token |
| $AXIMO_HOME/agent/remote.py | 0644 | Python agent (server-synced) |
| $AXIMO_HOME/agent/manifest.json | 0644 | Version + sha256 of cached agent files |
| $AXIMO_HOME/python/ | — | Embedded CPython runtime |
| $AXIMO_HOME/remote.log | — | Agent log (rotate/clear via aximo device logs --clear) |
Default $AXIMO_HOME is ~/.aximo on macOS/Linux, %USERPROFILE%\.aximo on Windows. Override via the AXIMO_HOME env var.
Devices are registered per-environment (see Configuration) — a device registered against sandbox can't connect to production and vice versa.
Mobile (local Android phone or iPhone)
The mobile commands let you register a physically-connected Android phone or iPhone as an Aximo device and drive it locally, over the same gateway tunnel as desktop BYOD. Aximo's agent controls the phone through a CLI-managed Appium server — UiAutomator2 for Android, XCUITest for iOS — useful for running mobile tests against your own handset instead of a Device Farm device.
iOS is macOS-only and needs Xcode + an Apple ID. Driving a physical iPhone requires a development-signed WebDriverAgent (WDA) runner on the device — an Apple platform rule, not an Aximo one. The CLI is zero-config about it: it discovers your signing identity and Team ID from the keychain, finds or mints provisioning profiles via Xcode automatic signing, and (on iOS 17+) preinstalls a prebuilt WDA with no Xcode build. A free/personal Apple ID works. Android has no signing requirement, so iOS setup is heavier; everything after setup (
register→connect→ run) works the same on both platforms.
Terminology note: aximo mobile list shows phones plugged into this machine; aximo device list (under Device / BYOD) returns mobile devices from AWS Device Farm. Don't confuse the two.
Prerequisites
- Node.js 20.18+, plus
aximo-cliinstalled globally (see Install). - An Aximo API key, configured via
aximo configureor env vars. - A USB-connected phone:
- Android — USB debugging enabled and authorized — see Enable USB debugging on the phone.
- iOS — Developer Mode enabled (Settings → Privacy & Security) and the computer trusted.
- On the machine that runs
aximo mobile connect:- Android —
adband the Android SDK, withANDROID_HOMEset — see Set up your computer (Android). - iOS — macOS with Xcode and an Apple ID signed into it — see Set up your computer (iOS).
- Android —
The Appium server is bundled with the CLI;
aximo mobile setupinstalls the UiAutomator2 (Android) and/or XCUITest (iOS) driver — you don't install or manage Appium yourself.
Set up your computer (Android)
You only need adb and the Android SDK on the machine running aximo mobile connect. Appium delegates to adb, and its UiAutomator2 driver needs the SDK (even though adb is already on PATH).
macOS
# Option A — standalone SDK Platform-Tools (no Android Studio): download the zip from
# https://developer.android.com/tools/releases/platform-tools and unzip it so adb ends up at
# ~/Library/Android/sdk/platform-tools/adb (the driver needs this SDK layout, not just adb on PATH)
# Option B — full Android Studio (also installs the SDK):
brew install --cask android-studio
# Point ANDROID_HOME at the SDK (both options above use this location):
echo 'export ANDROID_HOME="$HOME/Library/Android/sdk"' >> ~/.zshrc
echo 'export PATH="$PATH:$ANDROID_HOME/platform-tools"' >> ~/.zshrc
source ~/.zshrcLinux
# Debian/Ubuntu — adb from the distro (or download platform-tools from Google):
sudo apt-get install -y android-tools-adb
# Install the SDK via Android Studio or sdkmanager, then export its path:
echo 'export ANDROID_HOME="$HOME/Android/Sdk"' >> ~/.bashrc
echo 'export PATH="$PATH:$ANDROID_HOME/platform-tools"' >> ~/.bashrc
source ~/.bashrcWindows
- Install Android Studio (bundles the SDK + platform-tools), or download the standalone SDK Platform-Tools zip from Google.
- Set
ANDROID_HOMEto the SDK path (e.g.%LOCALAPPDATA%\Android\Sdk) and add%ANDROID_HOME%\platform-toolstoPATHvia System Properties → Environment Variables.
aximo mobile doctorauto-detects the SDK at the standard locations above, soANDROID_HOMEis optional when you use the defaults — but setting it explicitly is the most reliable. Confirmadbworks withadb version.
Set up your computer (iOS)
iOS local devices are macOS-only (device control delegates to Xcode's toolchain). Setup is two steps — no env vars:
# 1. Xcode (full app, from the App Store) + accept the license:
sudo xcodebuild -license accept
# 2. Sign into Xcode with your Apple ID and create a development certificate:
# Xcode → Settings → Accounts → + (your Apple ID)
# → Manage Certificates… → + → Apple DevelopmentEverything else is discovered automatically at connect/install time:
- Signing identity & Team ID — read from the keychain (
security find-identity); noAXIMO_IOS_TEAM_IDneeded. On a machine with several teams, setAXIMO_IOS_TEAM_ID(or MobileLink'sXCODE_TEAM_ID) to pick one. - Provisioning profiles — scanned from disk and matched against the device; when none fits, the CLI mints one via Xcode automatic signing (first run takes a minute or two; requires the Apple ID signed into Xcode).
- WebDriverAgent — on iOS 17+, a prebuilt WDA matching the bundled driver is downloaded, re-signed with your identity, and preinstalled — no Xcode build, no iOS platform download. Older devices fall back to the classic Xcode build (cached per device under
$AXIMO_HOME/ios-wda/<udid>/). A WDA bundle id is derived per team (existing*.WebDriverAgentRunnerprofiles are reused); override withAXIMO_IOS_WDA_BUNDLE_ID. - App installs —
.ipafiles that aren't signed for your device are re-signed automatically (seeaximo mobile installbelow). - Device discovery — via Xcode's
devicectl;idbis only an optional fallback (brew install idb-companion && pipx install fb-idb).
Optional overrides / opt-outs:
# export AXIMO_IOS_TEAM_ID="ABCDE12345" # pick a team on multi-team machines (else auto-detected)
# export AXIMO_IOS_SIGNING_ID="Apple Development"
# export AXIMO_IOS_WDA_BUNDLE_ID="com.<you>.WebDriverAgentRunner" # else derived/reused automatically
# export AXIMO_IOS_NO_RESIGN=1 # never re-sign the app under test
# export AXIMO_IOS_NO_PREINSTALLED_WDA=1 # force the classic Xcode build flow for WDA
# export AXIMO_IOS_DERIVED_DATA_PATH=... # pin a prebuilt WDA DerivedData (build flow)
# export AXIMO_IOS_NO_WDA_CACHE=1 # disable the WDA build cache (build flow)
# export AXIMO_IOS_SKIP_TUNNEL=1 # don't auto-start the RemoteXPC device tunnel (you manage it)Run aximo mobile doctor --platform ios to see what was discovered (identity, team, WDA id) and what's missing; failed commands include a hint naming the exact fix.
On-device steps you can't skip (Apple requires them once per phone): enable Developer Mode (Settings → Privacy & Security → Developer Mode; the phone restarts), tap Trust This Computer, and — after the first install of an app signed by your team — trust the developer certificate (Settings → General → VPN & Device Management → your "Apple Development" profile → Trust; the phone needs internet). On free/personal teams profiles expire every ~7 days; the CLI re-mints them automatically, but a re-mint resets the certificate trust, so expect that trust tap to recur weekly. Free teams are also capped at 10 App IDs per week.
On iOS 17+,
aximo mobile connectalso starts a small device tunnel that needs admin rights — it prompts once for your macOS password / Touch ID (reused on later runs; it only re-prompts when the running tunnel has no tunnel for your device — e.g. it was started before the phone was attached — and must be restarted). The tunnel deliberately keeps running afterconnectexits; stop it anytime withsudo pkill -f tunnel-creation. Devices below iOS 17 skip the tunnel entirely, and Android never uses it. If anything mentions "iOS X is not installed", runxcodebuild -downloadPlatform iOS(only the classic build flow needs it).
Enable USB debugging on the phone
Exact labels vary by manufacturer and Android version, but the flow is:
- Unlock Developer options — open Settings → About phone and tap Build number seven times until you see "You are now a developer".
- Turn on USB debugging — open Settings → System → Developer options and enable USB debugging.
- Connect and authorize — plug the phone into your computer with a data-capable USB cable, then tap Allow on the "Allow USB debugging?" prompt on the phone (tick "Always allow from this computer" to skip it next time).
- Verify the computer sees it:
The phone should appear in stateadb devices # or: aximo mobile listdevice.unauthorized→ re-accept the prompt on the phone;offline→ re-plug the cable and unlock the screen. Emulators are not supported — connect a physical phone.
Keep the phone unlocked the first time you run a session: the initial command installs Appium's UiAutomator2 helper and creates the automation session on the device.
OPPO / Realme / OnePlus (ColorOS / OxygenOS): also enable Developer options → Disable Permission Monitoring, then reboot the phone. These ROMs block the adb commands Appium depends on (hidden-API policy, the Unicode keyboard used for text input) until this is on — sessions may start, but typing and other tools fail with
WRITE_SECURE_SETTINGSerrors.aximo mobile doctordetects this lockdown (the secure settings check) and confirms the fix after the reboot. The toggle can silently reset after a system update — re-enable it if the check regresses.
First-time setup
aximo mobile setup # installs the Appium driver(s) for your platform(s)
aximo mobile doctor # checks tooling / drivers / bundled server
# Limit to one platform if you only test on one:
aximo mobile setup --platform ios # installs only the XCUITest driver
aximo mobile doctor --platform ios # checks Xcode / devicectl / discovered signingsetup installs the Appium driver(s) — UiAutomator2 for Android, XCUITest for iOS — then prints what (if anything) is still missing. doctor is the first thing to run when anything's off: for Android it reports adb and the Android SDK; for iOS it reports xcrun/Xcode, the discovered signing identity/team, and whether the iOS device tunnel is running — alongside the bundled Appium server and driver — and exits non-zero if a required check fails.
Registering and connecting
aximo mobile list # see connected phones (adb serials / iOS udids)
# Android:
aximo mobile register --name "My Pixel" --platform android # --platform defaults to android
# iOS:
aximo mobile register --name "My iPhone" --platform ios # targets the single connected iPhone
aximo mobile connect # starts managed Appium + opens the tunnel
# Ctrl+C to disconnectregister writes credentials to $AXIMO_HOME/mobile-devices/<deviceId>.json (mode 0600). It targets the single connected phone by default — pass --serial (Android) or --udid (iOS) to pick a specific one when several are attached. connect re-checks prerequisites, then — on iOS 17+ — ensures the device tunnel is running (prompting once for admin the first time), starts the managed Appium server, and opens the gateway tunnel to Aximo; it runs until Ctrl+C. If you have more than one registered phone, pass --device-id to choose which to connect.
Once connected, create a mobile session in Aximo targeting this local device — the agent routes its actions through the tunnel to the phone over Appium.
Testing your own app
Unlike Device Farm, a local phone runs the apps already installed on it. To test your own build, install it first:
aximo mobile install --app ./app-debug.apk # Android: adb install -r
aximo mobile install --app ./MyApp.ipa # iOS: auto-re-signed for the device if needed, installed via devicectlOn iOS the CLI inspects the .ipa first: if its embedded profile already covers the device it installs as-is; otherwise it re-signs it with your discovered identity (the same thing AWS Device Farm does to every upload — so the exact .ipa you upload there works here). When the bundle id has to be remapped (free teams), launches by the original bundle id are translated automatically during connect sessions, and the JSON result reports both ids. App Store downloads are FairPlay-encrypted and can't be re-signed — use a development/ad-hoc build. Opt out with AXIMO_IOS_NO_RESIGN=1.
Then create the session with the app's bundle id / package name as --bundle-id — the agent launches it on startup before the test begins:
# Android:
aximo session create --platform ANDROID --user-device-id DEVICE_ID --bundle-id com.example.app \
--project-id PROJECT_ID --title "Smoke" --prompt "Sign in and open settings"
# iOS:
aximo session create --platform IOS --user-device-id DEVICE_ID --bundle-id com.example.MyApp \
--project-id PROJECT_ID --title "Smoke" --prompt "Sign in and open settings"Managing devices
aximo mobile unregister # remove the locally registered device (server-side + local creds)
aximo mobile unregister --device-id DEVICE_ID # target a specific device when several are registeredFlags reference
| Command | Flags |
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
| aximo mobile list | (none) |
| aximo mobile setup | --platform android \| ios (optional; installs both drivers by default) |
| aximo mobile doctor | --platform android \| ios (optional; checks both by default) |
| aximo mobile register | --name <name> (required), --platform android \| ios (default android), --serial/--udid |
| aximo mobile install | --app <path-to-.apk\|.ipa> (required), --platform android \| ios (inferred), --serial/--udid |
| aximo mobile connect | --device-id <id> (required only if multiple devices are registered) |
| aximo mobile unregister | --device-id <id> (defaults to the locally registered device) |
MCP server
aximo mcp runs a Model Context Protocol server over stdio, exposing your recorded sessions, screenshots, and test cases to MCP-capable agents (Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, …). The motivating use: let an agent read a finished session and generate a Playwright spec without you piping JSON between commands.
It reuses the same credentials as every other command, so run aximo configure (or set AXIMO_API_KEY) first.
Claude Code — one command:
claude mcp add aximo -- aximo mcpOther clients — point them at the aximo mcp stdio command. Claude Desktop, Cursor, and Windsurf use the mcpServers key:
{
"mcpServers": {
"aximo": { "command": "aximo", "args": ["mcp"] }
}
}VS Code (Copilot agent mode) uses the same entry under a servers key in .vscode/mcp.json.
The server exposes 29 tools: 17 read-only ones for inspecting sessions, cases, plans, plan runs, devices, and uploads (aximo_get_session_timeline is the load-bearing one for Playwright export; aximo_list_plans and aximo_get_plan cover plan discovery; aximo_list_plan_runs and aximo_get_plan_run cover run-status polling; aximo_list_devices, aximo_list_user_devices, and aximo_list_uploads supply the device and upload tokens that case configs and run overrides take), 10 that author content (create, update, archive, and delete cases — including a case's per-target device configuration — create, update, and archive plans, save a session as a case), and 2 that trigger runs (aximo_run_case starts a case run and returns the sessionId; aximo_run_plan starts a plan run and returns the planRunId — both spend organization credits and require the case or plan to be apiEnabled). See the MCP reference for per-client config paths, the full tool table, the Playwright export prompt, and a no-client smoke test.
Agent Skills
Ships with an agent skill. Once installed, the agent discovers commands via aximo schema and follows workflows in skills/aximo-cli/.
npx skills add autifyhq/aximo-cliSee commands reference and workflows for the agent-oriented docs.
Contributing
Requirements
Preparation
pnpm install
pnpm run cli -- health checkRelease Process
Automated via release-please:
- Merge commits to
mainwith Conventional Commit messages - release-please creates/updates a Release PR with CHANGELOG.md
- Merge the Release PR to publish a GitHub Release and npm package
