Docs Center

BAL Language

Complete syntax and command reference for BAL.

Introduction

BAL (Browser Automation Language) is a domain-specific language for browser automation. It fully decouples "find element" from "do what": the language only describes intent, and an executor translates it into Playwright calls.

Core goals

  • Site-agnostic: no site logic is built in; site differences live in .page configs.
  • Input-first compatibility: every input falls back through strategies + verification; fill is never assumed.
  • Full upload coverage: images, video, attachments, directories, drag, paste, chunked — seven paths.
  • Tags abstracted: "type + suggest + select + dedup + verify" wrapped in one command.
  • Extensible: commands, selector strategies and upload strategies extend via registry without core changes.

Parameter declaration

Declare an input: key / label / type / required flag; injected and validated at runtime.

@input · Input

# @input prompt 提示词 text 必填

Declare an input: key / label / type / required flag; injected and validated at runtime.

@output · Output

# @output images 图片列表 text

Declare an output: key / label / type; results go to the work log and can feed the next node.

Design principles

PrincipleDescription
Site-agnosticNo site logic built in; differences go in .page configs
Input-first compatibilityEvery input falls back through strategies + verification; fill is never assumed
Explicit waitingNo blind sleep; wait_until real states
VerifiableEvery write op supports verify:true; degrade instead of staying silent
ExtensibleCommands, selector & upload strategies extend via registry
Readable & diffablePlain-text line syntax with comments and modules

Contents

1. Lexical rules

Basic units

  • 一行一条命令,# 开头为注释
  • 命令名小写蛇形:wait_until、set_input_files
  • 字符串双引号,支持 \" \\ \n \t 转义
  • 参数形式:位置参数 "值"、键值参数 key:value、选项 [opt=val]
  • 变量 $name,常量 UPPER_CASE 约定为全局

Comments & blank lines

# 整行注释
goto "https://example.com"   # 行尾注释

Line continuation

# 长命令用 \ 续行
upload css:"input[type=file]" \
    files:"a.jpg","b.png" \
    mode:auto \
    verify:true

String interpolation

set $name = "世界"
echo "你好,${name}"          # 你好,世界
echo "你好,$name"            # 同上,简单变量可省花括号

2. Selector syntax

Uniform form: <strategy>:<value>[modifiers]

Strategy table

StrategyExamplePlaywright mapping
texttext:"登录"get_by_text
rolerole:button["提交"]get_by_role
labellabel:"邮箱"get_by_label
placeholderplaceholder:"搜索"get_by_placeholder
testidtestid:"submit-btn"get_by_test_id
titletitle:"关闭"get_by_title
altalt:"封面"get_by_alt_text
csscss:".btn.primary"locator
xpathxpath:"//button[@type='submit']"locator
nthnth:34th in current context
refref:$savedReference a previously saved element handle

Modifiers

click text:"删除" [nth=2]        # 第 3 个匹配
click text:"删除" [last]
click text:"删除" [first]
click text:"删除" [visible=true]  # 只匹配可见元素
click css:".item" [filter="含关键词"]
click role:button["提交"] [exact=true]

Chained locating

# 在某个容器内查找
click css:".modal" >> text:"确认"
# 父子、兄弟关系
click css:".form" > placeholder:"用户名"
click text:"用户名" + css:".error"

Saving element handles

find placeholder:"标题" -> $title_input
input $title_input value:"测试"

3. Variables & expressions

Assignment

set $count = 0
set $url = $BASE + "/page/" + $count
set $pwd = env("APP_PASSWORD")
set $user = input("请输入用户名")
set $now = now()
set $id = uuid()
set $n = random(1, 100)

Built-in functions

FunctionDescription
now([format])Current time
uuid()UUID v4
random(min, max)Random integer
env(name)Environment variable
input(prompt)Runtime prompt
file(path)Read file content
glob(pattern)File glob
trim(s) / lower(s) / upper(s)String utils
split(s, sep) / join(list, sep)List utils
len(list) / index(list, i)List ops
json(str) / to_json(obj)JSON utils
base64_encode(s) / base64_decode(s)Encoding
sleep(ms)Pause (in expressions)

Expressions

set $total = $price * $qty
set $ok = $count > 3 and $name != ""
set $msg = "共 " + str($count) + " 条"

支持:+ - * / %、== != > < >= <=、and or not、括号。

4. Command overview

【配置与会话】 config  session  context  page  viewport  user_agent  locale  timezone
【导航】       goto  back  forward  reload  open  switch_tab  close_tab  wait_for_load
【查找】       find  find_all  exists  count
【输入】       input  fill  type  type_slow  paste  clear  press  press_seq  ime_input
【点击】       click  dblclick  right_click  hover  focus  blur  tap
【表单】       check  uncheck  select  select_multi  set_date  set_time  set_range
【上传】       upload  upload_dir  upload_drag  upload_paste  upload_chunked  cancel_upload
【标签】       tag_add  tag_remove  tag_select  tag_clear  tag_list
【鼠标】       drag  drag_to  scroll_to  scroll_by  scroll_into_view
【键盘】       keyboard_press  keyboard_type  keyboard_down  keyboard_up
【等待】       wait  wait_until  wait_for_url  wait_for_response  wait_for_download
【断言】       assert  assert_exists  assert_text  assert_url  assert_count
【提取】       get_text  get_attr  get_value  get_count  get_url  get_title  get_html
【流程】       if/elif/else/endif  loop/endloop  foreach/endforeach  while/endwhile
               break  continue  return
【函数】       func/endfunc  call  import
【错误】       try/catch/finally  on_error  retry
【弹窗】       dialog_accept  dialog_dismiss  dialog_handle
【框架】       frame  frame_exit  frame_main
【窗口】       new_window  switch_window  close_window
【网络】       intercept  mock  block  unblock  route  unroute
【存储】       storage_save  storage_load  cookie_set  cookie_get  cookie_clear
【调试】       screenshot  echo  log  pause  trace_start  trace_stop  dump

5. Config & session

config (global)

config timeout=30000
config screenshot_on_error=true
config input_mode=auto            # auto | fast | compat | human
config input_delay=30
config input_verify=true
config input_max_retry=2
config upload_timeout=180000
config headless=false
config slow_mo=0

session

session new
session load "account.json"
session save "account.json"
session close

context / page

context new [viewport=1920x1080] [locale=zh-CN] [timezone=Asia/Shanghai]
context close
page new
page close
page switch 0
page list -> $pages

viewport / user_agent / locale

viewport 1440 900
user_agent "Mozilla/5.0 ..."
locale "zh-CN"
timezone "Asia/Shanghai"
geolocation 31.23 121.47
permissions ["geolocation", "notifications"]

7. Find & existence

find <selector> -> $var
find_all <selector> -> $list
exists <selector> -> $bool
count <selector> -> $n
find placeholder:"标题" -> $title
find_all css:".item" -> $items
exists text:"加载更多" -> $has_more
count css:".comment" -> $n

8. Input familyCore

Key design: every input falls back through multiple strategies with verification. Never assume fill() works — React/Vue controlled components, contenteditable rich text and inputs with autofill often ignore direct assignment.

Unified entry: input

input <selector> value:"..." 
    mode:auto|fast|type|type_slow|keyboard|native|richtext|ime
    delay:30
    clear:true
    verify:true
    on_fail:abort|continue|retry(3,1000)

Mode reference

modeBehaviorUse case
fastDirect fillStatic forms, uncontrolled
typepress_sequentially(delay)Plain React components
type_slowChar by char, delay≥100Search boxes with suggestions
keyboardclick + Ctrl+A + Delete + keyboard.typeStrict anti-bot, IME
nativeNative setter + dispatch input/changeReact/Vue controlled
richtextcontenteditable chainRich text editors
imeDispatch composition eventsCJK IME
autoDetect type → fallback chain → verifyRecommended default

Auto-fallback flow

input mode:auto
  ├─ 检测元素类型
  │   ├─ input/textarea  → 表单策略链
  │   ├─ contenteditable → 富文本策略链
  │   └─ 未知            → 先试表单,再试富文本
  ├─ 表单策略链:fill → press_sequentially → keyboard.type → native_setter
  ├─ 富文本链:press_seq → insert_text → execCommand → innerText
  ├─ 每步验证:input_value() 或 inner_text()
  ├─ 失败降级,全部失败 → 截图 + trace + on_fail
  └─ 返回实际生效策略名(写入日志)

Dedicated commands

fill placeholder:"标题" value:"测试"
type placeholder:"标题" value:"测试" delay:50
type_slow placeholder:"搜索" value:"Playwright" delay:150
paste css:".editor" value:"很长的正文..."
clear placeholder:"标题"
press css:".editor" key:"Enter"
press css:".editor" key:"Control+A"
press_seq css:".editor" value:"逐字符输入" delay:30
ime_input css:".editor" value:"中文输入法内容"

Auto-unlock read-only & disabled

# mode:auto 检测到 readOnly/disabled 时自动执行:
el.readOnly = false; el.disabled = false;
unlock placeholder:"只读字段"
lock placeholder:"字段"

Input verification

input placeholder:"标题" value:"测试" verify:true
assert_value placeholder:"标题" equals "测试"
assert_value placeholder:"标题" contains "测"

9. File uploadCore

Unified entry: upload

upload <selector> 
    files:"a.jpg","b.png"
    mode:auto|input|drag|paste|chunked|dir
    cover:"a.jpg"
    verify:true
    timeout:180000
    on_fail:abort|continue|retry(3,1000)

Mode reference

modeBehaviorUse case
inputDirect set_input_filesStandard input[type=file]
dragSimulate drag onto target areaNo input; pure drag upload
pasteSimulate pasting filesEditors supporting paste upload
chunkedChunked upload for big filesVideo, large attachments
dirUpload a whole directorywebkitdirectory
autoDetect → fallback chainRecommended default

Fallback chain

upload mode:auto
  ├─ 查找 input[type=file]
  │   ├─ 存在且 visible/attached → set_input_files
  │   └─ 隐藏 → 直接 set_input_files(无需可见)
  ├─ 无 input → 点击上传触发区 → 等待 input 出现 → set_input_files
  ├─ 仍无 input → 尝试 drag 策略
  ├─ 仍失败 → 尝试 paste 策略
  ├─ 大文件(>50MB)→ 自动切 chunked
  ├─ 等待上传完成:缩略图数量 / 进度条消失 / 网络空闲
  └─ 校验:文件数量、封面、缩略图存在

Image upload

upload css:"input[type=file]" files:"cover.jpg","detail1.png","detail2.png"
upload css:"input[type=file]" files:glob("assets/*.jpg")
upload css:".upload-area" files:"cover.jpg" mode:drag
upload css:".editor" files:"screenshot.png" mode:paste
upload css:"input[type=file]" files:"a.jpg" cover:"a.jpg" verify:true

Video upload

upload css:"input[type=file]" files:"video.mp4" mode:auto timeout:600000
upload css:"input[type=file]" files:"video.mp4" mode:chunked chunk_size:5242880
assert_uploaded css:".video-item" count:1
wait_until text:"视频上传完成" timeout:600000

Attachment upload

upload css:"input[type=file]" files:"doc.pdf","sheet.xlsx"
upload css:"input[type=file]" files:"archive.zip" verify:true

Directory upload

upload_dir css:"input[type=file]" dir:"assets/images"

Drag · paste · chunked

upload_drag css:".drop-zone" files:"cover.jpg"
drag_file css:".drop-zone" path:"cover.jpg"
upload_paste css:".editor" path:"screenshot.png"
upload_chunked css:"input[type=file]" path:"big-video.mp4" chunk_size:5242880

Upload control

cancel_upload                    # 取消当前上传
pause_upload                     # 暂停(若页面支持)
resume_upload
assert_uploaded <selector> count:<n>
wait_upload_complete timeout:180000

10. Tags & hashtagsCore

Key design: tag interaction combines type + suggest + select. Abstracted into a command family: type keyword → wait suggestions → pick candidate → dedup → verify, all in one command.

tag_add

tag_add <selector> 
    value:"Playwright"
    mode:auto|type|select|enter|comma
    wait_suggest:true
    select:nth(0)|first|exact|contains
    verify:true
    dedup:true

Interaction flow

tag_add mode:auto
  ├─ 点击标签输入入口(或 # 触发)
  ├─ 输入关键词(走 input mode:auto)
  ├─ 等待联想候选出现
  ├─ 选择候选:
  │   ├─ select:first       → 第一个
  │   ├─ select:nth(2)      → 第三个
  │   ├─ select:exact       → 文本完全匹配
  │   └─ select:contains    → 文本包含
  ├─ 或按 Enter/逗号 直接创建
  ├─ 验证标签 chip 出现
  └─ 去重(已存在则跳过)

Examples

# 小红书话题
tag_add placeholder:"搜索话题" value:"Playwright" select:exact
tag_add placeholder:"搜索话题" value:"自动化测试" select:first
tag_add placeholder:"搜索话题" value:"小红书运营" select:contains

# 批量
foreach $t in ["Playwright", "自动化", "测试"]
    tag_add placeholder:"搜索话题" value:$t select:first
endforeach

Other tag commands

tag_remove placeholder:"搜索话题" value:"Playwright"
tag_clear placeholder:"搜索话题"
tag_list -> $tags                      # 提取当前所有标签
tag_select css:".tag-list" value:"已有标签"   # 从已有列表中选择
assert_tag placeholder:"搜索话题" contains "Playwright"
assert_tag_count placeholder:"搜索话题" equals 3

Trigger methods

# 通过 # 触发
tag_add css:".editor" value:"Playwright" trigger:"#"

# 通过按钮触发
tag_add css:".editor" value:"Playwright" trigger:click

# 直接输入 + 回车
tag_add css:".tag-input" value:"Playwright" mode:enter

11. Form controls

Checkboxes & radios

check css:"#agree"
uncheck css:"#subscribe"
check css:".radio-option" [value="A"]
is_checked css:"#agree" -> $checked

Dropdown selection

select css:"#city" value:"上海"
select css:"#city" label:"上海市"
select css:"#city" index:2
select_multi css:"#tags" values:"A","B","C"
deselect css:"#city" value:"上海"
select_native css:"#city" value:"上海"     # 原生 select
select_custom css:".dropdown" value:"上海" # 自定义下拉

Date · time · range

set_date css:"#date" value:"2026-09-29"
set_time css:"#time" value:"14:30"
set_range css:"#price" value:500
set_color css:"#color" value:"#ff0000"

Sliders & ratings

set_slider css:".slider" value:75
set_rating css:".rating" value:5

12. Click & mouse

click <selector> [button=left|right|middle] [click_count=1] [force=false]
dblclick <selector>
right_click <selector>
hover <selector>
focus <selector>
blur <selector>
tap <selector>                     # 触摸
click_at x:100 y:200
click_position <selector> x:10 y:10
drag <from> to <to>
drag_to <selector> target:<selector>
scroll_to <selector>
scroll_by x:0 y:500
scroll_into_view <selector>
scroll_top / scroll_bottom

13. Keyboard

keyboard_press "Enter"
keyboard_press "Control+A"
keyboard_type "Hello" delay:50
keyboard_down "Shift"
keyboard_up "Shift"
keyboard_insert_text "中文文本"

14. Wait & sync

wait 1000
wait_until <selector> [state=visible|hidden|attached|detached] [timeout=30000]
wait_until url contains "/dashboard"
wait_until url equals "https://..."
wait_until text:"加载完成"
wait_until title contains "首页"
wait_until count css:".item" >= 10
wait_for_url "**/dashboard"
wait_for_response url contains "/api/user"
wait_for_request url contains "/api/submit"
wait_for_download to:"file.pdf"
wait_for_popup -> $new_page
wait_for_function "() => window.ready === true"
wait_for_load state:networkidle
wait_for_timeout 3000

15. Assertions

assert exists <selector>
assert not_exists <selector>
assert visible <selector>
assert hidden <selector>
assert enabled <selector>
assert disabled <selector>
assert text <selector> equals "预期"
assert text <selector> contains "包含"
assert text <selector> matches "/正则/"
assert value <selector> equals "值"
assert attr <selector> name:"href" contains "/home"
assert url contains "/dashboard"
assert title equals "首页"
assert count <selector> equals 5
assert count <selector> >= 3
assert checked css:"#agree"
assert uploaded <selector> count:3
assert tag <selector> contains "Playwright"
assert no_console_error
assert no_network_error
# 失败策略
assert exists css:".ad" on_fail:continue
assert exists css:".required" on_fail:abort
assert exists css:".optional" on_fail:retry(3, 1000)

16. Data extraction

get_text <selector> -> $var
get_text_all <selector> -> $list
get_attr <selector> name:"href" -> $url
get_value <selector> -> $val
get_html <selector> -> $html
get_inner_html <selector> -> $html
get_count <selector> -> $n
get_url -> $url
get_title -> $title
get_cookie name:"session" -> $cookie
get_local_storage key:"token" -> $token
get_all_text -> $page_text
get_screenshot <selector> to:"el.png"
# 批量提取
get_all css:".item" -> $items {
    text: "get_text(css:'.title')",
    link: "get_attr(css:'a', 'href')",
    price: "get_text(css:'.price')"
}

17. Flow control

Conditionals

if $count > 3
    echo "超过阈值"
elif $count == 0
    echo "零"
else
    echo "正常"
endif

Counted loops

loop 5 times
    click text:"下一页"
    wait 1000
endloop

Iteration

foreach $item in ["a", "b", "c"]
    echo $item
endforeach

foreach $file in glob("assets/*.jpg")
    upload css:"input[type=file]" files:$file
endforeach

Conditional loops

while exists text:"加载更多"
    click text:"加载更多"
    wait 1000
endwhile

Loop control

loop 10 times
    if exists text:"结束"
        break
    endif
    if not_exists text:"继续"
        continue
    endif
endloop

Return

func check()
    if not_exists css:".ok"
        return false
    endif
    return true
endfunc

18. Functions & modules

Define & call

func login(user, pass)
    goto $BASE + "/login"
    input placeholder:"用户名" value:$user
    input placeholder:"密码" value:$pass mode:keyboard
    click role:button["登录"]
    wait_until url contains "/home"
endfunc

call login("admin", env("APP_PASSWORD"))

Default parameters

func publish(title, body, tags = [])
    input placeholder:"标题" value:$title
    input role:textbox[first] value:$body mode:richtext
    foreach $t in $tags
        tag_add placeholder:"搜索话题" value:$t select:first
    endforeach
    click text:"发布"
endfunc

Module import

import "lib/xhs.bal"
import "lib/common.bal"

call xhs_publish("标题", "正文", ["标签1", "标签2"])

Module export

# lib/xhs.bal
export func xhs_login(session_file)
    ...
endfunc

export func xhs_publish(title, body, tags)
    ...
endfunc

19. Error handling

try / catch / finally

try
    click text:"提交"
    wait_until text:"成功"
catch e
    echo "失败: " + $e.message
    screenshot "error.png"
finally
    echo "清理"
endtry

on_error modifier

click text:"提交" on_error:retry(3, 1000)
click text:"可选广告" on_error:continue
upload css:"input[type=file]" files:"a.jpg" on_error:abort

Global error policy

config on_error=abort            # abort | continue | retry
config screenshot_on_error=true
config trace_on_error=true

20. Dialogs · frames · windows

Native dialogs

dialog_accept
dialog_dismiss
dialog_handle accept
dialog_handle dismiss
dialog_prompt "输入内容"
on_dialog accept

iframe

frame css:"iframe#login"
    input placeholder:"用户名" value:"admin"
    click text:"登录"
frame_exit
frame_main

Multiple windows

new_window "https://example.com" -> $page
switch_window 1
switch_window title contains "新窗口"
close_window

21. Network & storage

Intercept & mock

intercept url contains "/api/user" -> $response
mock url contains "/api/user" body:{"name": "test"} status:200
block url contains ".png"
unblock url contains ".png"
route url contains "/api/**" handler:"mock_handler"
unroute
wait_for_response url contains "/api/submit"

Storage

storage_save "state.json"
storage_load "state.json"
cookie_set name:"token" value:"xxx" domain:".example.com"
cookie_get name:"token" -> $token
cookie_clear
local_storage_set key:"token" value:"xxx"
local_storage_get key:"token" -> $val

22. Debugging

screenshot "step1.png"
screenshot <selector> to:"element.png"
echo "当前 URL: " + get_url()
log info "步骤完成"
pause                              # 打开 Inspector
trace_start "trace.zip"
trace_stop
dump html -> "page.html"
dump text -> "page.txt"
dump console -> "console.log"
dump network -> "network.har"
highlight <selector>

23. Extensions

Register custom commands

# Python 侧
@executor.register("xhs_publish_note")
async def publish_note(ctx, title, body, tags):
    ...

# BAL 侧
xhs_publish_note title:"标题" body:"正文" tags:["a","b"]

Register selector strategies

STRATEGIES["data"] = lambda p, v: p.locator(f'[data-{v}]')

click data:action="submit"

Register upload strategies

UPLOAD_STRATEGIES["websocket"] = upload_via_ws

upload css:"#file" files:"a.jpg" mode:websocket

Site config .page

site: xiaohongshu
base_url: "https://www.xiaohongshu.com"
selectors:
  creator_button:  'text:"创作中心"'
  title_input:     'placeholder:"添加标题"'
  body_editor:
    selector: 'role:textbox[first]'
    input_mode: richtext
  upload_input:    'css:input[type="file"]'
  hashtag_entry:   'text:"#添加话题"'
  hashtag_search:  'placeholder:"搜索话题"'
input_defaults:
  mode: auto
  verify: true
upload_defaults:
  mode: auto
  timeout: 180000

load_page "xiaohongshu.page"
goto creator
input $title_input value:"标题"

24. Examples

XHS note publishing

# xhs_publish.bal
config input_mode=auto
config input_verify=true
config screenshot_on_error=true

load_page "xiaohongshu.page"
session load "account.json"

goto creator

# 上传图片(第一张为封面)
upload $upload_input \
    files:"cover.jpg","detail1.png","detail2.png" \
    mode:auto \
    cover:"cover.jpg" \
    verify:true
wait_upload_complete timeout:120000

# 标题(React 受控 input)
input $title_input value:"Playwright 自动化发布测试" mode:auto verify:true

# 正文(contenteditable 富文本)
input $body_editor value:"这是正文内容,测试富文本输入。" mode:richtext delay:30

# 标签
foreach $t in ["Playwright", "自动化测试", "小红书运营"]
    tag_add $hashtag_search value:$t select:first verify:true
endforeach

assert_tag_count $hashtag_search equals 3

# 发布
click text:"发布"
wait_until url contains "/publish/success" timeout:60000
screenshot "publish_result.png"

session save "account.json"

Video upload + attachments

config upload_timeout=600000

upload css:"input[type=file]" files:"video.mp4" mode:chunked chunk_size:5242880
wait_until text:"视频上传完成" timeout:600000
assert_uploaded css:".video-item" count:1

upload css:"input[type=file]" files:"cover.jpg" mode:auto
upload css:"input[type=file]" files:"doc.pdf","sheet.xlsx"

Batch ops with error recovery

foreach $item in $items
    try
        input placeholder:"搜索" value:$item mode:type_slow delay:150
        wait_until css:".result-item" state:visible
        click css:".result-item" [first]
        click text:"收藏"
        wait_until text:"已收藏"
    catch e
        log error "处理失败: " + $item + " - " + $e.message
        screenshot "error_" + $item + ".png"
        continue
    endtry
endforeach

25. Appendix

Selector cheatsheet

text · role · label · placeholder · testid · title · alt · css · xpath · nth · ref

Input mode cheatsheet

fast · type · type_slow · keyboard · native · richtext · ime · auto

Upload mode cheatsheet

input · drag · paste · chunked · dir · auto

Error codes

CodeMeaning
E_SELECTOR_NOT_FOUNDSelector has no match
E_TIMEOUTWait timeout
E_INPUT_FAILEDAll input strategies failed
E_UPLOAD_FAILEDUpload failed
E_ASSERT_FAILEDAssertion failed
E_DIALOG_UNHANDLEDDialog unhandled
E_FRAME_NOT_FOUNDIframe not found
E_NETWORK_ERRORNetwork error
E_PERMISSION_DENIEDPermission denied

Coverage checklist

CategoryCoverage
Navigationgoto/back/forward/reload/multi-tab/multi-window
Elementsfind/click/hover/drag/scroll/focus
Inputforms/rich text/read-only/IME/controlled/verify
Uploadimage/video/attachment/dir/drag/paste/chunked
Tagstype/suggest/select/remove/dedup/batch
Formscheck/radio/select/date/time/range/slider
Waitelement/URL/text/network/download/popup/function
Assertexists/visible/text/attr/count/upload/tag
Extracttext/attr/HTML/cookie/storage
Flowif/loop/foreach/break/continue/return
Functionsdefine/call/defaults/import/export
Errorstry/catch/finally/retry/on_error
Dialogsalert/confirm/prompt
Framesiframe enter/exit
Networkintercept/mock/block/wait
Storagesession/cookie/localStorage
Debugscreenshot/log/pause/trace/dump

Closing

Three core designs
  • Never assume input: input mode:auto maximizes compatibility — every field has a fallback path.
  • Never assume upload: upload mode:auto covers input, drag, paste, chunked and dir — five paths.
  • Tags abstracted: tag_add wraps "type + suggest + select + dedup + verify" into one command.

Together with .page site configs and the command registry, this DSL adapts to any site and any control — covering the full web workflow from navigation to upload to extraction — without touching the language core.