Skip to content

Scripting Overview

Guida includes a full scripting environment for browser automation. Scripts interact with pages, manage data, and orchestrate workflows through the g.* API.

Three engines are available:

  • JavaScript (ClearScript V8) — Full ES2024+ compatibility, top-level await, native Promises, and ES module imports. Powered by the same V8 engine used in Chrome and Node.js.
  • Lua (LuaCSharp) — Clean, table-based API using standard Lua syntax. Fully integrated with workspace libraries via require().
  • Janet (JanetSharp) — Expressive functional Lisp dialect with immutable data structures and a powerful built-in PEG (Parsing Expression Grammar) engine.

All three engines share the same g.* API surface.

Because automation scripts are fundamentally asynchronous, Guida supports top-level await for scripts that do not require an early return statement:

// Top-level await works out of the box
const tabs = g.tabs.getTabs();
g.log("Open tabs: " + tabs.length);

For scripts that need to return early, wrap your code in an async function main() and await main(); at the end.

Press F5 in the script editor to run.

Error handling in Guida depends on the engine you are using.

In JavaScript and Janet, all g.* methods return values directly and throw exceptions on error. In JavaScript, use try/catch to handle errors:

try {
await g.dom.click("#submit");
} catch (e) {
g.log("Click failed: " + e.message);
return;
}

In Lua, the g.* API uses idiomatic multiple returns (nil, error) instead of throwing exceptions. This ensures that the Visual Studio debugger doesn’t break on “unhandled” exceptions during development.

Always check the first return value for success:

local success, err = g.nav.navigateToUrl("https://example.com")
if not success then
g.log("Error: Navigation failed: " .. err)
else
g.log("Page loaded successfully")
end

Many browser operations are async. In JavaScript, use await. In Lua and Janet, calls are automatically awaited by the engine:

JavaScript:

async function main() {
await g.nav.navigateToUrl("https://example.com");
await g.dom.waitForDomStable();
const html = await g.page.getDom();
g.log("Page loaded, DOM length: " + html.length);
}

Lua:

g.nav.navigateToUrl("https://example.com")
g.dom.waitForDomStable()
local html = g.page.getDom()
g.log("Page loaded, DOM length: " .. #html)

Janet:

(g.nav.navigateToUrl "https://example.com")
(g.dom.waitForDomStable)
(def html (g.page.getDom))
(g.log (string "Page loaded, DOM length: " (length html)))

Guida allows you to write reusable functions in shared files.

Use standard ES import/export syntax. Path is resolved relative to the calling script’s directory, falling back to the workspace root.

lib/helpers.js:

export async function loginToApp(username, password) {
await g.nav.navigateToUrl("https://example.com/login");
await g.dom.enterText("#user", username);
await g.dom.enterText("#pass", password);
await g.dom.click("#submit");
}

myscript.js:

import { loginToApp } from "./lib/helpers.js";
await loginToApp("admin", "secure123");
const data = await g.dom.extractContent(".stats");
g.log("Dashboard stats: " + data);

Use the standard require() function. Guida searches for modules in the workspace lib/ folder and the current workflow’s lib/ and scripts/ folders.

lib/utils.lua:

local M = {}
function M.hello() g.log("Hello from library!") end
return M

myscript.lua:

local utils = require("utils")
utils.hello()

Use the import macro to load Janet modules from the workspace lib/ and scripts/ folders.

lib/utils.janet:

(defn hello []
(g.log "Hello from Janet library!"))

myscript.janet:

(import utils)
(utils/hello)

Set a script execution timeout (in milliseconds):

// @timeout 60000
async function main() {
// This script will be cancelled after 60 seconds
}

The default timeout is 10 seconds. Place the pragma before any code.

Guard scripts that depend on g.worker.getContext() from running outside a worker context:

// @require-context
async function main() {
const ctx = g.worker.getContext();
// This script will only run inside a queue processor or worker pool
}

If the script is run standalone (F5, MCP, event trigger), it fails immediately with a clear error instead of failing mid-execution. See Queues and Review for details.

Use g.log() to write to the Console pane:

g.log("Processing item: " + item.name);

The built-in editor provides:

  • IntelliSense — auto-completions for all g.* methods and their parameters
  • Signature help — parameter hints shown when typing (
  • Hover docs — type information on mouse hover
  • Chrome DevTools Debugging — click the Debug button on any .js file to launch an external Chrome DevTools instance connected directly to the V8 engine. Set breakpoints, step through code, and inspect variables using the standard Chrome interface.
  • Find/replace — with regex support
  • Bracket matching and indentation guides

Some websites detect automated browsers. Use stealth: true to inject anti-detection patches before page scripts run:

await g.nav.navigateToUrl("https://example.com", { stealth: true });

This applies five patches: hides navigator.webdriver, injects window.chrome.runtime, fixes Permissions.prototype.query, fakes navigator.plugins, and sets navigator.languages to ["en-US", "en"].

For granular control, pass a StealthOptions object to enable/disable individual patches. You can also detect JavaScript-based redirects with { detectRedirects: true }. See the API Reference for full details.

Guida includes text-to-speech via Windows Speech Synthesis. Configure the default voice and speech rate in Settings > TTS.

Settings > TTS tab showing Default Voice dropdown, Default Rate slider, and Preview button

Scripts can speak text and override defaults per-call:

await g.tts.speak("Processing complete", { voice: "Microsoft David", rate: 2 });

See the API Reference for all methods.

All Guida automation methods are available through the global g namespace. While JavaScript and Lua share the same API surface, there are two key differences in how they are used:

Unlike some other C# Lua integrations, Guida’s API uses the dot (.) operator for all calls (e.g., g.dom.click("#btn")). Do not use the colon (:) operator (e.g., g.dom:click), as the API methods are bound directly to C# services and do not expect a self argument.

Lua uses tables for data structures. When an API method returns an object (like a browser tab or a document from the store), it is returned as a Lua table with standard 1-based indexing for arrays.

-- Working with the document store
local doc, err = g.store.get("products", "item-1")
if doc and doc.data then
g.log("Found product: " .. doc.data.name)
end

Janet is a functional, Lisp-like language. Its syntax is heavily based on s-expressions (parentheses) and prefix notation.

Instead of using dot notation with parentheses outside like g.dom.click("#btn"), you wrap the entire expression in parentheses: (g.dom.click "#btn").

When an API method returns an object (like a TabInfo or a document from the store), it is returned as a Janet dictionary. You access properties using the get or get-in function:

# Working with the document store
(def doc (g.store.get "products" "item-1"))
(if doc
(g.log (string "Found product: " (get (get doc "data") "name"))))
  • API Reference — full method listing for all g.* namespaces
  • Recipes — practical scripts based on real examples from the app
  • Workspaces — organize scripts, data, and config in workspace folders
  • Quick Start — hands-on 5-minute walkthrough