feat(cli): add browse clipboard commands#2240
Conversation
Expose the SDK clipboard API through the browse driver and oclif so terminal and agent workflows can read, write, paste, copy, and clear the browser clipboard without raw CDP calls. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: c372551 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 12 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant User as CLI User
participant Oclif as oclif Command
participant CLI as Driver Command CLI
participant Handlers as Clipboard Handlers
participant Manager as DriverSessionManager
participant Context as BrowserContext
participant Clipboard as Clipboard API
Note over User,Clipboard: NEW: Browse Clipboard Commands
User->>Oclif: browse clipboard read|write|paste|copy|cut|clear [--session] [args]
Oclif->>Oclif: Parse flags/args
alt clipboard.write
Oclif->>CLI: runDriverCommandFromFlags("clipboard.write", {text})
else clipboard.paste
Oclif->>CLI: runDriverCommandFromFlags("clipboard.paste", {shortcut})
else other commands
Oclif->>CLI: runDriverCommandFromFlags("clipboard.<cmd>", {})
end
CLI->>Handlers: Dispatch to clipboard.<cmd> handler
Handlers->>Manager: browserContext()
Manager-->>Handlers: BrowserContext instance
alt clipboard.read
Handlers->>Clipboard: readText()
Clipboard-->>Handlers: clipboard text
Handlers-->>CLI: { text }
else clipboard.write
Handlers->>Clipboard: writeText(text)
Clipboard-->>Handlers: void
Handlers-->>CLI: { ok: true }
else clipboard.paste
Handlers->>Clipboard: paste({ shortcut } | undefined)
Clipboard-->>Handlers: void
Handlers-->>CLI: { ok: true }
else clipboard.copy|cut|clear
Handlers->>Clipboard: copy() | cut() | clear()
Clipboard-->>Handlers: void
Handlers-->>CLI: { ok: true }
end
CLI-->>Oclif: Result
Oclif-->>User: Display result
|
|
||
| static override examples = [ | ||
| "browse clipboard clear", | ||
| "browse clipboard clear --session research", |
There was a problem hiding this comment.
is clipboard siloed by browser / tab like this?
There was a problem hiding this comment.
it’s not tab-siloed.
browse clipboard * runs against the active page (for focus + permission grant), but the clipboard value is shared across tabs in that browser session.
so when you write in tab 0 and switch to tab 1, read returns the same value.
So --session here selects which browse daemon/browser session to target, not per-tab clipboard isolation.
## Summary Fork-triggered `pull_request` workflow runs don't receive repo configuration variables or secrets, so `STAINLESS_ORG`/`STAINLESS_PROJECT` arrive empty and the Stainless action dies immediately with `Input required and not supplied: project` on every external-contributor PR. Seen on #2241 (claimed copy of #2240): the fork PR's failing `preview` check attached to the shared head SHA, leaving the claimed PR red through no fault of its own. This guards both jobs on `github.event.pull_request.head.repo.full_name == github.repository`: - `preview` — skips on fork PRs instead of failing. Claimed copies of external PRs run in-repo, so SDK preview coverage is preserved. - `merge` — same defect: a directly-merged fork PR would trigger a guaranteed-failing merge build (still no vars/secrets on the `closed` event). Skipping is strictly better than failing; the claim flow means merged external work is always in-repo anyway. The `full_name` comparison is used instead of `!head.repo.fork` so a deleted fork repo (`head.repo == null`) skips rather than runs-and-fails. Linear: [STG-2257](https://linear.app/browserbase/issue/STG-2257/fixci-skip-stainless-sdk-jobs-for-fork-prs) ## E2E Test Matrix | Command / flow | Observed output | Confidence / sufficiency | | --- | --- | --- | | Failing fork run: [actions/runs/27375420406](https://github.com/browserbase/stagehand/actions/runs/27375420406/job/80903332001) (triggered by fork PR #2240) | Env dump shows `STAINLESS_ORG:` and `STAINLESS_PROJECT:` empty; action exits with `Error: Input required and not supplied: project` | Proves the root cause is withheld vars on fork runs, not the spec or config — exactly the case the new guard skips | | `actionlint .github/workflows/stainless.yml` on this branch | Exit 0, no findings | Validates workflow schema and the new `if:` expression syntax | | `preview` check on **this PR** (in-repo branch) | See checks below — job runs (guard evaluates true for same-repo PRs) and builds SDK previews | Live proof the guard doesn't skip in-repo PRs; the fork-skip path can only be exercised by the next real fork PR | 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Skip Stainless CI jobs for fork-origin PRs to avoid failures from missing repo vars/secrets. Keeps external PRs green while coverage runs on in-repo “claimed” copies. Addresses Linear STG-2257. - **Bug Fixes** - Skip `preview` and `merge` unless `github.event.pull_request.head.repo.full_name == github.repository`; `merge` still requires closed+merged into `main`. - Use the `full_name` check to safely skip deleted forks (`head.repo` can be null). <sup>Written for commit 58c5891. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2243?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
##why
The SDK exposes context.clipboard, but the browse CLI had no way to use it — users had to fall back to raw CDP.
##what changed
-Six driver commands: clipboard.read, write, paste, copy, cut, clear
-Matching oclif commands: browse clipboard read|write|paste|copy|cut|clear
-Wired through DriverSessionManager.browserContext() → context.clipboard (no core changes)
paste supports --shortcut Control+V|Meta+V|ControlOrMeta+V
##test plan
pnpm exec vitest run tests/clipboard.test.ts tests/driver-commands.test.ts
Manual: write → read → paste on a focused input
Summary by cubic
Add clipboard support to the
browseCLI with commands to read, write, paste, copy, cut, and clear. Enables agents and terminal users to use the SDK clipboard without raw CDP.clipboard.read|write|paste|copy|cut|clear, wired viaDriverSessionManager.browserContext()tocontext.clipboard.oclifcommands:browse clipboardread|write|paste|copy|cut|clear;pastesupports--shortcut(Control+V,Meta+V,ControlOrMeta+V).vitesttests for handler wiring and CLI registration.Written for commit c372551. Summary will update on new commits.