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

PrefixSource
$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
$nameBlock/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, while body).
  • Example: $i = $i + 1, $fullName = "Hello {$params.name}".

Store bag (store)

  • Write persistent values with store <key> <expr>.
  • Read with $store.<key>.
  • $store itself is read-only except through store statements.
flow {
  in portal {
    store orderId extract { css "#order-id" }
    store receipt { orderId: $store.orderId, runAt: $context.startTime }
  }
}

Execution context ($context)

FieldValue
$context.startTimeTask start timestamp (ISO 8601, agent timezone). Frozen for the run.
$context.timezoneAgent IANA timezone (e.g. America/Sao_Paulo).
$context.executionIdCurrent execution id.
$context.agentIdAgent 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)

LevelOperators
Boolean ORor
Boolean ANDand
Unary NOTnot
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
  • now(...) — live date/time (see below)
  • infer "prompt" with ($page) — LLM-inferred value
  • extract { selector } [attribute "href"] [timeout N] [default value]
  • extract_all { selector } [attribute "href"] [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

FormExample
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, fires input/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.

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_contains is substring match; case-insensitive by default.
  • case_sensitive true prefix 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 { ... }
  • terminate ends the run; terminate({ result: $store.data }) sets the final payload (parentheses required when passing a value).
  • while max N limits iterations (integer 1–100).
  • break only works inside while.

LLM helpers (decide / infer)

if decide "Is the user logged in?" with ($page) { ... } else { ... }
$label = infer "primary CTA label" with ($page)
  • with is required; use parentheses: with ($page), with ($page, $store), with (fragment { css ".panel" }, $params).
  • Never include $secrets in with.

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

MistakeFix
Missing flow blockWrap all statements in flow { ... }
Local assignmentUse $x = expr
Persist extracted valueUse store key extract { ... } or $x = extract { ... }
$secrets outside inMove secrets usage inside in handler { }
Missing with on decide/inferBoth require with (...)
break outside whileOnly valid inside while loops
Invalid while maxMust be integer 1–100
Bare terminate valueUse terminate(value) with parentheses

Session-aware authentication

See Credentials for the recommended login pattern that handles both fresh and persisted sessions.