Skip to main content
This page documents the current command surface exactly as implemented. If you run visor --help, visor -h, or invoke the CLI with no command, visor prints a usage summary instead of a JSON response envelope. visor --version and visor -v print the package version instead of a JSON response envelope.

Global runtime inputs

These inputs are accepted globally and, where relevant, by individual commands.
InputMeaning
deviceDevice or simulator identifier
timeoutPer-step timeout budget in milliseconds for scenario runs
outputBase output directory for run artifacts and reports
formattext or json
server-urlAppium server URL
app-idAndroid app package or iOS bundle identifier; on iOS it must match an installed app on the selected target
attachAttach to an already running app process when possible
no-mapDisable app-map reads and writes for this command
repairAllow opt-in exploratory app-map repair after a missing or stale route
Important notes:
  • format is currently informational only for normal commands; the implementation still returns JSON envelopes
  • visor --help, visor -h, and no-command invocation print plain-text usage instead of JSON
  • visor --version and visor -v print the package version instead of JSON
  • platform is inferred from the selected device; --platform is not accepted

Mapped execution

Mapped execution is enabled by default for direct action commands and scenario runs. Visor observes lightweight UI source around target actions, stores a schema-versioned host-machine app map under .visor/maps, and can reuse confirmed navigation edges when a later command targets a known screen from a different current screen. Use --no-map or VISOR_NO_MAP=true when you need raw Appium behavior. No-map mode disables both map reads and map writes. Exploratory repair is off by default. Use --repair when you want Visor to try safe controls after a missing or stale route instead of failing fast. Every mapped direct action returns data.map. Mapped target, coordinate, and scroll actions also return data.observation with before/after fingerprints, screen_changed, previous/current screen ids, and visible_text_count. Scenario runs return data.run.map and per-step map details under data.run.steps[].details.map when a step used routing, repair, or map observation.

Direct action commands

tap

Purpose: click an element or coordinate. Inputs:
  • target mode: target
  • coordinate mode: x, y
  • optional: normalized
  • optional post-action wait: --wait-for, --timeout, and --poll-ms to wait for a selector after the tap
Returns action payload fields:
  • action
  • platform
  • args
args contains either:
  • target, or
  • x, y, and normalized
Coordinate x and y values are screen points by default. With normalized, they are fractions of the current screen width and height. Coordinate taps work as direct commands and as scenario steps. Target taps report args.tap_method; when Visor can resolve an element rectangle, it taps the center coordinate and returns the resolved x and y. Examples:
visor tap --target accessibility=Continue
visor tap --target text=Continue
visor tap --target text~=Settings
visor tap --target "first-in-section=Featured products"
visor tap --x 120 --y 640
visor tap --x 0.5 --y 0.92 --normalized
Purpose: move to a destination. Required input:
  • to
Returns action payload fields:
  • action
  • platform
  • args.to
Current behavior:
  • the destination is passed to WebdriverIO through url(to)

act

Purpose: perform a helper action. Supported variants:
  • name=type with target and value
  • name=type with value only, which types into the active field
  • name=back
  • name=home
  • name=reset
  • name=drag with start-x, start-y, end-x, and end-y
  • name=slider with target, value from 0 to 1, and optional start-value
drag also accepts normalized so coordinates can be fractions of the viewport. reset requires app-id because Visor restarts the target app. Optional post-action wait inputs:
  • --wait-for
  • --timeout
  • --poll-ms
--wait-for polls for a selector after the helper action completes. --timeout and --poll-ms control that post-action wait. Returns action payload fields:
  • action
  • platform
  • args

scroll

Purpose: perform a page-style vertical scroll. Required input:
  • direction with value up or down
Optional input:
  • percent, default 70
  • post-action wait: --wait-for, --timeout, and --poll-ms
--wait-for polls for a selector after the scroll completes. --timeout and --poll-ms control that post-action wait. Returns action payload fields:
  • action
  • platform
  • args.direction
  • args.percent
Current behavior:
  • Android uses Appium scroll gestures within the current viewport
  • iOS attempts the same gesture path and falls back to a touch swipe when needed

screenshot

Purpose: capture a PNG image. Optional input:
  • label
  • path
  • settle or settle-ms
Returns action payload fields:
  • action
  • platform
  • args.label
  • args.file
  • args.path
  • args.width
  • args.height
If label is omitted, the runtime defaults it to capture. If path is provided, Visor writes the PNG to that path. Otherwise it writes <label>.png in the current working directory for direct Appium actions, or under --output when that directory is supplied. Direct command responses include the screenshot path in both data.args.path and the top-level artifacts array. settle waits briefly before capture; settle-ms controls the wait duration. Examples:
visor screenshot --label checkout
visor screenshot --path /tmp/checkout.png

wait

Purpose: pause, wait for a selector, or wait for source stability. Inputs:
  • ms
  • for with optional timeout and poll-ms
  • stable with optional timeout and poll-ms
Returns action payload fields:
  • action
  • platform
  • args.ms, or selector/stability wait result fields
wait --for <selector> polls until the selector exists. wait --stable polls UI source until two consecutive captures match. If either predicate times out, the command or scenario step fails and preserves the wait payload with matched: false.

source

Purpose: capture current UI source as XML. Optional inputs:
  • label
  • path
  • settle or settle-ms
Returns action payload fields:
  • action
  • platform
  • args.label
  • args.file
  • args.path
  • args.format
  • args.bytes
For direct action commands, run visor start first. For direct screenshot and source, a returned args.path is also surfaced as a top-level response artifact. --output <dir> writes the capture under that directory when path is not provided.

discover

Purpose: observe the current screen and update the shared app map without running a scenario action. Optional runtime inputs:
  • device
  • server-url
  • app-id
  • attach
  • no-map
  • crawl
  • crawl-depth
  • crawl-limit
  • crawl-include
  • crawl-allow-risky
Use --crawl to explore safe controls from the current screen and record app-map edges. Crawling is bounded by --crawl-depth and --crawl-limit. Use --crawl-include <text> and --crawl-allow-risky on sandbox/dev builds when you intentionally want the crawler to include otherwise risky controls such as delete, logout, purchase, or confirm surfaces. Response data fields:
  • action
  • map
  • map.summary, when the persisted map can be read
  • screen
  • crawl, when --crawl is used
map.summary is a path-free inventory of the persisted app map for review: schema version, identity, app id, platform, screen count, variant count, edge count, authentication-required variant count, and last update time. Persisted variants include source-derived items for review and replay tooling, plus exit_recipes for visible back/close/dismiss/done controls when coordinates are available.

record

Purpose: start or stop a named replay flow. Required input:
  • name
Optional input:
  • stop
  • force
  • record-values
visor record <name> creates .visor/flows/<name>.json and marks it active. Successful direct action commands append ordered steps to every active flow. visor record <name> --stop closes the flow so it can be replayed. Recording is privacy-preserving by default: typed act --name type values are redacted from the flow, and map, observation, and screen metadata are not persisted into .visor/flows/*.json. Use --record-values only when you intentionally want typed values saved for replay. Starting a recording fails if a flow with the same name already exists; pass --force to overwrite it.

replay

Purpose: run a recorded flow through the normal scenario runner. Required input:
  • name
Optional runtime inputs:
  • device
  • timeout
  • output
  • server-url
  • app-id
  • attach
  • no-map
  • repair
  • param as key=value
replay converts recorded steps into a generated scenario and runs it through the Appium-backed daemon path. --param query=shoes substitutes {{query}} placeholders in recorded args before execution. Replay responses list substituted names in data.param_keys and do not echo parameter values.

Scenario commands

validate

Purpose: parse a scenario and return validation issues. Required input:
  • scenario
Response data fields:
  • valid
  • issues
Exit behavior:
  • returns success when the scenario is valid
  • returns a non-zero exit code with status: ok when the scenario is syntactically valid JSON but fails schema validation
  • returns a failure envelope when parsing fails or the file cannot be read

run

Purpose: execute one scenario, evaluate assertions, and write reports. Required input:
  • scenario
Optional runtime inputs:
  • device
  • timeout
  • output
  • server-url
  • app-id
  • attach
  • runtime, with values appium or local
  • no-map
Response data fields:
  • run
  • warnings
The run object contains the full run result, including step results, assertion results, artifacts, determinism signature, and run-level error data. For Appium-backed scenario runs, run visor start first. For deterministic local development checks, pass --runtime local. This runs the scenario against Visor’s local fixture runtime, writes normal reports and artifacts, and does not require Appium, a daemon, an emulator, a simulator, or a real app. When mapped execution is enabled, the run also includes map metadata and any hidden route or repair decisions in step details.

benchmark

Purpose: run one scenario repeatedly and compute a determinism score. Required input:
  • scenario
Optional inputs:
  • runs with default 20
  • threshold with default 95.0
  • device
  • timeout
  • output
  • server-url
  • app-id
  • attach
  • compare-map
Response data fields:
  • runs
  • threshold
  • determinismScore
  • pass
  • failures
  • runIds
  • warnings
  • variants when compare-map is enabled
Benchmark runs are Appium-backed. They do not accept --runtime local; use run with --runtime local for deterministic local harness checks. Use --compare-map to run the same scenario twice per iteration group: once with app-map reads and writes disabled, and once with app-map enabled. Each entry in variants includes name, mapEnabled, runs, threshold, determinismScore, pass, failures, and runIds. For benchmark runs, run visor start first.

report

Purpose: return guidance about where run reports are stored. Optional input:
  • path, which defaults to artifacts
Response data fields:
  • message
  • path
  • format

Runtime lifecycle commands

start

Purpose: start the Visor daemon and ensure Appium is reachable. Optional inputs:
  • server-url
  • appium-cmd
Response data fields:
  • serverUrl
  • daemon
  • appium
daemon includes process and diagnostic metadata such as running, pid, socketPath, metadataPath, logPath, packageVersion, runtimeVersion, currentPackageVersion, and stale. When visor start finds an already-running daemon whose package version differs from the current CLI package version, or whose package version is unknown, daemon.stale is true and the response includes daemon.warning and daemon.nextAction.

status

Purpose: report Visor daemon state, Appium reachability, and warm sessions. Optional inputs:
  • server-url
Response data fields:
  • serverUrl
  • daemon
  • appium
daemon.packageVersion and daemon.runtimeVersion describe the running daemon process. daemon.currentPackageVersion describes the CLI process that produced the status response. If those package versions differ, or the daemon does not report a package version and is shown as unknown, daemon.stale is true, daemon.warning explains the mismatch, and daemon.nextAction is restart. Visor reports this state but does not automatically restart the daemon.

stop

Purpose: close warm sessions and stop the Visor daemon. Optional inputs:
  • server-url
  • force
Response data fields:
  • serverUrl
  • stopped
  • daemon
  • appium

Response envelope behavior

Every command returns the same top-level envelope fields:
  • status
  • command_id
  • started_at
  • ended_at
  • artifacts
  • next_action
  • error
  • data
next_action is a hint, not an execution chain. Examples include run, report, and none. Help and version output are the exceptions: visor --help, visor -h, and no-command invocation print usage text instead of an envelope, while visor --version and visor -v print the package version.