A modern Appium recorder and automation CLI with real-time UI visualization, session navigation commands, and selector ranking. Built with Bun, Solid.js, and Kobalte.
- π¬ Session Recording: Intercepts and logs Appium commands (currently focused on
POST /session/:sessionId/*) - πΈ Screenshot Capture: Automatically captures screenshots after actions
- π€ JSON-First Command Mode: Scriptable subcommands for agent workflows (
session,screen,selectors,drive) - π§ CLI App Navigation: Drive taps, typing, back navigation, and swipe gestures via Appium
- π Best Selector Ranking: Heuristic ranking with strategy scoring + match counts + reason codes
- π Element Inspector: Interactive element inspection with multiple locator strategies
- π― Query Tester: Test locators in real-time on captured screenshots
- π Real-time Updates: Live dashboard with Server-Sent Events
- π¨ Modern UI: Beautiful dark theme with vibrant accents using Solid.js + Kobalte
- β‘ Fast: Built with Bun for optimal performance
- π οΈ Interactive CLI: Beautiful prompts for easy configuration
Start the Appium server (CORS is only needed if you plan to call Appium directly from a browser):
appium --port 4723 --allow-corsThe recorder proxy itself does not require --allow-cors for normal usage.
npm i -g appium-session-recorderappium-recorderThe CLI will interactively prompt you for:
- Proxy port (default: 4724)
- Proxy host (default: 127.0.0.1)
- Appium server URL (default: http://127.0.0.1:4723)
Alternatively, use command-line arguments:
appium-recorder --port 8080 --appium-url http://192.168.1.100:4723Or run command mode (JSON output by default):
appium-recorder session create --appium-url http://127.0.0.1:4723 --caps-file ./caps.json --prettyPoint Appium Inspector to the recorder proxy:
| Setting | Value |
|---|---|
| Remote Host | 127.0.0.1 |
| Remote Port | 4724 (or your configured port) |
| Remote Path | / |
Open your browser to:
http://localhost:4724/_recorder
This project is intended for local testing and development. It runs an unauthenticated proxy/UI that can forward commands to Appium. For normal usage, keep it bound to localhost and do not expose it to untrusted networks.
Current known gaps (will be addressed in future updates):
- Missing validation for
sessionId - Missing validation for
appiumUrl - Rate limiting for API endpoints
- Authentication/authorization (likely optional, since local-by-default is intentional)
Practical guidance:
- Run the recorder on
127.0.0.1only (default) and avoid port-forwarding/sharing the port. - Be cautious with
appium --allow-cors; treat the recorder + Appium as a local dev surface while running. - Page source is untrusted input; XML is rendered as text (not HTML) to mitigate XSS.
appium-recorder <group> <command> [flags]Available command groups:
proxy startsession create|deletescreen snapshot|elementsselectors bestdrive tap|type|back|swipe
Global command flags:
--prettypretty-print JSON--output <path>persist JSON output to a file- Legacy mode (
appium-recorder [legacy-options]) does not accept--prettyor--output
This repo includes a reusable Codex skill at:
skills/appium-cli-selector-navigator/SKILL.md
The skill is designed for CLI-only navigation + selector discovery loops.
Use this when you want to drive the app and find selectors with $appium-cli-selector-navigator.
- Start Appium:
appium --port 4723 --allow-cors- (Optional) Install skill globally for Codex:
mkdir -p ~/.claude/skills
cp -R ./skills/appium-cli-selector-navigator ~/.claude/skills/appium-cli-selector-navigatorRestart Claude after installing the global skill.
- Create a capabilities file (example iOS Safari):
cat > /tmp/caps.json <<'JSON'
{
"platformName": "iOS",
"appium:deviceName": "iPhone 17",
"appium:platformVersion": "26.2",
"appium:automationName": "XCUITest",
"appium:bundleId": "com.apple.mobilesafari",
"appium:newCommandTimeout": 3600,
"appium:connectHardwareKeyboard": true,
"appium:includeSafariInWebviews": true
}
JSON- Create session and capture
SESSION_ID:
appium-recorder session create \
--appium-url http://127.0.0.1:4723 \
--caps-file /tmp/caps.json \
--output /tmp/session.json \
--pretty \
&& SESSION_ID="$(jq -r '.result.sessionId' /tmp/session.json)" \
&& echo "$SESSION_ID"- Fetch current elements and save JSON:
appium-recorder screen elements \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" \
--only-actionable \
--limit 80 \
--output /tmp/elements.json \
--pretty \
&& jq -r '.result.elements[] | [.elementRef, .type, .name, .label, .resourceId] | @tsv' /tmp/elements.json- Pick element and get best selectors:
appium-recorder selectors best \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" \
--element-ref "<ELEMENT_REF>" \
--pretty- Drive actions (examples):
# Tap an element
appium-recorder drive tap \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" \
--using "accessibility id" --value "Accept all" --pretty
# Type into a field
appium-recorder drive type \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" \
--using "accessibility id" --value "Address" \
--text "example.com\n" --clear-first --pretty
# Navigate back
appium-recorder drive back \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" --pretty
# Swipe gesture
appium-recorder drive swipe \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" \
--from 900,1800 --to 900,400 --duration-ms 350 --pretty- Cleanup session:
appium-recorder session delete \
--appium-url http://127.0.0.1:4723 \
--session-id "$SESSION_ID" --prettyappium-recorder [options]
OPTIONS:
-p, --port <number> Proxy server port (default: 4724)
-u, --appium-url <url> Appium server URL (default: http://127.0.0.1:4723)
--host <host> Proxy server host (default: 127.0.0.1)
-h, --help Show help message
-v, --version Show versionConfiguration priority (highest to lowest):
- Command-line arguments
- Interactive prompts
- Environment variables
- Default values
APPIUM_URL=http://192.168.1.100:4723
PROXY_PORT=8080
PROXY_HOST=127.0.0.1
appium-recorder- Total Requests: Count of all intercepted requests
- Actions: Requests with screenshots (clicks, inputs, etc.)
- Real-time Updates: Automatically refreshes as you interact
- View all interactions in chronological order
- Color-coded by HTTP method (mostly
POST) - Action markers for requests with screenshots
- Click screenshots to open inspector
- Query Tester: Test different locator strategies
- accessibility id
- xpath
- class name
- iOS predicate string
- iOS class chain
- Element Details: View element properties (name, label, value, bounds)
- Locators: Auto-generated locators ready to copy
- Click to copy: One-click locator copying
git clone https://github.com/JustasMonkev/appium-session-recorder.git
cd appium-session-recorder
bun installbun run cliThis is equivalent to appium-recorder but runs directly from source. All examples in this README that use appium-recorder can be replaced with bun run cli during development.
cd src/ui
bun run build# Terminal 1: Build UI in watch mode
cd src/ui
bun run dev
# Terminal 2: Run CLI
bun run clibun run test # Run all tests
bun run test:watch # Watch mode
bun run test:coverage # With coveragebun run buildThis builds both the UI and the CLI executable.
The recorder captures:
- Element clicks
- Text input (value)
- Element clear
- Find element/elements
- Touch actions
- Navigation (back, forward, refresh)
For each action:
- β Request details (method, path, body)
- β Screenshot (base64)
- β Page source (XML)
- β Timestamp
- Test Debugging: Review session history to debug failing tests
- Element Discovery: Find reliable locators for automation
- Training: Show team members how to interact with the app
- Test Recording: Generate test scripts from recorded interactions
βββββββββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββββββββββ
β Appium Inspector β ββββ β Session Recorder β ββββ β Appium Server β
β (localhost:4724) β β (Bun + Express) β β (localhost:4723) β
βββββββββββββββββββββββ βββββββββββββββββββββββ βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β Web UI (Solid.js) β
β Real-time Updates β
βββββββββββββββββββββββ
