Act commands
Click, fill, type, select, check, press, hover, scroll, upload, download and submit.
Act commands change the page. Every one of them runs through the
compatibility bridge (a real, locally installed Chrome/Chromium driven
headlessly) — the first Act command on a tab promotes it, printing
warning: tab promoted to bridge. Once promoted, a tab stays on the
bridge for the rest of its life.
Every Act command targets an element by ID (jawb click 48), or by
a fallback selector: --text <s>, --role <role>[:<name>], or --css <selector>. Exactly one is required. If a fallback matches more than
one element, jawb reports E_AMBIGUOUS with the candidate IDs; --nth <k> picks one (1-based).
Each command takes the positionals shown below and no more. An extra word
is a usage error (E_USAGE, exit 2) and nothing is sent to the page, so
quote a value or a name that contains spaces: jawb fill --role "textbox:Full name" "Jane Doe". type, select and upload are the
exceptions: type joins its words with spaces, select takes several
values and upload several files.
| Command | What it does |
|---|---|
click <target> [--button left|right|middle] [--double] | Clicks, after checking the element is visible, enabled, not covered, and stable (auto-waits up to 5s). |
fill <target> <value> [--secret-from vault:<name>|env:<NAME>|file:<PATH>] | Replaces the field's value. |
type <target> <text> [--delay <ms>] | Types key by key and appends. |
select <target> <value...> [--by value|label] | Chooses an option. |
check <target> / uncheck <target> | Sets a checkbox/radio state. |
press <key> [<target>] | Sends a key (e.g. Enter, Control+A). Without a target: the focused element. |
hover <target> | Hovers. |
scroll --to <id> | --by <px>[,<px>] | --top | --bottom | Scrolls the page or to an element. |
upload <target> <file...> | Uploads file bytes (never the path). Requires confirmation (see policy). |
download <target> [--to <dir>] | Triggers a download; --to makes the CLI fetch the saved bytes into a directory you choose. |
submit [<target>] | Submits a form (target defaults to the focused element's form). |
media play|pause <target> | Controls a media element. |
Every Act command returns a diff (what changed since the previous revision), not a full re-observation. Example from filling and submitting a job application form:
$ jawb fill --role "textbox:Full name" "Jane Doe"
fill [4] ok
~ [14] state: [] -> ["focused"]; value: "" -> "Jane Doe"
re-rendered 10 element(s), new ids: 3→13, 4→14, 5→15, 6→16, 7→17, 8→18 (+4 more)
rev 2 → 3 (bridge, 243ms)
$ jawb select --role "combobox:Team" "Browser engine" --by label
select [28] ok: selected ["browser"]
~ [36] state: ["focused"] -> []
~ [38] state: [] -> ["focused"]; value: "Backend" -> "Browser engine"
re-rendered 10 element(s), new ids: 23→33, 24→34, 25→35, 26→36, 27→37, 28→38 (+4 more)
rev 4 → 5 (bridge, 248ms)
$ jawb upload --role textbox:Resume resume.txt
error: this upload action needs confirmation (E_CONFIRMATION_REQUIRED)
confirm token (upload): ct_c4wk43ztqc56vkq0cwzh
hint: re-run the same command with --confirm-token <token> (or --yes)
$ jawb upload --role textbox:Resume resume.txt --yes
upload [40] ok: 1 file(s) sent as bytes
~ [40] value: "" -> "resume.txt"
~ [41] label: "No file chosen" -> "Attached resume.txt (80 bytes)"
rev 5 → 6 (bridge, 194ms)
$ jawb submit --role "button:Send application"
submit [42] ok
navigated -> http://.../thanks.html?name=Jane+Doe&email=jane%40example.com&role=browser&resume=resume.txt (2 elements; run `jawb observe` for the page)
rev 6 → 7 (bridge, 204ms)
Notice the page rebuilding its own DOM after each fill/select (a
React-like form does this constantly) never invalidated the elements jawb
had already targeted — that's the ID-retargeting behavior described in
observe.
A page with a payment or password field, or a control whose label looks
like a purchase action (buy, pay, place order, subscribe), triggers a
stricter confirmation flow than the upload above — see
policy and confirmation, which is not something --yes
can bypass.
Secrets
Store a password or token once, then fill it by name. jawbd keeps it encrypted with the profile's key and resolves it itself, so the value never passes through the CLI, your shell history, the envelope or the trace:
$ printf %s "$SITE_PASSWORD" | jawb secret set site-pw # value from stdin, never argv
stored secret site-pw in profile default
$ jawb fill --role "textbox:Password" --secret-from vault:site-pw
fill [7] ok
$ jawb secret list
site-pw
$ jawb secret rm site-pw
--from env:<NAME> or --from file:<PATH> read the value from an
environment variable or a file instead of stdin. Secrets belong to a
persistent profile (--profile picks one): incognito tabs cannot use
them, storage export never includes them, and profile delete removes
them. secret list shows names and bound domains. jawb never shows a
value back.
An extension that calls an API can ask jawbd to send a secret with its
requests, for example as an Authorization header. jawbd sends it only to
the domains you bound the secret to, and only if the extension declares
them too:
$ printf %s "$API_TOKEN" | jawb secret set shop.key --domains api.shop.example
stored secret shop.key in profile default; sent only to api.shop.example
$ jawb secret set shop.key --domains api.shop.example,eu.shop.example
jawb: this sends a secret to MORE domains and needs a human (pc_…):
...
Type `widen` to approve it, or `deny` to decline it:
A secret with no domains is never sent by an extension; fill --secret-from still uses it. Adding a domain to an existing secret needs a
person: jawb shows the change and asks you to type widen. A program can
ask for the change but cannot approve it with the token alone; you approve
it here, with jawb confirm <pc_id>, or in the desktop app. Creating a
secret, or removing domains, applies at once. Needs jawbd API level 29.