Skip to content

JavaScript API

Repeato allows you to execute JavaScript using Script steps. The following globals are available while a test or batch is running.

For machine-readable documentation, use the raw Markdown reference or the TypeScript declarations. See JavaScript recipes for complete, executable examples.

Type: object

An object that you can use to share data between steps.

Type: string

The string scanned by the last executed Check for text step.

Type: string

The directory of the current workspace.

Type: number

The zero-based index of the currently executing step.

Type: string

The directory of the current test.

Type: string

The directory of the current test run.

Type: AxiosInstance

The Axios HTTP client.

const response = await axios.get('https://example.com')
Signature
log(message: object)

Logs a message or object.

Signature
sleep(milliseconds: number): Promise<void>

Delays execution for the given number of milliseconds.

Signature
getResponse(host: string, path: string, headers?: object): Promise<string>

Sends a GET request to a host.

Signature
sendPost(
host: string,
path: string,
body: string | object,
acceptHeader?: string,
headers?: object,
): Promise<string>

Sends a POST request.

Signature
showDialog({
title,
content,
onConfirm,
onClose,
}: {
title: string
content: string
onConfirm?: () => void
onClose?: () => void
}): { cancel: Function; confirm: Function }

Shows a dialog whose content supports Markdown, including formatted text, links, and images. The optional callbacks run when the corresponding action occurs. The returned dialog instance can be closed using cancel() or confirm().

Controls the current test batch and provides batch lifecycle hooks.

Type: TestBatch

The current batch of tests.

Type: number

The index of the current exception handler.

Type: number

The index of the current test.

Type: BatchRun

The current batch run. Use log(batchRunner.batchRun) to inspect all properties.

Type: boolean

Indicates whether the batch runner is currently running.

Type: string

The current run mode.

stop(): Promise<void>

Stops the batch runner.

Signature
createBatchRunExport(): Promise<string>

Creates an export of the current batch run in the workspace’s batchRuns folder and returns its path.

Signature
uploadReport(dirPath: string): Promise<string>

Uploads the report at dirPath to the Repeato cloud and returns the report URL.

Signature
batchRunner.setTestBatchById(batchId: string)

Sets the test batch for the batch runner.

Signature
batchRunner.setRunMode(mode: string)

Sets the run mode to all tests or failed tests only.

batchRunner.setRunMode('AllTests')
batchRunner.setRunMode('OnlyFailed')
Signature
batchRunner.addOnBatchStart(key: string, callback: (batch: Batch) => void)

Registers a callback that runs just before the batch starts.

Signature
batchRunner.addOnTestFail(
key: string,
callback: (test: Test, testRun: TestRun, stepResult: StepResult) => void,
)

Registers a callback that runs when a test fails.

Signature
batchRunner.addOnTestSuccess(
key: string,
callback: (test: Test, testRun: TestRun) => void,
)

Registers a callback that runs when a test succeeds.

Signature
batchRunner.addOnBatchCompleted(
key: string,
callback: (batchRun: BatchRun) => void,
)

Registers a callback that runs when the batch completes, whether it succeeds or fails.

Signature
batchRunner.removeOnBatchStart(key: string)

Removes a callback registered with addOnBatchStart().

Signature
batchRunner.removeOnTestFail(key: string)

Removes a callback registered with addOnTestFail().

Signature
batchRunner.removeOnTestSuccess(key: string)

Removes a callback registered with addOnTestSuccess().

Signature
batchRunner.removeOnBatchCompleted(key: string)

Removes a callback registered with addOnBatchCompleted().

Type: TestRunner

Provides access to all testRunner properties and methods. Use log(batchRunner.testRunner) to inspect them.

Controls execution of the current test and exposes step lifecycle hooks.

Type: Test

The current test. Use log(testRunner.currentTest) to inspect all properties.

Type: TestRun

The current test run. Use log(testRunner.currentTestRun) to inspect all properties.

testRunner.currentTestRun.partial is a synchronous, read-only boolean on all platforms in Script steps and Advanced configuration callbacks. It is true for single-step and Run to here execution, with no device interaction or other side effects. Partial runs keep their step results but cannot be successful or become reference runs; wasSuccessful remains false. Older saved runs default to partial: false.

Type: StepResult

The result of the previously executed step. Use log(testRunner.lastStepResult) to inspect all properties.

In Script steps and Advanced configuration callbacks, step.semanticAgreement is a synchronous, read-only click configuration flag (false by default; absent for other step types). Configure it through Studio’s click editor or MCP. Enabled clicks require a fresh hierarchy with a unique visible semantic target containing the proposed tap point; failure rejects the tap, and devices without usable hierarchy/locators cannot establish agreement.

stepResult.semanticAgreement is a synchronous read with no side effects. It is present when the guard evaluated a target and contains agreed, reason, candidateCount, and optional target bounds, resolved locators, or an ambiguous locatorType. Bounds use normalized coordinates from 0 to 1. Missing evidence does not mean agreement passed. This records geometric agreement, not actual event delivery or business outcomes.

Inspect the preceding click's evidence
const agreement = testRunner.lastStepResult?.semanticAgreement
if (agreement) log(agreement)

In Script steps and Advanced configuration, stepResult.executionEvidence is a synchronous, read-only optional property available on newly executed matching/click results before step-completed callbacks. It returns stored execution facts or undefined on older results, with no device side effects. It includes the effective searchedRegion, requestedLocators, matchSource, optional resolvedLocator, matchedBounds, actual attempted tapPoint, and dispatch timestamps/status. Bounds and tap points use normalized full-frame coordinates from 0 to 1; source-frame width/height are processed image pixels. Coordinate clicks have no search region or resolved element bounds.

sourceFrame identifies a screenshot associated with the result. Its phase distinguishes the exact worker-input matching_frame from a screenshot read after_locator_resolution (which is not the hierarchy used to resolve the locator). receivedAt is an ISO client receive/read timestamp, not a provider capture timestamp; matchedAt records worker completion when known. Screenshots are filenames relative to the normal run directory, retrieved through the run’s screenshot API or MCP artifact metadata. Missing fields remain null. Matching buffers are not stored inline in the result.

tapStatus is not_dispatched, dispatching, completed (the driver command resolved), or unknown (a command rejected after dispatch may have partially applied). A completed dispatch does not prove event delivery or the desired app outcome. A matching frame is historical; the physical screen can change before a tap. No automatic retry is performed on an uncertain dispatch.

stepResult.warnings is a read-only array of warning codes. A successful locator fallback retains success: true and adds fingerprint_fallback_used; warnings no longer suppress its tap. Optional failed steps can still have success: undefined. Locator-derived bounds use their center without applying a recorded fingerprint offset; fingerprint/OCR clicks retain their configured offset behavior.

Inspect execution facts without taking a new screenshot
const result = testRunner.lastStepResult
if (result?.executionEvidence) {
log(result.executionEvidence)
log(result.warnings)
}

In Script steps and Advanced configuration, stepResult.failureEvidence is an optional, read-only persisted value. Reading it is synchronous, returns the stored evidence or undefined, and has no device side effects. Studio captures it after failed steps (including optional failures), after addOnStepCompleted callbacks finish. Those callbacks do not yet see this capture; later Script steps or test-failure callbacks can inspect it when execution continues. Successful and older results may omit it.

failureEvidence.hierarchy contains status (captured, unavailable, or capture_failed), data (the full JSON-serializable driver hierarchy or null), and capturedAt (an ISO timestamp or null). Capture requires a connected device with hierarchy support and has a three-second deadline. Missing hierarchy support, disconnection, timeout, or serialization errors do not fail the test further. Capture occurs after the failed action, separately from its screenshot. An empty hierarchy is distinct from unavailable data.

Evidence also includes classification, search stages when available, and capture timing. Unknown keyboard visibility, IME bounds, scroll offset, foreground package, and tap delivery remain unknown. Hierarchy attributes and bounds retain driver-specific structure and units; they are not normalized 0–1 touch coordinates. Hierarchies can contain entered text. Evidence stays in the normal run structure. Each test retains at most five runs, protecting the reference run; failed/interrupted results and screenshots remain until their run is evicted. Manual exports can keep a copy longer. MCP repeato_get_step_result returns the complete result JSON, including this hierarchy, from retained test runs.

Inspect stored failure evidence without contacting the device
const evidence = testRunner.lastStepResult?.failureEvidence
if (evidence?.hierarchy.status === 'captured') {
log(evidence.hierarchy.data)
}

Type: number

The zero-based index of the current step.

Signature
testRunner.setNextStepId(id: string)

Sets the next step to execute by ID.

testRunner.setNextStepId('AH23D6')
Signature
testRunner.setNextStepIndex(index: number)

Sets the next step to execute by its zero-based index.

Signature
testRunner.goBackBy(stepCount: number)

Moves back by stepCount steps and continues execution from there.

Signature
testRunner.addOnScreenshotSaved(
key: string,
listener: (stepResult: StepResult, screenshotPath: string) => void,
)

Registers a listener that runs as soon as a screenshot has been saved.

Signature
testRunner.addOnStepCompleted(
key: string,
callback: (stepResult: StepResult) => void,
)

Registers a callback that runs whenever a step completes.

Signature
testRunner.removeOnScreenshotSaved(key: string)

Removes a listener registered with addOnScreenshotSaved().

Signature
testRunner.removeOnStepCompleted(key: string)

Removes a callback registered with addOnStepCompleted().

testRunner.setScaleInvariantMatchingEnabled()

Section titled “testRunner.setScaleInvariantMatchingEnabled()”
Signature
testRunner.setScaleInvariantMatchingEnabled(enabled: boolean)

Enables or disables scale-invariant matching. It is enabled by default.

Provides information about and sends commands to the currently connected device.

Type: DeviceData

All data related to the currently connected device. Use log(deviceConnector.selectedDeviceData) to inspect it.

Signature
sendString(value: string): Promise<void>

Sends a string to the device. The app under test must have a focused input field to receive the data.

Signature
clearText(): Promise<void>

Available in Script steps and Advanced configuration with a connected device and a focused input. Asynchronously clears that field and resolves with no value; it does not type replacement text.

Cloud Android uses the input’s long-press selection menu and Select all, followed by Backspace and an empty-field check. It requires one inspectable focused EditText and an available selection control; no ADB is used. Cloud iOS clearing is unsupported. Missing focus, unavailable controls, unsupported devices, and provider timeouts reject the promise. A failure may leave input or selection state changed, so inspect the screen before retrying.

Usage after focusing an input
await deviceConnector.clearText()
await deviceConnector.sendString('Replacement text')
Signature
sendClick(xPercentage: number, yPercentage: number): Promise<void>

Sends a touch event to the device. Coordinates range from 0 to 1; sendClick(0.5, 0.5) taps the center of the screen.

Signature
sendDown(xPercentage: number, yPercentage: number): Promise<void>

Sends a touch-down event. Each sendDown() needs a sendUp(); combine it with sendMove() to simulate a drag gesture.

Signature
sendMove(xPercentage: number, yPercentage: number): Promise<void>

Sends a move event. Use sendDown() first and finish the drag with sendUp().

Signature
sendUp(xPercentage: number, yPercentage: number): Promise<void>

Sends a touch-up event.

Signature
sendAdbCommand(command: string): Promise<string>

Sends an Android Debug Bridge (ADB) command.

Signature
deviceConnector.addOnDeviceLog(
listenerName: string,
callback: (log: string) => void,
)

Registers a callback that runs for every log line sent by the app under test.

Signature
sendIdbCommand(command: string): Promise<string>

Sends an iOS Development Bridge (IDB) command.

await deviceConnector.sendIdbCommand('list-targets')
Signature
takeScreenshot(filepath: string): Promise<void>

Takes a screenshot and saves it at filepath.

Signature
setClipboard(content: string): Promise<void>

Sets the device clipboard to a string.

getClipboard(): Promise<string>

Gets the device clipboard content.

Signature
vision(
prompt: string,
region: { x: number; y: number; width: number; height: number } | undefined,
): Promise<string>

Sends a prompt and a screenshot of the current device screen to the vision module. Pass a region to limit the screenshot to part of the screen.

Provides text-to-speech, audio playback, and audio recording.

Signature
audioTools.say(text: string)

Turns the given text into audio.

Signature
playAudio(url: string): Promise<number>

Plays audio from the given URL.

audioTools.stopAudio()

Stops the currently playing audio.

Signature
recordAudio(duration: number): Promise<string>

Records audio for the given duration in milliseconds and returns the URL of the temporary recording.

testRunner.lastStepResult.executionEvidence is a synchronous read-only snapshot. Its optional ocr object retains the latest completed recognizedText, including when comparison fails, along with the engine, expected text, operator, completed-frame count and completion time. Status distinguishes no_frame_processed, no_completed_frame, engine_error, no_text, no_match and matched. Missing completed output is null, while completed recognition of no text is an empty string. The source-frame reference identifies the inspected screenshot when available. Late worker completion does not update an already returned timeout result. OCR settling consumes at most one quarter of its search budget or one second, leaving the remaining time for recognition. OCR timing and recognition accuracy depend on the engine and device content.

executionEvidence.locatorResults records hierarchy locator candidates and rejection reasons. Native hierarchy fallbacks require one enabled actionable control inside the configured normalized search region; static labels, ambiguous controls and known keyboard-covered tap points are rejected. Android supports an exact hint locator. Missing keyboard evidence remains unknown. Matching and completed input dispatch do not prove the app outcome.

Text steps can configure expectedFocusHint in Studio or MCP. Before clearing or typing during recording and replay, this optional guard asynchronously reads the device hierarchy and requires exactly one focused editable field with that exact hint. It fails before text mutation when the hierarchy is unavailable or focus disagrees. It does not change focus. step.expectedFocusHint itself is a synchronous read of the configured expectation. Native Android exposes hints where supplied by the app/provider; other platforms may not expose them.

In Script steps and Advanced configuration, Navigation steps expose synchronous read-only goal, startCondition, inputReferences and maxActions properties. Other step types omit them. The optional starting condition must be verified before any action. Input references select existing data fields. Navigation sends the selected names and resolved values to the provider in state.inputData and saved in decision evidence. Do not select secrets. Values typed by the runtime still come from supplied data and are not generated by the model. maxActions accepts 1–100 and defaults to 20. Navigation requires a device with hierarchy and screenshots, a configured OpenRouter key, and the supported decision model. The inherited step timeout is its total execution budget in seconds (default 60, maximum 300).

After execution, stepResult.executionEvidence.navigation is a synchronous, read-only optional object available before completion callbacks. It has no device side effects and is absent on other/older results. It contains model (the pinned resolved model), requestedModel, status, elapsedMs, actionCount, modelCalls, and trace. Each trace entry includes the operation, dynamic target number, status, elapsed milliseconds, optional goalConfidence, operationConfidence, visibilityDecision (VISIBLE/HIDDEN/UNCERTAIN), visibilityConfidence, inputReference, and the exact observation screenshot filename in the test-run directory. Target numbers apply only to that observation; they are not reusable locators. The matching evidence source is navigation; individual actions are recorded in the navigation trace rather than the top-level tap fields.

A goal judgment, timeout, cancellation, invalid starting condition, unsafe target, unstable screen, lack of progress or exhausted budget ends the step without automatically retrying the test. Goal recognition is a model judgment; retain a separate ordinary assertion for the final business outcome. Hierarchy geometry uses normalized coordinates from 0 to 1. Screenshots and visible page content are sent to the configured model provider, so use demo data for benchmarks and keep secrets out of goals.

In Script steps and Advanced configuration on Studio and CLI, testRun.stepResults is a chronological flat list. A Navigation step can produce several real results with the same step.id: informational observations followed by one final result. Read these fields synchronously; reading them has no side effects. resultKind is intermediate or final; older loaded results default to final. Optional executionId groups one execution, sequence orders its results, and elapsedMs measures milliseconds since that Navigation execution started. Intermediate executionEvidence.navigationObservation records operation, status, target and optional error/reference details. Its screenshot is the pre-action model observation, not an after-action confirmation. screenshot is an optional filename relative to the test-run directory; an unavailable image is not synthesized.

testRun.getStepResultsByStepId(stepId) synchronously returns all results for that step in recorded order, without changing the run. getStepResultByStepId(stepId) returns the latest final result or undefined before completion. getSuccessfulStepResults() includes successful final results only. testRunner.lastStepResult and step-completed callbacks still refer to the final outcome, not intermediate decisions. Intermediate results do not increase successful, failed or optional-step counts. Existing historical runs retain the original single result; the viewer does not convert legacy JSON traces into invented results.

For new Navigation observations, executionEvidence.navigationObservation.decisionInput is a synchronous, read-only snapshot of the structured model request: state, questions, model/provider and saved screenshot reference. Reading it has no side effects. It excludes authorization and inline image bytes, and is absent in older runs. Only explicitly selected inputReferences are resolved and included as the name-to-string-value map decisionInput.state.inputData. These values are transmitted to the configured navigation provider and retained in execution evidence; do not select secrets. The entire data object and unrelated fields are excluded. The screenshot can also show visible field contents. Independent expected-result assertions are not included. The Batch Run Viewer displays this snapshot in an expandable Decision input region. The final trace omits duplicate input payloads; inspect the actual intermediate results for each decision.

New intermediate Navigation results expose optional executionEvidence.navigationObservation.decisionOutput: the exact received model JSON body, saved before local response validation. It is a synchronous read-only snapshot on Studio and CLI, also available through MCP run/result tools; reading it has no side effects. No authorization headers are stored. Older results and requests without a received JSON response omit it; missing outputs are never reconstructed from the executed action. The viewer displays it with the same expandable tree as Decision input. The final trace omits duplicate input/output bodies.

Optional decisionMetrics records local requestDurationMs (fetch through JSON decoding, excluding image encoding), requestBytes (UTF-8 JSON body including base64 image, excluding HTTP headers), structuredBytes (UTF-8 serialization of model, structured state, questions and provider without the image), and imageBytes (JPEG/PNG bytes before base64). Sizes are not token usage or billed cost. Actual usage, when supplied by the provider, remains in decisionOutput. These fields share the same availability and read-only behavior as decisionOutput.

Navigation sends an incoming JPEG screenshot unchanged with its matching JPEG media type; other frames retain PNG normalization. This does not resize the image or add lossy compression. The saved observation remains the exact source frame.

Navigation completion currently requires model goal probability at least 0.70; an explicit start condition still requires 0.85 on the first decision. This shared Studio/CLI/MCP execution policy does not establish task success independently. Retain explicit outcome assertions, especially for booking, saving or expected error states. The probabilities are model judgments, not a guarantee. The completion-policy experiment and regressions are tracked in Studio’s iteration log.

Navigation TYPE_TEXT decisions choose the editable target and named data reference together in one choice. The runner validates the pair against its current choices, verifies focus and, when a focus click is needed, target visibility or driver reachability, and resolves the supplied value locally before typing. The selected reference names and expected string values are also transmitted in decisionInput.state.inputData and retained in evidence; unrelated test data is excluded. The persisted observation still exposes the candidate ID in target and the existing data path in inputReference; older model outputs used separate target and input heads. Reading this evidence is synchronous and has no side effects. Joint selection does not guarantee that the chosen field/reference pair is semantically correct.

Navigation currently accepts at most 250 locally filtered visible candidates, including static text and actionable controls. Larger observations fail explicitly without silently truncating targets. This is an execution resource limit, not a minimum number of elements or a model requirement. Existing timeout/action budgets still apply.

Set REPEATO_NAVIGATION_MODEL=gpt-6-luna and OPENAI_API_KEY before starting Studio/CLI/MCP. Jev remains the default. GPT-6 Luna uses POST https://api.openai.com/v1/decisions, with the same filtered state, original screenshot and question meanings. Legacy internal noul questions map to native predicate questions; fixed criteria map to native choice values/descriptions. No Responses JSON generation, reasoning setting, output-token limit, generated coordinates/text values or model fallback is used by this adapter. Existing cancellation/deadline, visibility, freshness, focus, budgets and independent outcome assertions remain applicable.

Raw navigationObservation.decisionOutput retains the native API body before validation. The adapter matches the answers array by each unique question name, validates types/probability ranges/allowed values, and maps predicate probability into the existing goal/start checks (0.70/0.85 unchanged). Required refusal, duplicate/missing/unknown names and invalid choices fail without dispatch. Confidence/probabilities come from the API rather than invented values. Different models’ probability calibration is not assumed equivalent. Any returned model must match gpt-6-luna; if the API omits model identity, resolved model remains null rather than being fabricated.

decisionInput.api is openai-decisions; requestOptions.questions records the exact mapped wire questions, while state and the screenshot reference reconstruct shared evidence. The Jev-only provider field is absent. Reads are synchronous and have no side effects. For GPT, structuredBytes serializes the native request with an empty image URL; image bytes are reported separately. Missing provider cost is unknown, not zero. Existing historical openai-responses reports remain unmodified and must not be labeled native Decisions runs.

The official Decisions guide documents text/image inputs and predicate/choice responses. Live model/account availability still requires a successful preflight. All execution evidence remains accessible through the existing MCP run/result tools; no separate MCP action endpoint is required.

Native Decisions preserves individual refusals or unavailable speculative heads without inventing answers: the shared executor rejects a refusal if it is required for the chosen operation, goal, applicable start or chosen target visibility. An unused refused head does not cancel another independently verified operation. Native choice arrays require at least two entries; a head with one real option includes an explicit no-applicable-choice abstention. This reserved value cannot dispatch and is retained in the raw API output; if chosen, that head is treated as unavailable.

For TYPE_TEXT, Navigation can reuse a uniquely matched focused and enabled editable field from the fresh hierarchy. It then skips the focus click and model visibility approval. Identity when available, role, label and bounds must match. Focus is rechecked before text mutations, and the resulting field value must exactly match the supplied reference. This does not establish visual visibility. The optional read-only observation field focusClickDispatched is false when existing focus was reused and true when a focus click was dispatched. Older runs omit it. Reading it in Studio or CLI Script steps and Advanced configuration is synchronous and has no side effects. The viewer does not draw a click marker when this field is false.

Browser Navigation treats a native Date control as one editable target and omits its internal segment candidates. Supply dates as YYYY-MM-DD strings in inputReferences. The asynchronous runner clicks the native English Year, Month and Day segments locally, types the supplied parts and verifies the complete ISO date before continuing. It checks fresh target, segment bounds and focus; invalid dates, unsupported segments, disabled controls or value mismatches fail. This browser-only path does not add Android/iOS date-picker support. Segment clicks are internal to one model TYPE_TEXT action; the existing action budget counts that action once. No DOM value mutation or Browser Host deployment is required.

Browser Navigation offers actionable controls when at least 50% of their rectangle area lies inside the normalized viewport. Other nodes retain full-rectangle filtering. A partially clipped candidate retains its original bounds and exposes clickPoint (normalized x/y), the center of the visible rectangle. Dispatch and the report marker use that point. This geometric fraction is not proof against overlays: the screenshot visibility check must identify an exposed click point, and fresh reachability/keyboard checks remain. A host hit-test for the original center is not reused as positive proof for a different point. Native target filtering is unchanged. CLICK choices also explicitly describe selected expected values that already match editable fields; unrelated current field contents are not sent.

New stale Navigation observations include optional hierarchyChanges with comparison=“hierarchy_paths”, totalChanges and a changes array. Each entry identifies a JSON-pointer path, change type and available before/after values from the original request hierarchy and the fresh dispatch-check hierarchy. This is a path-based structural comparison, not inferred element identity or a causal diagnosis. Editable contents and value properties are redacted; their changes remain recorded. It is stored locally, is not added to model input, and is synchronously readable without side effects through executionEvidence.navigationObservation in Script steps, Advanced configuration and existing MCP run/result tools. Older reports omit the field and the viewer explicitly says the comparison was not recorded. The existing stale detection and no-dispatch behavior are unchanged.

EN