Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,16 @@ browser-hand open --url https://example.com --page-name demo --group-title "DNS
browser-hand snapshot --page-name demo
browser-hand click --page-name demo --text "More information..."
browser-hand screenshot --page-name demo
browser-hand close --page-name demo # done: close the tab Browser Hand opened
```

`close` needs an explicit selector (`--page-name`, `--query`, `--target-*`, or `--group-title`) and never closes the active tab by default. It returns `{ closed, targetId, url, title }`, and `closed` is only `true` once Chrome no longer lists the tab.

- A selector that matches several tabs is refused unless you pass `--all`.
- `--group-title "DNS records"` closes every tab in that Browser Hand tab group.
- It only closes tabs Browser Hand opened. A tab you already had open is refused; `--all` and `--group-title` skip such tabs and list them under `skipped`. Pass `--include-user-tabs` to close one anyway.
- Ownership is tracked by the relay, so restart it (`npm run build:relay && npm run relay`) and reload the extension after upgrading. A relay that predates this fails closed instead of guessing.

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)
Expand Down
35 changes: 35 additions & 0 deletions cli-js/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* browser-hand evaluate --code 'document.title'
* browser-hand goto --url https://example.com
* browser-hand open --url https://example.com --page-name smoke
* browser-hand close --page-name smoke
* browser-hand doctor
*
* Output: JSON on stdout. Exits 0 on success, 1 on tool/handler error,
Expand Down Expand Up @@ -61,6 +62,12 @@ Operations:
focus [--focus window|tab] Bring a tab forward for human input (one-shot; default window).
--reason <why> Required for good audit (e.g. 2fa, confirm-publish).
tabs [--query <text>] List every http(s) tab. Unique-active first; last-focused when stamped.
close (--page-name <n> | --query <text> | --target-id <id> | --group-title <t>)
Close a tab Browser Hand opened. Returns { closed, targetId, url, title }.
Never closes the active tab by default and refuses a selector
that matches several tabs unless --all is given. --group-title
closes every tab in that Browser Hand tab group. Refuses a tab
you had open unless --include-user-tabs is passed.
doctor [--verbose] Health check: status, tabCount, and the active tab. Not a tab list.
relay Start a persistent extension relay (foreground) so the
Chrome extension has a stable endpoint to connect to.
Expand All @@ -83,6 +90,9 @@ Global flags:
--role <role> Click by ARIA role. Requires --name.
--name <name> Accessible name for --role. Exact match.
--group-title <text> Optional tab-group label for open/goto. A new title starts a new group.
With close: the Browser Hand tab group to close.
--all close: allow a selector that matches several tabs to close all of them.
--include-user-tabs close: also close tabs Browser Hand did not open (tabs you had open).
-h, --help Show this help.

Examples:
Expand All @@ -93,6 +103,9 @@ Examples:
browser-hand tabs
browser-hand tabs --query stripe
browser-hand snapshot --query stripe
browser-hand close --page-name smoke
browser-hand close --query example.com --all
browser-hand close --group-title "DNS records"
browser-hand doctor
browser-hand click --page-name work --role combobox --name "Type"
browser-hand click --page-name work --ref e4
Expand All @@ -112,6 +125,10 @@ function parseArgs(argv) {
args.quiet = true;
} else if (tok === "--full-page") {
args.fullPage = true;
} else if (tok === "--all") {
args.all = true;
} else if (tok === "--include-user-tabs") {
args.includeUserTabs = true;
} else if (tok.startsWith("--")) {
const key = tok.slice(2);
const next = argv[i + 1];
Expand Down Expand Up @@ -269,6 +286,24 @@ function buildHandlerInput(operation, args) {
input.query = args.query.trim();
}
break;
case "close":
if (args["group-title"] === true) {
fail("--group-title requires a value");
}
if (typeof args["group-title"] === "string" && args["group-title"].trim()) {
input.groupTitle = args["group-title"].trim();
}
if (args.all === true) input.all = true;
if (args.includeUserTabs === true) input.includeUserTabs = true;
if (!input.pageName && !input.target && !input.groupTitle) {
fail(
"close requires --page-name, --query, --target-id/--target-url/--target-title, or --group-title"
);
}
if (input.pageName && (input.target || input.groupTitle)) {
fail("close --page-name cannot be combined with --query, --target-*, or --group-title");
}
break;
case "snapshot":
if (typeof args.scope === "string" && args.scope.trim()) {
const scope = args.scope.trim();
Expand Down
Loading
Loading