feat(cli): add browse watch command#2244
Conversation
Add a polling watch command for text, URL, visible, and checked conditions with configurable interval and timeout, plus regex support for text and URL queries. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 111deb7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run. |
There was a problem hiding this comment.
No issues found across 4 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant CLI as CLI Entry Point
participant Watch as browse watch command
participant Resolver as resolveTargetForCommand
participant Runner as runDriverCommandWithTarget
participant WatchLib as pollWatch / createStringMatcher
participant Driver as Stagehand Driver (get/is)
CLI->>Watch: parse(kind, target, flags)
Watch->>Watch: validate flags (interval, regex+kind combo)
Watch->>Resolver: resolveTargetForCommand(session, flags)
Resolver-->>Watch: target object
Watch->>WatchLib: createStringMatcher(query, regex)
WatchLib-->>Watch: matcher function
loop pollWatch (configurable interval/timeout)
Watch->>WatchLib: check() (called by pollWatch)
WatchLib->>Watch: checkCondition(kind, matcher, query, selector, session, target)
alt kind == "visible" or "checked"
Watch->>Runner: runDriverCommandWithTarget(session, target, "is", { check: kind, selector: query })
Runner->>Driver: is({ check, selector })
Driver-->>Runner: { visible/checked: boolean }
Runner-->>Watch: { visible/checked: boolean }
Watch-->>WatchLib: { matched: boolean(value), value: string(bool) }
else kind == "url"
Watch->>Runner: runDriverCommandWithTarget(session, target, "get", { what: "url" })
Runner->>Driver: get({ what: "url" })
Driver-->>Runner: { url: string }
Runner-->>Watch: { url: string }
Watch->>WatchLib: matcher(url) → boolean
Watch-->>WatchLib: { matched, value: url }
else kind == "text"
Watch->>Runner: runDriverCommandWithTarget(session, target, "get", { selector (or body), what: "text" })
Runner->>Driver: get({ selector, what: "text" })
Driver-->>Runner: { text: string }
Runner-->>Watch: { text: string }
Watch->>WatchLib: matcher(text) → boolean
Watch-->>WatchLib: { matched, value: text }
end
WatchLib-->>Watch: WatchAttempt
alt matched == true
WatchLib->>WatchLib: return { attempts, elapsedMs, value }
Watch->>CLI: outputJson { attempts, elapsedMs, kind, matched: true, session, value }
else timeout reached
WatchLib->>WatchLib: throw Error("Watch condition not met...")
Watch->>CLI: outputJson error message
end
end
why
The browse CLI has
wait,is, andget, but no single command to poll for state transitions (e.g. “wait until text appears”, “wait until URL matches”, “wait until selector becomes visible”). Users currently have to script loops manually.what changed
browse watchcommand supporting:text <query>url <query>visible <selector>checked <selector>get/is) under the hood.browseminor release.test plan
pnpm --filter browse buildpnpm --filter browse exec vitest run tests/watch.test.ts tests/driver-commands.test.tsSummary by cubic
Adds a new
browse watchCLI command to wait for text, URL, visibility, or checked state changes with configurable interval, timeout, and optional regex. This removes the need for manual polling loops.browse watch <kind> <target>where kind istext,url,visible, orchecked.--interval <ms>,--timeout <ms>,--regex(text/url),--selector <css>(scope for text).get/isunder the hood; new polling helper with substring or regex matching.browseminor release.Written for commit 111deb7. Summary will update on new commits.