From b633d081176b8016e752d4161f3a917c684495ea Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Wed, 14 Oct 2026 11:29:51 -0400 Subject: [PATCH 1/3] docs: release the extension as 1.1.0 Record the titled tab groups and the other product fixes, and show the group in the README. A v* tag that does not match package.json skips the headless npm publish. --- .github/workflows/release.yml | 22 ++++++++++++++++++++++ CHANGELOG.md | 12 ++++++++++++ README.md | 7 ++++++- docs/img/tab-group-title.png | Bin 0 -> 9305 bytes extension/package-lock.json | 4 ++-- extension/package.json | 2 +- package-lock.json | 2 +- 7 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 docs/img/tab-group-title.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b733fe5..3a7dd01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,30 @@ on: - 'v*' jobs: + release-plan: + name: Decide publish path + runs-on: ubuntu-latest + outputs: + publish_npm: ${{ steps.check.outputs.publish_npm }} + steps: + - uses: actions/checkout@v4 + - id: check + shell: bash + run: | + tag="${GITHUB_REF_NAME#v}" + pkg="$(node -p "require('./package.json').version")" + if [ "$tag" = "$pkg" ]; then + echo "publish_npm=true" >> "$GITHUB_OUTPUT" + echo "Tag v$tag matches package.json. Publishing the headless package." + else + echo "publish_npm=false" >> "$GITHUB_OUTPUT" + echo "Tag v$tag does not match package.json $pkg. Skipping the headless npm publish and binary build." + fi + build-daemon: name: Build Daemon Bundles + needs: release-plan + if: needs.release-plan.outputs.publish_npm == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c70f24..4fa09b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.0] - 2026-10-14 + +- Added `--group-title` on `open` and `goto`. Tabs for one task join a Chrome tab group with that title and a color. The same title keeps joining the group. A different title starts a new group. With no title, the group stays named Browser Hand. If a saved group cannot be reused in that tab's window, Browser Hand opens a replacement group instead of failing the command. +- Added `steps`, one call for a repeated form (click, fill, type, evaluate, snapshot). The call stops at the first miss, and step snapshots leave formatted secrets out. +- Added a scoped snapshot (open dialog plus main by default) and clicks by snapshot ref or by role and accessible name. Covered controls are skipped. `doctor` stays a short health check. `tabs` is the inventory. +- Snapshot `forms` lists the labeled fields in that same scope. +- Fixed fill so a missed field fails the command, and the read-back keeps unicode and punctuation. A control whose form id is missing is not attached. +- Fixed combobox fill so it types, then chooses the matching option. +- Fixed `open` so an existing tab is reused instead of a new blank one. Snapshot and evaluate no longer create a named tab just to read the page. +- Fixed the debugger infobar: after you dismiss "Browser Hand started debugging this browser", that tab stays on the scripting path and the bar is not brought back. +- Improved the toolbar icon so the crop is the solid glove, not the wrist mist. + ## [0.2.9] - 2026-07-14 - Added configurable per-browser idle cleanup with `--idle-timeout`, `DEV_BROWSER_IDLE_TIMEOUT_MS`, and `~/.dev-browser/config.json` support. Idle cleanup preserves persistent profiles, excludes externally connected Chrome, and safely rechecks activity under the per-browser lock before closing. diff --git a/README.md b/README.md index bb5be8f..0c44c75 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Browser Hand takes a different route: a small **Chrome extension** bridges your - **Real sessions** — use the Chrome profile you already use (cookies, passkeys, extensions, open tabs). - **Background-first** — open and automate tabs without stealing OS focus; optional one-shot focus only when a human must look (2FA, captcha, confirm). - **Agent-shaped CLI** — `tabs`, `doctor`, named pages, snapshot, fill, click, type, evaluate, screenshot — JSON out, logs on stderr. +- **Named tab groups.** `--group-title` puts the tabs for one task in a Chrome group with that name and a color. A different title starts a new group. - **Hard pages** — soft recovery when password managers detach the debugger; scripting fallback; shadow DOM / iframe pierce; human-like pointer and contenteditable input. - **Agent gym** — local challenge pages under `extension/challenges/` built to exercise agent failure modes (labels, modals, SPA routes, username fields), not just “click the demo button once.” - **Optional headless** — upstream sandboxed QuickJS + Playwright CLI when you want CI or a clean browser, not your daily profile. @@ -76,12 +77,16 @@ Removes legacy skill aliases named `dev-browser` and prints how to remove an old browser-hand tabs browser-hand snapshot # unique-active tab (last-focused when stamped) browser-hand snapshot --query stripe -browser-hand open --url https://example.com --page-name demo +browser-hand open --url https://example.com --page-name demo --group-title "DNS records" browser-hand snapshot --page-name demo browser-hand click --page-name demo --text "More information..." browser-hand screenshot --page-name demo ``` +The first `open` of a task can name the Chrome tab group. Later opens with the same title join that group. A different title starts a new group and a new color. Leave the flag off and the group stays named Browser Hand. + +![Chrome tab group titled DNS records, holding the Example Domain and Example Domains tabs](docs/img/tab-group-title.png) + Live smoke (uses your real Chrome): ```bash diff --git a/docs/img/tab-group-title.png b/docs/img/tab-group-title.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9eee0a3b4c3ae6708d3c257d93eeb66d406226 GIT binary patch literal 9305 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 VjX1}u;u@#{>E1KatXD@w{~u^ni0=RZ literal 0 HcmV?d00001 diff --git a/extension/package-lock.json b/extension/package-lock.json index cb66bd0..c06915b 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "dev-browser-extension", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dev-browser-extension", - "version": "1.0.0", + "version": "1.1.0", "devDependencies": { "@types/chrome": "^0.1.32", "typescript": "^5.0.0", diff --git a/extension/package.json b/extension/package.json index 25aa576..21cb9de 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "browser-hand-extension", - "version": "1.0.0", + "version": "1.1.0", "type": "module", "scripts": { "dev": "wxt", diff --git a/package-lock.json b/package-lock.json index c4ccd8b..784e3be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ }, "extension": { "name": "browser-hand-extension", - "version": "1.0.0", + "version": "1.1.0", "devDependencies": { "@types/chrome": "^0.1.32", "typescript": "^5.0.0", From 46953febf13487605505ed1b21545f485449309e Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Wed, 14 Oct 2026 11:37:43 -0400 Subject: [PATCH 2/3] fix(daemon): read example.com body text instead of a removed heading example.com no longer has an h1, so the locator tests waited out their timeout. The body still contains the documentation sentence. --- daemon/src/sandbox/__tests__/protocol-bridge.test.ts | 2 +- daemon/src/sandbox/__tests__/sandbox-integration.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/src/sandbox/__tests__/protocol-bridge.test.ts b/daemon/src/sandbox/__tests__/protocol-bridge.test.ts index 61da58c..732f123 100644 --- a/daemon/src/sandbox/__tests__/protocol-bridge.test.ts +++ b/daemon/src/sandbox/__tests__/protocol-bridge.test.ts @@ -37,7 +37,7 @@ describe.sequential("protocol bridge", () => { await page.goto("https://example.com"); await expect(page.title()).resolves.toBe("Example Domain"); - await expect(page.locator("h1").textContent()).resolves.toBe("Example Domain"); + await expect(page.locator("body").textContent()).resolves.toContain("documentation examples"); await expect(page.evaluate(() => document.title)).resolves.toBe("Example Domain"); }, 120_000); diff --git a/daemon/src/sandbox/__tests__/sandbox-integration.test.ts b/daemon/src/sandbox/__tests__/sandbox-integration.test.ts index 923ce8b..0fcbe89 100644 --- a/daemon/src/sandbox/__tests__/sandbox-integration.test.ts +++ b/daemon/src/sandbox/__tests__/sandbox-integration.test.ts @@ -85,7 +85,7 @@ describe.sequential("QuickJS sandbox integration", () => { ` const page = await browser.getPage("locator"); await page.goto("https://example.com"); - console.log(await page.locator("h1").textContent()); + console.log(await page.locator("body").textContent()); `, manager, "default", From a86c2d0130aab1815a091619fe0da527bab775a7 Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Wed, 14 Oct 2026 11:47:27 -0400 Subject: [PATCH 3/3] fix(daemon): expect the example.com sentence the locator actually returns The page title is still Example Domain. The body text is the documentation sentence, in several languages. --- daemon/src/sandbox/__tests__/sandbox-integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/src/sandbox/__tests__/sandbox-integration.test.ts b/daemon/src/sandbox/__tests__/sandbox-integration.test.ts index 0fcbe89..fb11dbe 100644 --- a/daemon/src/sandbox/__tests__/sandbox-integration.test.ts +++ b/daemon/src/sandbox/__tests__/sandbox-integration.test.ts @@ -95,7 +95,7 @@ describe.sequential("QuickJS sandbox integration", () => { } ); - expect(output.stdout.join("")).toContain("Example Domain"); + expect(output.stdout.join("")).toContain("documentation examples"); }, 120_000); it("supports page.evaluate", async () => {