Core Concepts

Selectors and Persist Keys

OpenSteer targets DOM nodes with element counters, CSS selectors, or saved `persist` keys.

Selectors and Persist Keys

OpenSteer has one targeting model across the SDK: every DOM action targets exactly one of element, selector, or persist.

element

Use the current snapshot counter.

await opensteer.click({ element: 3 });
await opensteer.input({ element: 5, text: "laptop" });

This is the fastest way to explore a page, but it only works against the current snapshot.

selector

Use a CSS selector directly.

await opensteer.click({ selector: "#submit" });
await opensteer.hover({ selector: "[data-testid='menu']" });

This is useful when you already know the selector and do not need a snapshot counter.

persist

Use a saved key that points to a previously learned deterministic path.

await opensteer.click({ persist: "primary call to action" });
await opensteer.input({
  persist: "search input",
  text: "laptop",
});

You usually create that key during exploration:

await opensteer.click({
  element: 3,
  persist: "primary call to action",
});

On the first run, OpenSteer resolves the element from the current page and saves the deterministic path. On later runs, persist reuses that saved path.

CLI behavior

The CLI DOM commands are positional and exploration-oriented:

opensteer click 3 --workspace demo --persist "primary call to action"
opensteer input 5 laptop --workspace demo --persist "search input"

The CLI uses the element you pass today and can save a persist key while doing it.

Persist-only DOM targeting is part of the SDK surface:

await opensteer.click({ persist: "primary call to action" });

Extraction uses the same idea

Extraction can also save a reusable descriptor:

await opensteer.extract({
  persist: "page summary",
  schema: {
    title: { element: 2 },
    url: { source: "current_url" },
  },
});

const replayed = await opensteer.extract({
  persist: "page summary",
});

Where descriptors are stored

Persisted DOM and extraction descriptors live under:

.opensteer/workspaces/<workspace>/registry/descriptors/

That storage is workspace-scoped, which is why workspace naming matters.