Class: Browserctl::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/browserctl/client.rb,
lib/browserctl/client/recording_interceptor.rb

Overview

Thin IPC client that wraps each browserd command as a Ruby method call.

Defined Under Namespace

Classes: RecordingInterceptor

Instance Method Summary collapse

Constructor Details

#initialize(socket_path = nil, recording_interceptor: nil) ⇒ Client

Returns a new instance of Client.



12
13
14
15
# File 'lib/browserctl/client.rb', line 12

def initialize(socket_path = nil, recording_interceptor: nil)
  @socket_path = socket_path || auto_discover_socket
  @recording_interceptor = recording_interceptor
end

Instance Method Details

#auth_check(name, include_cookies: false, state: nil, suggested_flow: nil) ⇒ Object

Runs the auth_required detector against a named page. Returns either ‘{ ok: true, auth_required: false }` or an AUTH_REQUIRED error response.

Parameters:

  • name (String)

    page name

  • include_cookies (Boolean) (defaults to: false)

    also feed the page’s current cookies into the detector (catches expired-cookie auth)

  • state (String, nil) (defaults to: nil)

    bundle name the caller was working with; passed back verbatim so callers can recover without bookkeeping

  • suggested_flow (String, nil) (defaults to: nil)

    flow name to surface when triggered



351
352
353
354
355
356
357
# File 'lib/browserctl/client.rb', line 351

def auth_check(name, include_cookies: false, state: nil, suggested_flow: nil)
  call("auth_check",
       name: name,
       include_cookies: include_cookies,
       state: state,
       suggested_flow: suggested_flow)
end

#call(cmd, **params) ⇒ Object



17
18
19
20
21
22
23
# File 'lib/browserctl/client.rb', line 17

def call(cmd, **params)
  result = communicate(JSON.generate({ cmd: cmd }.merge(params)))
  recording_interceptor.append(cmd, response: result, params: params)
  result
rescue Errno::ENOENT, Errno::ECONNREFUSED
  raise DaemonUnavailableError, "browserd is not running — start it with: browserd"
end

#click(name, selector = nil, ref: nil) ⇒ Hash

Clicks an element identified by CSS selector or snapshot ref.

Parameters:

  • name (String)

    logical page name

  • selector (String, nil) (defaults to: nil)

    CSS selector

  • ref (String, nil) (defaults to: nil)

    snapshot ref (e.g. “e3”)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/browserctl/client.rb', line 51

def click(name, selector = nil, ref: nil)
  unless selector || ref
    raise Browserctl::Error.new(
      "click: provide selector or ref",
      code: Browserctl::Error::Codes::INVALID_SELECTOR_REF,
      context: { method: :click, name: name }
    )
  end

  call("click", name: name, selector: selector, ref: ref,
                capture_post_snapshot: recording_interceptor.capture_post_snapshot_flag)
end

#cookies(name) ⇒ Hash

Returns all cookies for a named page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true, cookies: [Hash] }` or `{ error: }`



160
# File 'lib/browserctl/client.rb', line 160

def cookies(name)              = call("cookies", name: name)

#delete_cookies(name) ⇒ Hash

Deletes all cookies for a named page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



177
# File 'lib/browserctl/client.rb', line 177

def delete_cookies(name) = call("delete_cookies", name: name)

#devtools(name) ⇒ Hash

Returns the Chrome DevTools URL for a named page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true, devtools_url: }` or `{ error: }`



139
# File 'lib/browserctl/client.rb', line 139

def devtools(name)             = call("devtools", name: name)

#dialog_accept(name, text: nil) ⇒ Hash

Pre-registers a one-shot handler to accept the next JS dialog on a page.

Parameters:

  • name (String)

    logical page name

  • text (String, nil) (defaults to: nil)

    prompt text for window.prompt dialogs (ignored for alert/confirm)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



310
# File 'lib/browserctl/client.rb', line 310

def dialog_accept(name, text: nil) = call("dialog_accept", name: name, text: text)

#dialog_dismiss(name) ⇒ Hash

Pre-registers a one-shot handler to dismiss the next JS dialog on a page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



315
# File 'lib/browserctl/client.rb', line 315

def dialog_dismiss(name)           = call("dialog_dismiss", name: name)

#evaluate(name, expression) ⇒ Hash

Evaluates a JavaScript expression and returns the result.

Parameters:

  • name (String)

    logical page name

  • expression (String)

    JavaScript expression

Returns:

  • (Hash)

    ‘{ ok: true, result: }` or `{ error: }`



115
# File 'lib/browserctl/client.rb', line 115

def evaluate(name, expression) = call("evaluate", name: name, expression: expression)

#export_cookies(name, path) ⇒ Hash

Exports all cookies for a named page to a JSON file. File I/O is client-side; daemon provides the cookie data.

Parameters:

  • name (String)

    logical page name

  • path (String)

    file path to write cookies to

Returns:

  • (Hash)

    ‘{ ok: true, path:, count: }` or `{ error: }`



184
185
186
187
188
189
190
191
# File 'lib/browserctl/client.rb', line 184

def export_cookies(name, path)
  result = call("cookies", name: name)
  return result unless result[:ok]

  FileUtils.mkdir_p(File.dirname(path))
  File.open(path, "w", 0o600) { |f| f.write(JSON.generate(result[:cookies])) }
  { ok: true, path: path, count: result[:cookies].length }
end

#fetch(key) ⇒ Hash

Retrieves a value from the daemon-scoped key-value store.

Parameters:

  • key (String)

    storage key

Returns:

  • (Hash)

    ‘{ ok: true, value: }` or `{ error:, code: “key_not_found” }`



155
# File 'lib/browserctl/client.rb', line 155

def fetch(key)                 = call("fetch", key: key)

#fill(name, selector = nil, value = nil, ref: nil) ⇒ Hash

Fills an input element with a value.

Parameters:

  • name (String)

    logical page name

  • selector (String, nil) (defaults to: nil)

    CSS selector

  • value (String, nil) (defaults to: nil)

    text to type

  • ref (String, nil) (defaults to: nil)

    snapshot ref

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/browserctl/client.rb', line 70

def fill(name, selector = nil, value = nil, ref: nil)
  unless selector || ref
    raise Browserctl::Error.new(
      "fill: provide selector or ref",
      code: Browserctl::Error::Codes::INVALID_SELECTOR_REF,
      context: { method: :fill, name: name }
    )
  end

  call("fill", name: name, selector: selector, ref: ref, value: value,
               capture_post_snapshot: recording_interceptor.capture_post_snapshot_flag)
end

#hover(name, selector = nil, ref: nil) ⇒ Hash

Moves the mouse to the centre of the element matched by selector.

Parameters:

  • name (String)

    logical page name

  • selector (String) (defaults to: nil)

    CSS selector

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



258
259
260
261
262
263
264
265
266
267
268
# File 'lib/browserctl/client.rb', line 258

def hover(name, selector = nil, ref: nil)
  unless selector || ref
    raise Browserctl::Error.new(
      "hover: provide selector or ref",
      code: Browserctl::Error::Codes::INVALID_SELECTOR_REF,
      context: { method: :hover, name: name }
    )
  end

  call("hover", name: name, selector: selector, ref: ref)
end

#import_cookies(name, path) ⇒ Hash

Imports cookies from a JSON file into a named page.

Parameters:

  • name (String)

    logical page name

  • path (String)

    file path to read cookies from

Returns:

  • (Hash)

    ‘{ ok: true, count: }` or `{ error: }`

Raises:



197
198
199
200
201
202
# File 'lib/browserctl/client.rb', line 197

def import_cookies(name, path)
  raise Browserctl::Error, "cookie file not found: #{path}" unless File.exist?(path)

  cookies = JSON.parse(File.read(path), symbolize_names: true)
  call("import_cookies", name: name, cookies: cookies)
end

Navigates a page to a URL. Returns ‘challenge: true` when Cloudflare is detected.

Parameters:

  • name (String)

    logical page name

  • url (String)

    destination URL

Returns:

  • (Hash)

    ‘{ ok: true, url:, challenge: }` or `{ error: }`



44
# File 'lib/browserctl/client.rb', line 44

def navigate(name, url)        = call("navigate", name: name, url: url)

#page_close(name) ⇒ Hash

Closes a named page and removes it from the session.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



34
# File 'lib/browserctl/client.rb', line 34

def page_close(name)           = call("page_close", name: name)

#page_focus(name) ⇒ Hash

Brings the named page’s tab to front. Only works when browserd was started with –headed.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



144
# File 'lib/browserctl/client.rb', line 144

def page_focus(name) = call("page_focus", name: name)

#page_listHash

Lists all open page names.

Returns:

  • (Hash)

    ‘{ pages: [String] }`



38
# File 'lib/browserctl/client.rb', line 38

def page_list                  = call("page_list")

#page_open(name, url: nil) ⇒ Hash

Opens or focuses a named browser page.

Parameters:

  • name (String)

    logical page name

  • url (String, nil) (defaults to: nil)

    optional URL to navigate to after opening

Returns:

  • (Hash)

    ‘{ ok: true, name: }` or `{ error: }`



29
# File 'lib/browserctl/client.rb', line 29

def page_open(name, url: nil)  = call("page_open",  name: name, url: url)

#pause(name, message: nil) ⇒ Hash

Pauses automation on a page so a human can interact directly.

Parameters:

  • name (String)

    logical page name

  • message (String, nil) (defaults to: nil)

    optional message displayed to the human

Returns:

  • (Hash)

    ‘{ ok: true, paused: true, message: }` or `{ error: }`



129
# File 'lib/browserctl/client.rb', line 129

def pause(name, message: nil)  = call("pause",  name: name, message: message)

#pingHash

Checks if browserd is alive.

Returns:

  • (Hash)

    ‘{ ok: true, pid: }` or raises if daemon is not running



119
# File 'lib/browserctl/client.rb', line 119

def ping                       = call("ping")

#press(name, key) ⇒ Hash

Fires a keydown + keyup event for the given key name on a page.

Parameters:

  • name (String)

    logical page name

  • key (String)

    key name e.g. “Enter”, “Tab”, “Escape”, “ArrowDown”

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



252
# File 'lib/browserctl/client.rb', line 252

def press(name, key) = call("press", name: name, key: key)

#resume(name) ⇒ Hash

Resumes automation on a paused page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true, paused: false }` or `{ error: }`



134
# File 'lib/browserctl/client.rb', line 134

def resume(name)               = call("resume", name: name)

#screenshot(name, path: nil, full: false) ⇒ Hash

Takes a screenshot of a named page.

Parameters:

  • name (String)

    logical page name

  • path (String, nil) (defaults to: nil)

    output path (default: ~/.browserctl/screenshots/)

  • full (Boolean) (defaults to: false)

    capture full page (default: false)

Returns:

  • (Hash)

    ‘{ ok: true, path: }` or `{ error: }`



88
# File 'lib/browserctl/client.rb', line 88

def screenshot(name, path: nil, full: false) = call("screenshot", name: name, path: path, full: full)

#select(name, selector = nil, value = nil, ref: nil) ⇒ Hash

Sets a <select> element’s value and fires a change event.

Parameters:

  • name (String)

    logical page name

  • selector (String, nil) (defaults to: nil)

    CSS selector for the select element

  • value (String, nil) (defaults to: nil)

    option value to select

  • ref (String, nil) (defaults to: nil)

    element ref from a prior snapshot

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



294
295
296
297
298
299
300
301
302
303
304
# File 'lib/browserctl/client.rb', line 294

def select(name, selector = nil, value = nil, ref: nil)
  unless selector || ref
    raise Browserctl::Error.new(
      "select: provide selector or ref",
      code: Browserctl::Error::Codes::INVALID_SELECTOR_REF,
      context: { method: :select, name: name }
    )
  end

  call("select", name: name, selector: selector, ref: ref, value: value)
end

Sets a cookie on a named page.

Parameters:

  • name (String)

    logical page name

  • cookie_name (String)

    cookie name (e.g. “cf_clearance”)

  • value (String)

    cookie value

  • domain (String)

    cookie domain (e.g. “.example.com”)

  • path (String) (defaults to: "/")

    cookie path (default: “/”)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



169
170
171
172
# File 'lib/browserctl/client.rb', line 169

def set_cookie(name, cookie_name, value, domain, path: "/")
  call("set_cookie", name: name, cookie_name: cookie_name,
                     value: value, domain: domain, path: path)
end

#shutdownHash

Shuts down browserd gracefully.

Returns:

  • (Hash)

    ‘{ ok: true }`



123
# File 'lib/browserctl/client.rb', line 123

def shutdown                   = call("shutdown")

#snapshot(name, format: "elements", diff: false) ⇒ Hash

Takes a DOM snapshot. Returns ‘challenge: true` when Cloudflare is detected.

Parameters:

  • name (String)

    logical page name

  • format (String) (defaults to: "elements")

    “elements” (interactable elements JSON) or “html” (raw HTML)

  • diff (Boolean) (defaults to: false)

    return only elements changed since last snapshot

Returns:

  • (Hash)

    ‘{ ok: true, snapshot:, challenge: }` or `{ ok: true, html:, challenge: }` or `{ error: }`



95
96
97
# File 'lib/browserctl/client.rb', line 95

def snapshot(name, format: "elements", diff: false)
  call("snapshot", name: name, format: format, diff: diff)
end

#state_delete(name) ⇒ Object

Permanently deletes a state bundle.



341
# File 'lib/browserctl/client.rb', line 341

def state_delete(name) = call("state_delete", name: name)

#state_info(name) ⇒ Object

Reads a single bundle’s manifest.



338
# File 'lib/browserctl/client.rb', line 338

def state_info(name) = call("state_info", name: name)

#state_listObject

Lists all stored state bundles (manifest only — no payload decryption).



335
# File 'lib/browserctl/client.rb', line 335

def state_list = call("state_list")

#state_load(name, passphrase: nil, skip_auth_check: false) ⇒ Object

Restores a .bctl bundle into the running daemon. The daemon runs the auth_required detector against the bundle’s cookies before applying; callers that have already verified the bundle (e.g. workflow ‘load_state` after a successful rotate) can pass `skip_auth_check: true` to bypass it.



330
331
332
# File 'lib/browserctl/client.rb', line 330

def state_load(name, passphrase: nil, skip_auth_check: false)
  call("state_load", name: name, passphrase: passphrase, skip_auth_check: skip_auth_check)
end

#state_save(name, origins: nil, flow: nil, flow_version: nil, passphrase: nil) ⇒ Hash

Saves browser state (cookies + storage) into a single .bctl bundle.

Returns:

  • (Hash)

    ‘{ ok:, path:, origins:, cookies:, encrypted: }` or `{ error: }`



319
320
321
322
323
# File 'lib/browserctl/client.rb', line 319

def state_save(name, origins: nil, flow: nil, flow_version: nil, passphrase: nil)
  call("state_save",
       name: name, origins: origins, flow: flow,
       flow_version: flow_version, passphrase: passphrase)
end

#storage_delete(name, stores: "all") ⇒ Hash

Clears localStorage and/or sessionStorage for the page.

Parameters:

  • name (String)

    logical page name

  • stores (String) (defaults to: "all")

    “local”, “session”, or “all” (default: “all”)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



244
245
246
# File 'lib/browserctl/client.rb', line 244

def storage_delete(name, stores: "all")
  call("storage_delete", name: name, stores: stores)
end

#storage_export(name, path, stores: "all") ⇒ Hash

Exports localStorage and/or sessionStorage to a JSON file.

Parameters:

  • name (String)

    logical page name

  • path (String)

    destination file path

  • stores (String) (defaults to: "all")

    “local”, “session”, or “all” (default: “all”)

Returns:

  • (Hash)

    ‘{ ok: true, path:, key_count: }` or `{ error: }`



228
229
230
# File 'lib/browserctl/client.rb', line 228

def storage_export(name, path, stores: "all")
  call("storage_export", name: name, path: path, stores: stores)
end

#storage_get(name, key, store: "local") ⇒ Hash

Returns the value of a localStorage or sessionStorage key.

Parameters:

  • name (String)

    logical page name

  • key (String)

    storage key

  • store (String) (defaults to: "local")

    “local” or “session” (default: “local”)

Returns:

  • (Hash)

    ‘{ ok: true, value: }` or `{ error: }`



209
210
211
# File 'lib/browserctl/client.rb', line 209

def storage_get(name, key, store: "local")
  call("storage_get", name: name, key: key, store: store)
end

#storage_import(name, path) ⇒ Hash

Imports storage keys from a JSON file into the page’s localStorage.

Parameters:

  • name (String)

    logical page name

  • path (String)

    source file path

Returns:

  • (Hash)

    ‘{ ok: true, origins: N, key_count: M }` or `{ error: }`



236
237
238
# File 'lib/browserctl/client.rb', line 236

def storage_import(name, path)
  call("storage_import", name: name, path: path)
end

#storage_set(name, key, value, store: "local") ⇒ Hash

Sets a localStorage or sessionStorage key.

Parameters:

  • name (String)

    logical page name

  • key (String)

    storage key

  • value (String)

    storage value

  • store (String) (defaults to: "local")

    “local” or “session” (default: “local”)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



219
220
221
# File 'lib/browserctl/client.rb', line 219

def storage_set(name, key, value, store: "local")
  call("storage_set", name: name, key: key, value: value, store: store)
end

#store(key, value) ⇒ Hash

Stores a value in the daemon-scoped key-value store.

Parameters:

  • key (String)

    storage key

  • value (Object)

    value to store (must be JSON-serialisable)

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



150
# File 'lib/browserctl/client.rb', line 150

def store(key, value)          = call("store", key: key, value: value)

#upload(name, selector = nil, path = nil, ref: nil) ⇒ Hash

Sets a file-input element to the given file path.

Parameters:

  • name (String)

    logical page name

  • selector (String, nil) (defaults to: nil)

    CSS selector for the file input

  • path (String, nil) (defaults to: nil)

    absolute or relative file path

  • ref (String, nil) (defaults to: nil)

    element ref from a prior snapshot

Returns:

  • (Hash)

    ‘{ ok: true }` or `{ error: }`



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/browserctl/client.rb', line 276

def upload(name, selector = nil, path = nil, ref: nil)
  unless selector || ref
    raise Browserctl::Error.new(
      "upload: provide selector or ref",
      code: Browserctl::Error::Codes::INVALID_SELECTOR_REF,
      context: { method: :upload, name: name }
    )
  end

  call("upload", name: name, selector: selector, ref: ref, path: path)
end

#url(name) ⇒ Hash

Returns the current URL of a named page.

Parameters:

  • name (String)

    logical page name

Returns:

  • (Hash)

    ‘{ ok: true, url: }` or `{ error: }`



109
# File 'lib/browserctl/client.rb', line 109

def url(name)                  = call("url",      name: name)

#wait(name, selector, timeout: 30) ⇒ Hash

Waits for a CSS selector to appear within the given timeout.

Parameters:

  • name (String)

    logical page name

  • selector (String)

    CSS selector to wait for

  • timeout (Numeric) (defaults to: 30)

    seconds before giving up (default: 30)

Returns:

  • (Hash)

    ‘{ ok: true, selector: }` or `{ error: }`



104
# File 'lib/browserctl/client.rb', line 104

def wait(name, selector, timeout: 30) = call("wait", name: name, selector: selector, timeout: timeout)