tabctl 0.6.0-alpha.9 → 0.6.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -76
- package/dist/extension/background.js +1465 -474
- package/dist/extension/lib/content.js +293 -30
- package/dist/extension/lib/screenshot.js +9 -1
- package/dist/extension/manifest.json +4 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# tabctl
|
|
2
2
|
|
|
3
|
-
Every open tab is a thread you forgot to pull. Tabctl
|
|
3
|
+
Every open tab is a thread you forgot to pull. Tabctl helps you query and change them safely.
|
|
4
4
|
|
|
5
|
-
A command-line instrument for browser tab orchestration
|
|
5
|
+
A command-line instrument for browser tab orchestration, now centered on a GraphQL API exposed through `tabctl query` and `tabctl schema`, plus `ping` and `history` convenience commands. Built for humans who hoard tabs and the AI agents who clean up after them.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -25,18 +25,17 @@ cargo install --path rust/crates/tabctl
|
|
|
25
25
|
|
|
26
26
|
## Agent Skill
|
|
27
27
|
|
|
28
|
-
Give your coding agent eyes into the browser.
|
|
28
|
+
Give your coding agent eyes into the browser. Install the tabctl skill via the Skills CLI:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
tabctl skill
|
|
32
|
-
# or: npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode -a github-copilot -a claude-code
|
|
31
|
+
npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode -a github-copilot -a claude-code
|
|
33
32
|
```
|
|
34
33
|
|
|
35
34
|
## Safety
|
|
36
35
|
|
|
37
36
|
Nothing leaves your machine. No cloud. No telemetry. Just a socket between your terminal and your browser, quiet as rain on neon.
|
|
38
37
|
|
|
39
|
-
Every mutation is undoable — `
|
|
38
|
+
Every mutation is undoable — `undoAction` rewinds closes, archives, and group changes like they never happened. A configurable policy layer shields pinned tabs and protected domains from accidental destruction. You pull the trigger; tabctl keeps the safety on until you mean it.
|
|
40
39
|
|
|
41
40
|
## What You Can Say
|
|
42
41
|
|
|
@@ -130,75 +129,80 @@ Optional setup release overrides:
|
|
|
130
129
|
|
|
131
130
|
### 3. Verify and explore
|
|
132
131
|
|
|
133
|
-
<!-- test: "ping sends ping action", "list sends list action" -->
|
|
134
132
|
```bash
|
|
135
|
-
tabctl ping
|
|
136
|
-
tabctl
|
|
133
|
+
tabctl ping
|
|
134
|
+
tabctl query '{ tabs { total items { tabId title url } } }'
|
|
135
|
+
tabctl schema
|
|
137
136
|
```
|
|
138
137
|
|
|
139
138
|
> **Multiple browsers?** See [Multi-Browser Setup](#multi-browser-setup) for running tabctl with both Chrome and Edge.
|
|
140
139
|
|
|
141
140
|
## Commands
|
|
142
141
|
|
|
143
|
-
<!-- test: "list sends list action", "analyze passes tab ids and progress option", "inspect passes signal options", "close without confirm fails", "report format md returns markdown content", "undo sends undo action with txid" -->
|
|
144
142
|
| Command | Description |
|
|
145
143
|
|---------|-------------|
|
|
146
|
-
| `tabctl
|
|
147
|
-
| `tabctl
|
|
148
|
-
| `tabctl
|
|
149
|
-
| `tabctl
|
|
150
|
-
| `tabctl
|
|
151
|
-
| `tabctl close --tab <id>` | Close tabs with full undo support |
|
|
152
|
-
| `tabctl report` | Generate reports in JSON, Markdown, or CSV |
|
|
153
|
-
| `tabctl undo` | Revert the last action |
|
|
144
|
+
| `tabctl query '<GRAPHQL>'` | Query and mutate browser state through GraphQL |
|
|
145
|
+
| `tabctl schema` | Print the GraphQL schema |
|
|
146
|
+
| `tabctl ping` | Check host/browser connectivity and runtime version sync |
|
|
147
|
+
| `tabctl history` | Show recent undo history entries |
|
|
148
|
+
| `tabctl setup`, `doctor`, `policy`, `profile-*` | Local/admin profile management |
|
|
154
149
|
|
|
155
|
-
|
|
150
|
+
Read-only browser features include:
|
|
151
|
+
- `inspectTabs` for page metadata and selector-based reads
|
|
152
|
+
- `readTabs` for page HTML → Markdown conversion with Kreuzberg preprocessing and per-tab diagnostics
|
|
153
|
+
- `reportTabs`, `captureScreenshots`, and browser-state history queries for summaries and context
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
When `--out` is omitted, screenshots are written to `./.tabctl/screenshots/<timestamp>` and the JSON response includes `writtenTo`.
|
|
155
|
+
See [CLI.md](CLI.md) for the full command reference, options, and examples.
|
|
159
156
|
|
|
160
|
-
##
|
|
161
|
-
Use screenshots only when you need visual context, then extract selectors with `inspect`.
|
|
157
|
+
## GraphQL examples
|
|
162
158
|
|
|
163
|
-
1) Capture context (full page tiles):
|
|
164
|
-
<!-- test: "screenshot passes capture options" -->
|
|
165
159
|
```bash
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
# Query tabs and groups
|
|
161
|
+
tabctl query '{ windows { windowId groups { groupId title } tabs { tabId title url groupTitle } } }'
|
|
168
162
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
tabctl inspect --tab <id> --signal selector --selector '{"name":"target","selector":".your-selector"}'
|
|
173
|
-
```
|
|
163
|
+
# Analyze stale and duplicate tabs
|
|
164
|
+
tabctl query '{ analyze(windowId: 123, staleDays: 30) { totalTabs duplicateTabs staleTabs } }'
|
|
174
165
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
tabctl inspect --tab <id> --signal selector --selector '{"name":"link","selector":"a[href]","attr":"href-url"}'
|
|
179
|
-
tabctl inspect --tab <id> --signal selector --selector "link=a[href]" --selector-attr href-url
|
|
180
|
-
```
|
|
166
|
+
# Inspect page metadata
|
|
167
|
+
tabctl query 'query { inspectTabs(tabIds: [456], signals: ["page-meta"]) { entries { tabId signals { name valueJson } } } }'
|
|
181
168
|
|
|
182
|
-
|
|
169
|
+
# Inspect selectors with typed attrs and filters
|
|
170
|
+
tabctl query 'query { inspectTabs(windowId: 123, selectors: [{ name: "prices", selector: ".price", attr: "text", all: true }, { name: "buy_now_visible", selector: "button.buy-now", attr: "visible" }, { name: "buy_now_style", selector: "button.buy-now", attr: "styles", styleProps: ["color", "background-color"] }, { name: "review_count", selector: ".review", attr: "count" }, { name: "email_value", selector: "input[type=email]", attr: "value" }]) { entries { tabId url signals { name valueJson } } } }'
|
|
183
171
|
|
|
184
|
-
|
|
172
|
+
# Read tab content as Markdown (Kreuzberg preprocessing by default)
|
|
173
|
+
tabctl query 'query { readTabs(windowId: 123, extract: true, maxChars: 30000) { entries { tabId title url markdown chars truncated extracted cached status emptyReason diagnostics { source sourceHtmlChars sourceTextChars documentReadyState truncatedHtml cachedAt cacheAgeMs } error } } }'
|
|
185
174
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
175
|
+
# Generate reports
|
|
176
|
+
tabctl query '{ reportTabs(windowId: 123) { entries { tabId title url description } } }'
|
|
177
|
+
|
|
178
|
+
# Capture screenshots
|
|
179
|
+
tabctl query 'query { captureScreenshots(tabIds: [456], mode: "viewport") { entries { tabId tiles { index width height } } } }'
|
|
180
|
+
|
|
181
|
+
# Inspect persisted browser-state history for future restore tooling
|
|
182
|
+
tabctl query 'query { latestBrowserState { snapshotId reason groups { logicalGroupId title browserGroupId tabUrls } } }'
|
|
183
|
+
tabctl query 'query { browserStateHistory(limit: 10) { snapshotId recordedAt reason eventCount eventKinds } }'
|
|
184
|
+
tabctl query 'query { browserStateGroupHistory(title: "Research", limit: 10) { snapshotId logicalGroupId title browserGroupId tabUrls } }'
|
|
185
|
+
|
|
186
|
+
# Open tabs in a new grouped window
|
|
187
|
+
tabctl query 'mutation { openTabs(urls: ["https://example.com"], group: "Research", newWindow: true) { windowId groupId tabs { tabId url } } }'
|
|
188
|
+
|
|
189
|
+
# Close tabs with undo support
|
|
190
|
+
tabctl query 'mutation { closeTabs(tabIds: [456], confirm: true) { txid closedTabs } }'
|
|
191
|
+
tabctl query 'mutation { undoAction(latest: true) { txid summary } }'
|
|
189
192
|
```
|
|
190
193
|
|
|
191
|
-
|
|
194
|
+
## Agent skills
|
|
195
|
+
|
|
196
|
+
Install the tabctl skill for agents (OpenCode, Claude Code, Codex, etc.) via the Skills CLI:
|
|
192
197
|
|
|
193
|
-
<!-- test: "skill install supports global scope" -->
|
|
194
198
|
```bash
|
|
195
|
-
tabctl skill
|
|
199
|
+
npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode
|
|
196
200
|
```
|
|
197
201
|
|
|
198
|
-
|
|
202
|
+
Install globally:
|
|
199
203
|
|
|
200
204
|
```bash
|
|
201
|
-
npx skills add https://github.com/ekroon/tabctl --skill tabctl -a opencode
|
|
205
|
+
npx skills add https://github.com/ekroon/tabctl --skill tabctl --global -a opencode
|
|
202
206
|
```
|
|
203
207
|
|
|
204
208
|
## Policy (protect tabs)
|
|
@@ -239,34 +243,38 @@ See [CLI.md](CLI.md#configuration) for full details.
|
|
|
239
243
|
## Runtime state
|
|
240
244
|
- Socket: `<dataDir>/tabctl.sock` (default: `~/.local/state/tabctl/tabctl.sock`)
|
|
241
245
|
- Undo log: `<dataDir>/undo.jsonl` (default: `~/.local/state/tabctl/undo.jsonl`)
|
|
246
|
+
- Browser-state history DB: `<dataDir>/state.db` (default: `~/.local/state/tabctl/state.db`)
|
|
242
247
|
- Profile registry: `<configDir>/profiles.json`
|
|
243
|
-
-
|
|
248
|
+
- Windows pipe endpoint file: `<dataDir>/pipe-endpoint`
|
|
244
249
|
|
|
245
250
|
## Windows + WSL transport
|
|
246
251
|
|
|
247
|
-
On Windows, the host exposes a
|
|
252
|
+
On Windows, the host exposes a named-pipe endpoint model:
|
|
248
253
|
- Windows native clients use a named pipe endpoint (`\\.\pipe\tabctl-<hash>`).
|
|
249
|
-
- WSL/Linux clients use
|
|
254
|
+
- WSL/Linux clients use a Windows named-pipe bridge; the Windows host publishes `<dataDir>/pipe-endpoint`, and the WSL CLI relays through `powershell.exe`.
|
|
250
255
|
|
|
251
256
|
WSL endpoint discovery (CLI):
|
|
252
|
-
1. `TABCTL_SOCKET` (explicit endpoint)
|
|
253
|
-
2. `
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
1. `TABCTL_SOCKET` (explicit endpoint).
|
|
258
|
+
2. `pipe-endpoint` file discovery from resolved data dir (and equivalent `/mnt/c/Users/*/.../tabctl/.../pipe-endpoint` locations).
|
|
259
|
+
|
|
260
|
+
WSL named-pipe mode:
|
|
261
|
+
- This is the default WSL transport now.
|
|
262
|
+
- The CLI discovers the pipe endpoint from `<dataDir>/pipe-endpoint` (including the mirrored `/mnt/c/Users/*/...` candidate paths used for other WSL bridge files).
|
|
263
|
+
- TCP is disabled for the WSL transport path.
|
|
256
264
|
|
|
257
|
-
Relevant knobs: `TABCTL_SOCKET`, `
|
|
265
|
+
Relevant knobs: `TABCTL_SOCKET`, `TABCTL_PROFILE`, `TABCTL_DATA_DIR`, `TABCTL_STATE_DIR`, `TABCTL_CONFIG_DIR`.
|
|
258
266
|
|
|
259
267
|
## Troubleshooting (setup/ping on Windows + WSL)
|
|
260
268
|
|
|
261
269
|
- `tabctl setup` fails with `Windows setup verification failed`: check `data.verification.reason` in JSON output (`ping-timeout`, `socket-not-found`, `socket-refused`, `ping-not-ok`, `extension-id-mismatch`), then follow printed manual steps.
|
|
262
270
|
- Runtime ID mismatch (`extension-id-mismatch`): compare expected vs runtime IDs from setup output, then rerun setup with the runtime ID shown by `edge://extensions` / `chrome://extensions`:
|
|
263
271
|
- `tabctl setup --browser <edge|chrome> --extension-id <runtime-id>`
|
|
264
|
-
- Runtime command runs can auto-sync extension files when host/extension versions drift; rerun `tabctl
|
|
272
|
+
- Runtime command runs can auto-sync extension files when host/extension versions drift; rerun `tabctl query 'mutation { reloadExtension { reloading } }'` if the browser does not pick up changes immediately.
|
|
265
273
|
- For local release-like testing while developing, force runtime sync behavior with `TABCTL_AUTO_SYNC_MODE=release-like`.
|
|
266
274
|
- Disable runtime sync entirely with `TABCTL_AUTO_SYNC_MODE=off`.
|
|
267
|
-
- `tabctl ping --json` is the
|
|
268
|
-
- Version metadata is intentionally health-only: regular
|
|
269
|
-
- `tabctl ping` returns connect errors (`ENOENT`, `ECONNREFUSED`, timeout): ensure extension is loaded and active, rerun `tabctl setup`, and in WSL verify
|
|
275
|
+
- `tabctl ping --json` is a host connectivity/health check; use it to confirm the native host is reachable and healthy.
|
|
276
|
+
- Version metadata is intentionally health-only: regular GraphQL payloads do not include version fields unless you explicitly query health surfaces, which may expose fields such as `versionsInSync`, `hostBaseVersion`, and `baseVersion`.
|
|
277
|
+
- `tabctl ping` returns connect errors (`ENOENT`, `ECONNREFUSED`, timeout): ensure extension is loaded and active, rerun `tabctl setup`, and in WSL verify the profile data dir contains a current `pipe-endpoint` file.
|
|
270
278
|
- `tabctl doctor --fix --json` includes per-profile connectivity diagnostics in `data.profiles[].connectivity`; if ping remains unhealthy after local repairs, follow `manualSteps`.
|
|
271
279
|
|
|
272
280
|
Local release-like sync test recipe:
|
|
@@ -275,9 +283,9 @@ Local release-like sync test recipe:
|
|
|
275
283
|
tabctl setup --browser edge --extension-id <extension-id> --release-tag v0.5.2
|
|
276
284
|
|
|
277
285
|
# 2) Run the current binary with forced release-like auto-sync
|
|
278
|
-
TABCTL_AUTO_SYNC_MODE=release-like cargo run --manifest-path rust/Cargo.toml -p tabctl --
|
|
286
|
+
TABCTL_AUTO_SYNC_MODE=release-like cargo run --manifest-path rust/Cargo.toml -p tabctl -- query '{ tabs { total } }'
|
|
279
287
|
|
|
280
|
-
# 3) Verify host/
|
|
288
|
+
# 3) Verify host connectivity/health after auto-sync
|
|
281
289
|
tabctl ping --json
|
|
282
290
|
```
|
|
283
291
|
|
|
@@ -302,7 +310,7 @@ tabctl profile-list
|
|
|
302
310
|
tabctl profile-switch edge
|
|
303
311
|
|
|
304
312
|
# One-off command with different profile
|
|
305
|
-
tabctl
|
|
313
|
+
tabctl --profile chrome-work query '{ tabs { total } }'
|
|
306
314
|
```
|
|
307
315
|
|
|
308
316
|
### Custom Chrome Profile Directories
|
|
@@ -329,8 +337,7 @@ Policy is shared across all profiles.
|
|
|
329
337
|
## Security
|
|
330
338
|
- The native host is locked to your extension ID.
|
|
331
339
|
- All data stays local; no external API keys are used.
|
|
332
|
-
-
|
|
333
|
-
- TCP transport is available on all platforms via `TABCTL_HOST_TCP=1` (host) and `TABCTL_TRANSPORT=tcp` (CLI). All TCP connections are authenticated. See [CLI.md](CLI.md) for details.
|
|
340
|
+
- WSL ↔ Windows communication uses a local named-pipe bridge via `powershell.exe`; no TCP fallback is used on that path.
|
|
334
341
|
|
|
335
342
|
## Development
|
|
336
343
|
|
|
@@ -348,8 +355,20 @@ npm test # unit tests
|
|
|
348
355
|
Rust-only validation:
|
|
349
356
|
```bash
|
|
350
357
|
npm run rust:verify
|
|
358
|
+
npm run check:targets # local cross-target cfg/type check
|
|
351
359
|
```
|
|
352
360
|
|
|
361
|
+
On macOS, `npm run check:targets` can use Zig for the C cross-compiler needed
|
|
362
|
+
by `libsqlite3-sys`:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
brew install zig
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The script auto-detects Zig outside CI and wires the Linux/Windows C compiler
|
|
369
|
+
environment for the check. The pre-push hook does not run this optional check;
|
|
370
|
+
run it manually when you want local cross-target coverage.
|
|
371
|
+
|
|
353
372
|
Browser-backed integration harness (requires built dist artifacts and Chrome):
|
|
354
373
|
```bash
|
|
355
374
|
npm run test:integration
|
|
@@ -375,8 +394,14 @@ Pre-release staging flow:
|
|
|
375
394
|
- `bump:rc` promotes alpha to `x.y.z-rc.1` (or increments RC)
|
|
376
395
|
- `bump:stable` drops the prerelease suffix for final stable publish
|
|
377
396
|
|
|
378
|
-
Release
|
|
397
|
+
Release automation:
|
|
398
|
+
- Run the **Prepare Release** workflow to choose or auto-detect the next version and open a release PR.
|
|
399
|
+
- When that PR merges, **Tag Release** creates `v<version>` and dispatches **Release**.
|
|
400
|
+
- The root `package.json` version and `optionalDependencies.tabctl-win32-x64` are kept in sync by `scripts/bump-version.js`.
|
|
401
|
+
|
|
402
|
+
Release publishing (`.github/workflows/release.yml`) supports both tag pushes and explicit workflow dispatch, and enforces:
|
|
379
403
|
- Git tag must match `package.json` version (`v<version>`)
|
|
404
|
+
- `package.json.optionalDependencies["tabctl-win32-x64"]` must match `package.json` version
|
|
380
405
|
- prerelease tags publish to `alpha`/`rc`; stable publishes to `latest`
|
|
381
406
|
- `npm run build` and `npm test` must pass before publish
|
|
382
407
|
- release assets include `tabctl-extension.zip` plus `tabctl-extension.zip.sha256`
|
|
@@ -399,15 +424,11 @@ TABCTL_VERSION_MODE=release npm run build
|
|
|
399
424
|
```
|
|
400
425
|
|
|
401
426
|
Notes:
|
|
402
|
-
-
|
|
403
|
-
- `close` without `--apply` requires `--confirm` to prevent accidental closure.
|
|
427
|
+
- Browser reads and mutations now go through GraphQL via `tabctl query`.
|
|
404
428
|
- Reports include short descriptions from page metadata and a fallback snippet.
|
|
405
|
-
- `
|
|
406
|
-
-
|
|
407
|
-
-
|
|
408
|
-
-
|
|
409
|
-
-
|
|
410
|
-
- `screenshot --out` writes per-tab folders into the target directory.
|
|
411
|
-
- `tabctl undo` accepts a positional txid, `--txid`, or `--latest`.
|
|
429
|
+
- `inspectTabs` supports `page-meta` plus selector reads with `all`, `text`, `textMode`, `styleProps`, and attrs such as `html`, `value`, `count`, `box`, `styles`, `visible`, `enabled`, and `checked`.
|
|
430
|
+
- `readTabs` converts main-frame page HTML to Markdown with Kreuzberg `html-to-markdown`; per-tab `status`, `emptyReason`, `diagnostics`, and `error` distinguish empty pages, unsupported URLs, injection failures, conversion failures, timeouts, and cached fallbacks (`status: CACHED`, `cached: true`, `diagnostics.source: "cache"`).
|
|
431
|
+
- Cached fallbacks use a bounded profile-local open-tab HTML cache refreshed after successful reads, active tab switches, and quiescent active pages; diagnostics include cache age and match mode when cached content is used.
|
|
432
|
+
- `captureScreenshots` returns tile metadata and image data from GraphQL.
|
|
433
|
+
- `undoAction` accepts either an explicit `txid` or `latest: true`.
|
|
412
434
|
- `tabctl history --json` returns a top-level JSON array.
|
|
413
|
-
- `--format` is only supported by `report` (use `--json` elsewhere).
|