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;
}