DSL Reference
Complete reference for reArray's task automation language: structure, selectors, actions, conditions, and flow control.
reArray tasks are written in a domain-specific language (DSL) executed by agents against real browser sessions. This page is the canonical syntax reference.
Required structure
- Exactly one
flow { ... }block per script - Platforms are preconfigured on the agent — do not declare platforms in DSL
- Use configured platform handlers directly:
in login { ... }
Variable namespaces
| Prefix | Source |
|---|---|
$params.* | Execution inputs (UI form or API) |
$settings.* | Agent settings |
$store.* | Read-only run-wide bag populated via store |
$secrets.* | Platform vault credentials (only inside in handler { }) |
$context.* | Read-only execution metadata |
$name | Block/loop-scoped local variable |
Locals ($name)
- Declare and assign with
$x = <expr>. First assignment creates the binding; later assignments update it in the current scope. - Locals are scoped to the current block (
in,if,for,whilebody). - If the value is an object (e.g.
extract … format,now()), read fields with$address.street/$t.year. - Example:
$i = $i + 1,$fullName = "Hello {$params.name}". - Use a local for intermediates.
storeis for values that must persist for the run / task output — do not copy a field out of$storewithstore street $store.patient.street.
Store bag (store)
- Write persistent values with
store <key> <expr>. - Read with
$store.<key>. $storeitself is read-only except throughstorestatements.
flow {
in portal {
$orderId = extract { css "#order-id" }
store receipt { orderId: $orderId, runAt: $context.startTime }
}
}
Execution context ($context)
| Field | Value |
|---|---|
$context.startTime | Task start timestamp (ISO 8601, agent timezone). Frozen for the run. |
$context.timezone | Agent IANA timezone (e.g. America/Sao_Paulo). |
$context.executionId | Current execution id. |
$context.agentId | Agent id running the task. |
String interpolation
Inside any string literal, {expr} embeds a value. Escape literal braces as \{ and \}.
fill { css "#search" } "Query: {$params.term}"
fill { css "#from" } "From: {now(days = -30, unit = "date")}"
Expressions
Operators (precedence low → high)
| Level | Operators |
|---|---|
| Boolean OR | or |
| Boolean AND | and |
| Unary NOT | not |
| Comparison | == != < <= > >= |
| Additive | + - |
| Multiplicative | * / % |
| Unary minus | - |
Comparisons are numeric-aware when both sides coerce to numbers; otherwise strings/objects use deep equality.
if $params.mode == "fast" and visible { css "#ready" } { ... }
if $total > 100 or $store.force { ... }
Values
- Literals:
"text",123,true,false,null - Arrays:
[1, 2, $params.id] - Objects:
{ key: "value", count: $store.n } - Variables:
$params.foo,$store.bar,$myLocal,$address.street now(...)— live date/time (see below)infer "prompt" with ($store.invoiceStatus)— LLM-inferred valueextract { selector } [attribute "href"] [format "{name}\\n{street}"] [timeout N] [default value]extract_all { selector } [attribute "href"] [format "{name}\\n{street}"] [timeout N] [default value]
Current time (now(...))
Evaluated on every reference (unlike frozen $context.startTime).
now() // full object now(unit = "date") // YYYY-MM-DD now(days = -30, unit = "date") // 30 days ago now(hours = 2, unit = "time") // HH:mm:ss now(unit = "date", tz = "America/New_York")
Deltas: years, months, weeks, days, hours, minutes, seconds (integers, may be negative).
Output units: iso, date, time, datetime, year, month, day, hour, minute, second, weekday, weekdayName, epochMs, epochSec.
flow {
in reports {
$t = now()
fill { css "#year" } $t.year
fill { css "#from" } now(days = -30, unit = "date")
fill { css "#to" } now(unit = "date")
}
}
Selectors
| Form | Example |
|---|---|
| CSS | { css "#submit" } |
| XPath | { xpath "//button[@type='submit']" } |
| Text | { text "Continue" } |
| Role | { role "button" } |
| Role + name | { role "button" "Continue" } |
| Fallback chain | { css "#submit" xpath "//button[@type='submit']" } |
| Frame scope | { frame "f0" css "#card-number" } |
| Interpolated CSS | { css "[data-invoice-id='{$params.invoiceId}']" } |
| Interpolated text | { text "Invoice {$params.invoiceId}" } |
css and text selectors may embed {expr} holes that resolve at run time; xpath and role may not. Keep the literal parts of the selector and interpolate only the value that varies — a selector made entirely of holes (css "{$params.sel}") is rejected. Put holes inside quotes ([data-id='{$params.id}']); an unquoted hole (#row-{$params.id}) accepts only bare identifier values.
To pick one row of a table by a value in one of its cells, interpolate into a :has() filter:
click { css "#invoices tr:has(td:text-is('{$params.invoiceId}')) button.open" }
Actions
click {selector} [timeout N]
fill {selector} expr [typing] [timeout N]
select {selector} expr [using ai] [timeout N]
wait expr [timeout N]
assert expr
upload {selector} expr [timeout N]
download {selector} [timeout N]
solve_captcha
fill {sel} "4242"— instant fill (default): fast, firesinput/change, skips key events.fill {sel} "4242" typing— clears the field then types character-by-character with real key events. Use for input masks, autocomplete pickers, or per-keystroke validation. Slower (~20ms per character). Must not contain newlines or tabs.select {sel} "US"— literal option value.select {sel} $params.country using ai— LLM maps input to the best<option>.
Implicit waits: click, fill, select, download, and screenshot {sel} poll until the target is visible (same as wait visible {sel}) before acting. upload and extract poll until the element exists (hidden inputs are OK). Default timeout is 10 seconds; add timeout N (milliseconds) to override. You usually do not need a separate wait visible before fill or click.
Use store <key> extract { ... } or $x = extract { ... } to capture DOM values. extract waits for a non-empty value by default; add timeout N and default value to control wait behavior and fallbacks. Add format "{name}\\n{street}" to split visible text into named fields without an LLM.
Conditions (DOM + expressions)
DOM conditions are expressions:
visible {selector}
exists {selector}
element_contains [case_sensitive expr] {selector} expr
decide "question?" with ($page)
not (expr)
element_containsis substring match; case-insensitive by default.case_sensitive trueprefix enables case-sensitive matching.
Flow control
if expr { ... } else { ... }
for $item in expr { ... }
while expr [max N] { ... }
break
terminate
terminate(expr)
$x = expr
store key expr
in platformHandler { ... }
when <alert|confirm|prompt> <accept|dismiss> [with expr] [timeout N] { ... }
terminateends the run;terminate({ result: $store.data })sets the final payload (parentheses required when passing a value).while max Nlimits iterations (integer 1–100).breakonly works insidewhile.
Native dialogs (when)
Wrap the step(s) that trigger a browser-native alert / confirm / prompt. The runtime arms a one-shot handler for the body — you do not register listeners.
when confirm accept {
click { css "button.delete" }
}
when confirm dismiss {
click { css "button.cancel-nav" }
}
when alert accept {
click { css "button.warn" }
}
when prompt accept with $params.rename {
click { css "button.rename" }
}
- Must sit inside an
inblock (like other browser actions). - The matching dialog is required: the block fails if none of the expected type appears.
- Only the first matching dialog is handled per
when; an unexpected extra dialog fails. - Nesting is allowed for chained dialogs (outer = first dialog, each nested
when= next):when confirm accept { when alert accept { click { css "button.delete" } } } with <expr>is only valid forprompt+accept(text passed into the prompt).- Optional
timeout Nextends how long to wait for a delayed dialog after the body finishes (from the start of the block). Without it, a short grace period is used. - Without a
whenblock, Playwright auto-dismisses native dialogs (effectively Cancel).
LLM helpers (decide / infer)
if decide "Is the user logged in?" with ($page) { ... } else { ... }
$label = infer "primary CTA label" with (fragment { css ".toolbar" })
$block = extract { css "#notes" }
$summary = infer "one-line summary of the notes" with ($block)
withis required; use parentheses.- Pass the smallest context that can answer the prompt: a
fragment { ... }, a local ($block), or a single key ($store.invoiceStatus,$params.mode). Do not pass whole$pageor$storewhen a fragment, local, or key suffices. - Never include
$secretsinwith.
See AI Helpers for usage guidance.
Platform blocks
Wrap site-specific steps in in handler { ... }:
flow {
in login_portal {
fill { css "#email" } $secrets.username
click { css "#submit" }
}
in crm {
store accountName extract { css ".account-name" }
}
}
$secrets.* is only valid inside the matching platform block.
Minimal valid template
flow {
in portal {
if visible { css "#dashboard" } {
// already signed in
} else {
if visible { css "#login-form" } {
fill { css "#username" } $secrets.username
fill { css "#password" } $secrets.password
click { css "button[type='submit']" }
}
wait visible { css "#dashboard" } timeout 30000
}
store welcome extract { css "#welcome-message" }
}
}
Common mistakes
| Mistake | Fix |
|---|---|
Missing flow block | Wrap all statements in flow { ... } |
| Local assignment | Use $x = expr |
| Persist extracted value | Use store key extract { ... } or $x = extract { ... } |
$secrets outside in | Move secrets usage inside in handler { } |
Missing with on decide/infer | Both require with (...) |
break outside while | Only valid inside while loops |
Invalid while max | Must be integer 1–100 |
Bare terminate value | Use terminate(value) with parentheses |
Session-aware authentication
See Credentials for the recommended login pattern that handles both fresh and persisted sessions.