rsclaw 2026.5.1

AI Agent Engine Compatible with OpenClaw
Documentation
package rsclaw:plugin;

interface host-browser {
    browser-open: func(url: string) -> result<string, string>;
    browser-snapshot: func() -> result<string, string>;
    browser-click: func(ref-str: string) -> result<string, string>;
    /// Native (CDP-level) mouse click at viewport coordinates. Use this
    /// when synthetic JS click events are ignored by the page (typical for
    /// React PointerEvent handlers on popups / paywalls).
    browser-click-at: func(x: u32, y: u32) -> result<string, string>;
    browser-fill: func(ref-str: string, text: string) -> result<string, string>;
    browser-press: func(key: string) -> result<string, string>;
    browser-eval: func(code: string) -> result<string, string>;
    browser-wait-text: func(text: string, timeout-ms: u32) -> result<string, string>;
    browser-screenshot: func() -> result<string, string>;
    browser-download: func(ref-str: string, filename: string) -> result<string, string>;
    browser-upload: func(ref-str: string, filepath: string) -> result<string, string>;
    browser-get-url: func() -> result<string, string>;

    /// Wait for an element matching `css-selector` to be present in the DOM.
    /// Polls every 250ms via JS until match or timeout. Returns "ok" on
    /// match, error on timeout. Use `browser-snapshot` afterwards if you
    /// need a clickable ref.
    wait-for-selector: func(css-selector: string, timeout-ms: u32) -> result<string, string>;

    /// Wait until network requests have been quiet for ~500ms, or until
    /// `timeout-ms` total. Returns "ok" or error on timeout.
    wait-for-network-idle: func(timeout-ms: u32) -> result<string, string>;

    /// Run a JavaScript function expression with structured arguments.
    /// `code` MUST evaluate to a function (e.g. `"async (args) => { ... }"`
    /// or `"function(args) { ... }"`). `args-json` is parsed and passed as
    /// the function's first argument. The function's return value is
    /// JSON-stringified and returned. Avoids the brittle string-interpolation
    /// and manual escaping that `browser-eval` requires.
    eval-with-args: func(code: string, args-json: string) -> result<string, string>;

    /// Switch the active browser tab to the most recently opened one.
    /// Useful when an action opens a popup window. Replaces the
    /// `browser-eval("__switch_latest_tab")` magic-string convention.
    switch-latest-tab: func() -> result<string, string>;
}

interface host-runtime {
    log: func(level: string, msg: string);
    sleep: func(ms: u32);
    read-file: func(path: string) -> result<string, string>;
    /// Send a progress/notification message to the user during long operations.
    notify: func(message: string) -> result<string, string>;

    /// Send a message + an inline image. The image-data-uri must be a
    /// `data:image/<format>;base64,<...>` string (the browser-screenshot
    /// host fn already returns this shape). The host always populates
    /// the OutboundMessage's `images` field with the data URI; channel
    /// handlers (feishu, wechat, desktop, etc.) decide how to render —
    /// IM channels upload it inline, desktop renders/saves it natively.
    notify-with-image: func(message: string, image-data-uri: string) -> result<string, string>;

    /// Send a message + a file attachment by absolute path. `mime` like
    /// "video/mp4" or "image/png". The file path must resolve under the
    /// plugin workspace (canonicalized & allowlisted by the host). The
    /// host populates OutboundMessage.files; channel handlers decide
    /// how to deliver (IM channels upload, desktop surfaces the path).
    notify-with-file: func(message: string, file-path: string, mime: string) -> result<string, string>;
}

interface host-storage {
    /// Request a writable absolute path for a new artifact file. The host
    /// picks the location (typically under its base-dir), creates the parent
    /// directory, and returns a normalized absolute path. Plugins MUST use
    /// this instead of constructing filesystem paths themselves — the host
    /// owns the on-disk layout.
    ///
    /// `filename` is a HINT — the host uses its extension to pick a category
    /// (i/v/a/d/f) but ignores the stem and writes a canonical
    /// `dl_<kind>_<YYYYMMDDHHmm><ab>.<ext>` instead. Pass any short
    /// representative name like `"video.mp4"` / `"image.png"`.
    allocate-artifact: func(filename: string) -> result<string, string>;

    /// Allocate a batch of related artifact paths sharing the same base
    /// (timestamp + 2-letter random suffix), differing only in the
    /// `_N` index suffix. Use this when a single tool call produces
    /// several outputs of the same kind (e.g. a 4-image batch).
    /// Returns paths in 1-based order.
    allocate-artifact-group: func(filename: string, count: u32) -> result<list<string>, string>;
}

interface plugin-api {
    handle-tool: func(tool-name: string, args-json: string) -> result<string, string>;
}

world jimeng-plugin {
    import host-browser;
    import host-runtime;
    import host-storage;
    export plugin-api;
}