dsh-tabbit 0.2.3 → 0.3.0
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/CHANGELOG.md +100 -0
- package/LICENSE +21 -0
- package/README.en.md +116 -0
- package/README.md +55 -81
- package/client/client.js +389 -0
- package/cordis.patch.yml +76 -5
- package/lib/core/index.js +742 -0
- package/lib/installer/detect.js +374 -0
- package/lib/installer/download.js +247 -0
- package/lib/installer/index.js +254 -0
- package/lib/mentions/index.js +595 -0
- package/lib/permissions/index.js +136 -0
- package/lib/runtime/cli.js +229 -0
- package/lib/runtime/client.js +431 -0
- package/lib/runtime/codec.js +126 -0
- package/lib/runtime/endpoint.js +248 -0
- package/lib/runtime/errors.js +126 -0
- package/lib/runtime/instances.js +287 -0
- package/lib/runtime/net.js +143 -0
- package/lib/runtime/peer.js +132 -0
- package/lib/tool-browser/index.js +425 -0
- package/lib/update-check.js +343 -0
- package/lib/web-fetch/index.js +219 -0
- package/package.json +53 -16
- package/skills/tabbit/SKILL.md +66 -0
- package/skills/tabbit/references/interaction-helpers.md +150 -0
- package/skills/tabbit/references/platform-invocation.md +174 -0
- package/skills/{tabbit-browser → tabbit}/references/playwright-recipes.md +11 -3
- package/skills/tabbit/references/runtime-recovery.md +104 -0
- package/README.zh-CN.md +0 -114
- package/index.js +0 -352
- package/installer.js +0 -568
- package/skills/tabbit-browser/SKILL.md +0 -274
- package/skills/tabbit-browser/agents/openai.yaml +0 -4
- package/skills/tabbit-browser/references/interaction-helpers.md +0 -103
- package/skills/tabbit-browser/references/platform-invocation.md +0 -45
- package/skills/tabbit-browser/references/runtime-recovery.md +0 -95
- package/update-check.js +0 -177
- /package/skills/{tabbit-browser → tabbit}/references/information-extraction.md +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tabbit
|
|
3
|
+
description: Control Tabbit Browser in a task-isolated Playwright workspace; never switch browser backends.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tabbit
|
|
7
|
+
|
|
8
|
+
## Choose invocation
|
|
9
|
+
|
|
10
|
+
- Set `<launcher>` to macOS/POSIX `"$HOME/.local/bin/tabbit-cli"` or Windows
|
|
11
|
+
PowerShell `& "$env:LOCALAPPDATA\Tabbit\LocalAgent\bin\tabbit-cli.exe"`. Do
|
|
12
|
+
not assume `tabbit-cli` is on `PATH`.
|
|
13
|
+
- If the command tool returns a reusable process handle and supports later
|
|
14
|
+
stdin writes through pipe-backed stdin (non-PTY on Windows), run `<launcher>
|
|
15
|
+
persistent` once. This is preferred.
|
|
16
|
+
- Otherwise use `<launcher> nodejs --task NAME` immediately. Reuse the exact
|
|
17
|
+
name for every command. Do not try persistent first. Read [platform
|
|
18
|
+
invocation](references/platform-invocation.md) for CMD and code input.
|
|
19
|
+
|
|
20
|
+
## Persistent workspace
|
|
21
|
+
|
|
22
|
+
Send newline frame; wait for its response. Never detach or background the
|
|
23
|
+
process.
|
|
24
|
+
Commands renew a five-minute lease; expiry keeps tabs/group and stops animation.
|
|
25
|
+
|
|
26
|
+
Send `bootstrap` first with a concise display `taskName`. In `code`, navigate,
|
|
27
|
+
act, verify with Playwright `expect`, then return proof:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{"op":"bootstrap","taskName":"Save-profile","requestId":"save-01","code":"await page.goto('https://example.test/profile',{waitUntil:'domcontentloaded'}); await page.getByRole('button',{name:'Save'}).click(); await expect(page.getByText('Saved')).toBeVisible(); return {verified: true};"}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If bootstrap returns `queued`, wait with
|
|
34
|
+
`{"op":"inspect","requestId":"save-01","waitMs":60000}`; never resubmit.
|
|
35
|
+
|
|
36
|
+
Split only when the returned state changes the next decision. Later `run`
|
|
37
|
+
frames name a path-safe `requestId`:
|
|
38
|
+
`{"op":"run","requestId":"save-02","code":"return await page.title();"}`
|
|
39
|
+
Send `{"op":"tabs"}` before bootstrap to take over an existing tab; include its
|
|
40
|
+
exact `tabId` in `claimTabIds`.
|
|
41
|
+
Inventory creates no task, Page, tab, or group. Later frames omit task identity.
|
|
42
|
+
|
|
43
|
+
The workspace owns only `context.pages()`/`pages()` and places every owned tab
|
|
44
|
+
in its one group. Prefer semantic locators, bounded loops,
|
|
45
|
+
same-program verification. Use `status` and `result.value`.
|
|
46
|
+
|
|
47
|
+
Before the final response, finish exactly once: send `{"op":"finish"}` in
|
|
48
|
+
persistent mode or run `<launcher> finish --task NAME` otherwise.
|
|
49
|
+
Plain finish retains tabs and the resumable group. Use
|
|
50
|
+
`{"op":"finish","keep":false}` only for explicit discard or necessary cleanup.
|
|
51
|
+
It closes only session-created tabs, never claimed or resumed tabs.
|
|
52
|
+
|
|
53
|
+
## Load details only when needed
|
|
54
|
+
|
|
55
|
+
Do not preload these references:
|
|
56
|
+
|
|
57
|
+
- Read [platform invocation](references/platform-invocation.md) for
|
|
58
|
+
product/instance routing or launcher failures.
|
|
59
|
+
- Read [interaction helpers](references/interaction-helpers.md) for
|
|
60
|
+
claim/resume, evidence, focus, paste, or blockers.
|
|
61
|
+
- Read [Playwright recipes](references/playwright-recipes.md) for popups,
|
|
62
|
+
frames, uploads, downloads, canvas, or repetition.
|
|
63
|
+
- Read [information extraction](references/information-extraction.md) for results.
|
|
64
|
+
- Read [runtime recovery](references/runtime-recovery.md) after a failed,
|
|
65
|
+
interrupted receipt or transport/finish failure.
|
|
66
|
+
Never blindly retry a possible mutation.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Interaction helpers
|
|
2
|
+
|
|
3
|
+
These additive helpers live in the persistent Node evaluation realm as the
|
|
4
|
+
frozen `tabbit` global. They do not replace native Playwright APIs.
|
|
5
|
+
|
|
6
|
+
Evaluation code supports top-level `await` and `return` and provides persistent
|
|
7
|
+
`browser`, `context`, `page`, `pages()`, `usePage()`, Node `assert`, official
|
|
8
|
+
Playwright Test `expect`, `artifactPath()`, `reportIssue()`, and `tabbit`.
|
|
9
|
+
Browser DOM globals exist only inside `page.evaluate()`; return bounded
|
|
10
|
+
JSON-safe values, never Page, Locator, Frame, JSHandle, or DOM objects.
|
|
11
|
+
|
|
12
|
+
## Inventory, claim, and resume
|
|
13
|
+
|
|
14
|
+
`{"op":"tabs"}` before bootstrap is metadata-only discovery across regular
|
|
15
|
+
tabs in the bound Profile. It creates no task, Page, tab, or group. Each bounded
|
|
16
|
+
descriptor includes `tabId`, `windowId`, tab-strip
|
|
17
|
+
`index`, `title`, `url`, `active`, relative `state`, and optional live group
|
|
18
|
+
metadata. `available` means unowned, `owned` means owned by this workspace, and
|
|
19
|
+
`busy` means another workspace owns it. Discovery never attaches, claims,
|
|
20
|
+
groups, focuses, navigates, or creates a Playwright Page.
|
|
21
|
+
|
|
22
|
+
Only owned tabs appear in `context.pages()` and `pages()`. Bootstrap an existing
|
|
23
|
+
available tab with its exact returned ID in `claimTabIds`. After bootstrap,
|
|
24
|
+
claim another available tab with `{"op":"claim","tabIds":[17]}` or resume
|
|
25
|
+
every claimable tab in one exact group with `{"op":"resume","groupId":"A1B2"}`.
|
|
26
|
+
Inventory is advisory:
|
|
27
|
+
claim/resume revalidates and can fail if another session won. Batches are
|
|
28
|
+
atomic; duplicate, stale, busy, unsupported, or cross-window inputs do not
|
|
29
|
+
partially claim. Claims never move tabs between windows. Group titles are
|
|
30
|
+
presentation, never identity or ownership.
|
|
31
|
+
|
|
32
|
+
When the next program is known, combine ownership and evaluation:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{"op":"run","tabIds":[17,18],"requestId":"compare-01","mutation":"possible","code":"await expect(page.getByRole('main')).toBeVisible(); return {controlled:pages().length,url:page.url()};"}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use `groupId` instead of `tabIds` to resume and run. A workspace has no group
|
|
39
|
+
while empty and one group once it owns a tab. Dragging an owned tab out is user
|
|
40
|
+
takeover and removes that Page; dragging an unowned tab in does not claim it.
|
|
41
|
+
|
|
42
|
+
## Screenshots
|
|
43
|
+
|
|
44
|
+
Use screenshots for canvas or DOM/visual disagreement, not routine discovery.
|
|
45
|
+
Before substantial input on a canvas or rich editor, make a small write probe
|
|
46
|
+
and verify the rendered result. `page.screenshot()` produces a receipt entry
|
|
47
|
+
rather than visual input. When
|
|
48
|
+
`outcome.screenshotsDelta` gives `nextAction.type: "load_image"`, immediately
|
|
49
|
+
load its path with `view_image(path)`. Without an image reader, make no visual
|
|
50
|
+
claim and capture no further screenshot. Capture again only after a meaningful
|
|
51
|
+
visual-state change. For broad accessible structure, prefer a bounded
|
|
52
|
+
`page.ariaSnapshot({mode: "ai", depth: 20, boxes: true})`, then act with a fresh
|
|
53
|
+
`page.locator("aria-ref=e2")`.
|
|
54
|
+
|
|
55
|
+
## `tabbit.observe(options)`
|
|
56
|
+
|
|
57
|
+
Returns bounded task state: page metadata, selected frame metadata, optional
|
|
58
|
+
deep-focus details, and truncated native ARIA snapshots for the page and each
|
|
59
|
+
selected frame. Frame entries include `hostBox`, `viewportIntersection`,
|
|
60
|
+
`visible`, `actionable`, and `occludedBy`.
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
return await tabbit.observe({frames: "visible", focus: true, depth: 16,
|
|
64
|
+
maxChars: 6000, frameMaxChars: 2000, maxFrames: 8});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`frames` is `"none"`, `"visible"`, or `"all"`; `depth` is 1–30, `maxChars` is
|
|
68
|
+
256–20000, `frameMaxChars` is 256–6000, and `maxFrames` is 1–32. This is
|
|
69
|
+
observation only. Do not treat a frame snapshot as permission to act when its
|
|
70
|
+
host is non-actionable.
|
|
71
|
+
|
|
72
|
+
## `tabbit.focusInfo()` and `tabbit.hitTest(targetOrPoint)`
|
|
73
|
+
|
|
74
|
+
`tabbit.focusInfo()` follows focus through child frames and open shadow roots,
|
|
75
|
+
returning role/name/type/editability, visibility, rectangle, and selection.
|
|
76
|
+
Call it before keyboard input.
|
|
77
|
+
|
|
78
|
+
`tabbit.hitTest(locator)` reports the element at the locator's center across
|
|
79
|
+
frames. `tabbit.hitTest({x, y})` checks a main-frame viewport point. Call it
|
|
80
|
+
before coordinate input and treat mismatches as a reason to re-observe.
|
|
81
|
+
|
|
82
|
+
## `tabbit.actionability(locator)`
|
|
83
|
+
|
|
84
|
+
`tabbit.actionability()` verifies the target inside its owner frame and every
|
|
85
|
+
iframe host up to the main page. It reports target visibility, event reception,
|
|
86
|
+
viewport intersection, frame identity, and blockers such as an `occludedBy`
|
|
87
|
+
element. Click with native `locator.click()` so Playwright performs its full
|
|
88
|
+
actionability and retry behavior.
|
|
89
|
+
If a click fails, preserve the Playwright error and use this helper for explicit
|
|
90
|
+
diagnostics; do not make the helper a precondition or force the click.
|
|
91
|
+
|
|
92
|
+
## `tabbit.pasteText(text, options)`
|
|
93
|
+
|
|
94
|
+
Dispatches a task-local synthetic paste, then uses an editable fallback when
|
|
95
|
+
needed. It never reads or overwrites the user's OS clipboard and reports
|
|
96
|
+
`trusted: false`. Options are `format: "text" | "tsv"` and
|
|
97
|
+
`requireEditableFocus: true | false`. The receipt reports byte/character counts,
|
|
98
|
+
strategy, and focus before/after, but never echoes the payload.
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
const input = page.getByRole("textbox", {name: "Data"});
|
|
102
|
+
await input.click();
|
|
103
|
+
const paste = await tabbit.pasteText("张三\t25\t技术", {
|
|
104
|
+
format: "tsv", requireEditableFocus: true,
|
|
105
|
+
});
|
|
106
|
+
await expect(input).not.toHaveValue("");
|
|
107
|
+
return {paste, value: await input.inputValue()};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Always verify application-visible state; synthetic events are not trusted user
|
|
111
|
+
events and a site may reject them.
|
|
112
|
+
|
|
113
|
+
## `tabbit.triggerAndWait(event, trigger, options)`
|
|
114
|
+
|
|
115
|
+
Arms the waiter before running `trigger`. Supported events are `popup`, `page`,
|
|
116
|
+
`download`, `dialog`, `navigation`, and `url` (`options.url` required for `url`).
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
const popup = await tabbit.triggerAndWait(
|
|
120
|
+
"popup", () => page.getByRole("link", {name: "Open"}).click(),
|
|
121
|
+
{timeoutMs: 10000},
|
|
122
|
+
);
|
|
123
|
+
return {popupUrl: popup.url()};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## `tabbit.triggerAndObserve(trigger, options)`
|
|
127
|
+
|
|
128
|
+
Use this for ambiguous transitions. It arms page, URL, navigation, frame, and
|
|
129
|
+
DOM-revision observation before `trigger`, then returns the highest-priority
|
|
130
|
+
observed result after a short settle window. Options are `timeoutMs`, `settleMs`,
|
|
131
|
+
`pollMs`, and `activatePage`. With `activatePage: true`, a newly opened page
|
|
132
|
+
becomes the task's active `page`.
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
const result = await tabbit.triggerAndObserve(
|
|
136
|
+
() => target.click(),
|
|
137
|
+
{timeoutMs: 3000, activatePage: true},
|
|
138
|
+
);
|
|
139
|
+
return {kind: result.kind, url: page.url()};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Input and blocker conventions
|
|
143
|
+
|
|
144
|
+
For reactive inputs, select a visible suggestion or use the supported commit
|
|
145
|
+
key, blur through a neutral control, and verify persistence. Close any visible
|
|
146
|
+
calendar or suggestion overlay before submitting. Follow rendered first-party
|
|
147
|
+
links for opaque routes instead of guessing.
|
|
148
|
+
|
|
149
|
+
Report verified blockers with `reportIssue(code)`: `AUTHENTICATION_REQUIRED`,
|
|
150
|
+
`SITE_ACCESS_BLOCKED`, or `BROWSER_ERROR_PAGE`.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Platform invocation
|
|
2
|
+
|
|
3
|
+
Read this reference only when the user names an instance or product, the host
|
|
4
|
+
cannot keep process stdin writable, or launcher, permission, installation, or
|
|
5
|
+
routing fails. Use only the installed stable launcher. Never invoke a CLI from
|
|
6
|
+
an app bundle, use a versioned native CLI, read `endpoint.json`, or switch
|
|
7
|
+
backends.
|
|
8
|
+
|
|
9
|
+
## Persistent invocation
|
|
10
|
+
|
|
11
|
+
Start the launcher once per Agent conversation and keep its stdin/stdout open.
|
|
12
|
+
It selects and authenticates one Browser instance once, then accepts bounded
|
|
13
|
+
newline-delimited JSON: optionally one unbound `tabs` inventory, then
|
|
14
|
+
`bootstrap`; zero or more `run`, `inspect`, `tabs`, `claim`, `resume`,
|
|
15
|
+
`receipt`, `checkpoint`, `diagnose`, `docs`, or `resource` frames; and `finish`
|
|
16
|
+
last. When claiming existing Browser state, pass the selected returned ID
|
|
17
|
+
through bootstrap `claimTabIds`. Wait for each newline JSON response before
|
|
18
|
+
sending the next frame. Later frames must not contain `task`, `taskId`, `name`,
|
|
19
|
+
`taskName`, or `generation`.
|
|
20
|
+
|
|
21
|
+
Every submitted frame produces exactly one newline JSON receipt on stdout.
|
|
22
|
+
Local validation failures and Runtime transport interruption are receipts too;
|
|
23
|
+
stderr is reserved for process-level startup, shutdown, and usage failures.
|
|
24
|
+
|
|
25
|
+
The macOS/POSIX stable launcher is:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
"$HOME/.local/bin/tabbit-cli" persistent
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The Windows PowerShell form is:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
& "$env:LOCALAPPDATA\Tabbit\LocalAgent\bin\tabbit-cli.exe" persistent
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The Windows CMD form is:
|
|
38
|
+
|
|
39
|
+
```bat
|
|
40
|
+
"%LOCALAPPDATA%\Tabbit\LocalAgent\bin\tabbit-cli.exe" persistent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use pipe-backed stdin for persistent mode on Windows. Do not allocate a console
|
|
44
|
+
PTY: terminal echo, line editing, or wrapping can alter an NDJSON frame. If the
|
|
45
|
+
host offers only a PTY, use the compatibility commands immediately. This rule
|
|
46
|
+
does not change the macOS/POSIX launcher or its stdin behavior.
|
|
47
|
+
|
|
48
|
+
Keep the launcher as the first command token. When the Agent command sandbox
|
|
49
|
+
blocks it, request reusable approval only for that stable path; do not wrap it
|
|
50
|
+
with `env`, a shell, or another executable. The client retains one Browser
|
|
51
|
+
connection for the workspace. If that connection closes, the workspace ends.
|
|
52
|
+
|
|
53
|
+
Do not start persistent mode with a detached or background command that closes
|
|
54
|
+
stdin. `Persistent input ended before bootstrap` means the host closed the
|
|
55
|
+
protocol stream; it is not Browser or instance unavailability. When the host
|
|
56
|
+
has no interactive process primitive, use the compatibility commands instead
|
|
57
|
+
of building a FIFO or wrapper. A frame the client rejects reports
|
|
58
|
+
`INVALID_PERSISTENT_INPUT` on stdout and leaves the connection open; correct
|
|
59
|
+
that frame and continue on the same process. Choose one concise task name and
|
|
60
|
+
reuse it exactly:
|
|
61
|
+
|
|
62
|
+
- `nodejs --task NAME`, `inspect --task NAME`, `tabs --task NAME`
|
|
63
|
+
- `claim --task NAME --tab ID...`, `resume --task NAME --group ID`
|
|
64
|
+
- `receipt --task NAME --request ID`, `checkpoint --task NAME`,
|
|
65
|
+
`diagnose --task NAME`
|
|
66
|
+
- `resource --task NAME --resource ID`, `finish --task NAME [--discard]`
|
|
67
|
+
|
|
68
|
+
These commands reuse the same Browser-owned task and receipt lane. Plain
|
|
69
|
+
`finish` retains it; `--discard` closes only tabs created by that session.
|
|
70
|
+
|
|
71
|
+
On Windows, pass multiline `nodejs` code through pipe-backed stdin. If the host
|
|
72
|
+
cannot close that pipe, write the code to a task-scoped temporary `.js` file,
|
|
73
|
+
then invoke the stable launcher from CMD with `<` redirection:
|
|
74
|
+
|
|
75
|
+
```bat
|
|
76
|
+
"%LOCALAPPDATA%\Tabbit\LocalAgent\bin\tabbit-cli.exe" nodejs --task NAME < "%TEMP%\tabbit-task.js"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Remove the temporary file after its receipt. Do not use a PowerShell pipeline,
|
|
80
|
+
here-string, `echo`, or a console PTY to transport code; those paths can alter
|
|
81
|
+
newlines, encoding, or long input.
|
|
82
|
+
|
|
83
|
+
## Product and instance selection
|
|
84
|
+
|
|
85
|
+
With no explicit instance or product request, do not inspect the registry or
|
|
86
|
+
set an override. The stable launcher selects once when persistent mode starts.
|
|
87
|
+
|
|
88
|
+
For an explicit request, inspect only the current user's registry with
|
|
89
|
+
read-only commands. Normalize only case and surrounding space for a product and
|
|
90
|
+
match exactly `Tabbit Browser`, `Tabbit Browser Dev`, `Tabbit`, or `Tabbit Dev`;
|
|
91
|
+
they are not aliases. Discard records that fail the platform checks below. Pick
|
|
92
|
+
the exact requested instance or one exact product match. If multiple valid
|
|
93
|
+
records match a product, show their instance IDs and ask the user. Automatic
|
|
94
|
+
liveness and newest-endpoint selection belong to the stable launcher.
|
|
95
|
+
|
|
96
|
+
Set the selected uppercase 16-hex instance ID before starting the one persistent
|
|
97
|
+
process. Do not invoke an unpinned launcher as a probe, change the ID during the
|
|
98
|
+
conversation, or repeat environment setup for operations.
|
|
99
|
+
|
|
100
|
+
POSIX shell:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
export TABBIT_PLAYWRIGHT_INSTANCE='0123456789ABCDEF'
|
|
104
|
+
"$HOME/.local/bin/tabbit-cli" persistent
|
|
105
|
+
unset TABBIT_PLAYWRIGHT_INSTANCE
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
PowerShell:
|
|
109
|
+
|
|
110
|
+
```powershell
|
|
111
|
+
$env:TABBIT_PLAYWRIGHT_INSTANCE = '0123456789ABCDEF'
|
|
112
|
+
& "$env:LOCALAPPDATA\Tabbit\LocalAgent\bin\tabbit-cli.exe" persistent
|
|
113
|
+
Remove-Item Env:TABBIT_PLAYWRIGHT_INSTANCE
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
CMD:
|
|
117
|
+
|
|
118
|
+
```bat
|
|
119
|
+
set "TABBIT_PLAYWRIGHT_INSTANCE=0123456789ABCDEF"
|
|
120
|
+
"%LOCALAPPDATA%\Tabbit\LocalAgent\bin\tabbit-cli.exe" persistent
|
|
121
|
+
set "TABBIT_PLAYWRIGHT_INSTANCE="
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Registry validation
|
|
125
|
+
|
|
126
|
+
On macOS/POSIX, the registry is exactly:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
$HOME/.local/share/tabbit-playwright/instances
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Inspect candidate `*.instance` files and matching `*.product` sidecars with
|
|
133
|
+
`find`, `test`, `stat`, and bounded text reads. The directory must be owned by
|
|
134
|
+
the current user, mode 0700, and not a symlink. A record must be a
|
|
135
|
+
regular non-symlink owned by the current user with mode 0600, an uppercase
|
|
136
|
+
16-hex filename ID, exact managed marker and line count, absolute managed paths,
|
|
137
|
+
and a canonical product sidecar. Missing `endpoint.json` means offline; never read
|
|
138
|
+
the file. Ignore invalid records.
|
|
139
|
+
|
|
140
|
+
On Windows, the registry is exactly:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
%LOCALAPPDATA%\Tabbit\LocalAgent\instances
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use PowerShell `Get-ChildItem`, `Get-Item`, `Get-Acl`, and `Get-Content -Raw`
|
|
147
|
+
only for read-only inspection. A valid `*.json` record is a regular file under
|
|
148
|
+
that protected root, has no reparse component, has the protected DACL for the
|
|
149
|
+
current user and `SYSTEM`, and has exactly `version`, `instanceId`, `product`,
|
|
150
|
+
`cliPath`, `endpointPath`, `browserPath`, and `userDataDir`. Require version 1,
|
|
151
|
+
a matching uppercase 16-hex filename ID, a canonical product, absolute accepted
|
|
152
|
+
installation paths, and `endpointPath` exactly below `userDataDir\LocalAgent`.
|
|
153
|
+
Ignore invalid records; do not repair them.
|
|
154
|
+
|
|
155
|
+
## Failure boundaries
|
|
156
|
+
|
|
157
|
+
- Agent sandbox denial: request reusable approval for the stable launcher path
|
|
158
|
+
and, only for explicit selection, read-only registry access. Approval does not
|
|
159
|
+
authorize registry changes.
|
|
160
|
+
- OS ownership, mode, ACL, endpoint-access, quarantine, or signing-policy
|
|
161
|
+
denial: verify Agent and Browser share an OS user, then relaunch Browser so it
|
|
162
|
+
can repair its managed integration. Report any remaining denial without
|
|
163
|
+
changing system security.
|
|
164
|
+
- Missing or malformed launcher/registry: invalid installation.
|
|
165
|
+
- exit 69: unavailable or ambiguous routing.
|
|
166
|
+
- `Persistent input ended before bootstrap`: closed host stdin; use an
|
|
167
|
+
interactive process or the compatibility commands above. Do not relaunch the
|
|
168
|
+
Browser.
|
|
169
|
+
- Failure after selection: runtime connection denial.
|
|
170
|
+
- `BROWSER_RUNTIME_UNAVAILABLE`: relaunch Browser once with permission. Never
|
|
171
|
+
start the Browser-owned Runtime Service directly.
|
|
172
|
+
|
|
173
|
+
Never broaden permissions or ACLs, require administrator access, disable
|
|
174
|
+
security controls, or copy/relocate native binaries.
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# Playwright recipes
|
|
2
2
|
|
|
3
|
-
Use these
|
|
4
|
-
Each
|
|
5
|
-
|
|
3
|
+
Use these code bodies as the `code` field of a bound `bootstrap` or `run`
|
|
4
|
+
request. Each is an async function body: use it directly without adding an
|
|
5
|
+
async wrapper. For name-addressed compatibility, send the same body to
|
|
6
|
+
`<launcher> nodejs --task NAME` through a POSIX heredoc on macOS/Linux or the
|
|
7
|
+
Windows code-input procedure in [platform invocation](platform-invocation.md).
|
|
8
|
+
|
|
9
|
+
Each frame gets a fresh async wrapper, so lexical variables do not survive.
|
|
10
|
+
Re-resolve Pages with `context.pages()`/`pages()`, or put intentional
|
|
11
|
+
cross-frame state on `globalThis`.
|
|
6
12
|
|
|
7
13
|
## Contents
|
|
8
14
|
|
|
@@ -37,6 +43,8 @@ return {
|
|
|
37
43
|
```
|
|
38
44
|
|
|
39
45
|
Use `mutation: "possible"` because navigation changes browser state.
|
|
46
|
+
Prefer `domcontentloaded`, or `commit` followed by an explicit readiness check.
|
|
47
|
+
Reserve full `load` for tasks that depend on every page resource.
|
|
40
48
|
|
|
41
49
|
## Inspect visible controls and visual targets
|
|
42
50
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Runtime receipts and recovery
|
|
2
|
+
|
|
3
|
+
Read this reference after a failed, interrupted, or quarantined receipt, a
|
|
4
|
+
possible mutation, a resource result, explicit diagnostics, transport close, or
|
|
5
|
+
finish failure. A `queued` or `running` receipt is not one of those: it is
|
|
6
|
+
ordinary pending work, described under receipt states below. Keep using the
|
|
7
|
+
same open `tabbit-cli persistent` process while its Browser connection remains
|
|
8
|
+
open.
|
|
9
|
+
|
|
10
|
+
## Compact and explicit results
|
|
11
|
+
|
|
12
|
+
Normal success is compact: use `result.value` and `status`.
|
|
13
|
+
Only bootstrap includes `task` identity. Static Browser/Playwright capabilities
|
|
14
|
+
are available with `{"op":"docs","topic":"capabilities"}`; binding,
|
|
15
|
+
controller, and Browser health are available with `{"op":"diagnose"}`. Do not
|
|
16
|
+
request either after ordinary successful actions.
|
|
17
|
+
|
|
18
|
+
Each evaluation has a stable `requestId`. Recovery operations are explicit:
|
|
19
|
+
|
|
20
|
+
- `{"op":"inspect","requestId":"save-01"}` returns the compact state of that
|
|
21
|
+
original request. Its optional `waitMs` waits for that receipt to reach a
|
|
22
|
+
terminal state without resubmitting the request, and can be repeated;
|
|
23
|
+
`{"op":"inspect"}` returns current task status. In compatibility mode the
|
|
24
|
+
same bounded wait is
|
|
25
|
+
`tabbit-cli receipt --task NAME --request save-01 --wait-ms 60000`.
|
|
26
|
+
- `{"op":"receipt","requestId":"save-01"}` returns its complete canonical
|
|
27
|
+
receipt.
|
|
28
|
+
- `{"op":"checkpoint"}` returns task/page recovery state.
|
|
29
|
+
- `{"op":"resource","resourceId":"...","offset":0,"maxBytes":8192}` reads
|
|
30
|
+
a bounded spilled result; continue with the returned `nextOffset` until
|
|
31
|
+
`eof`.
|
|
32
|
+
|
|
33
|
+
Use `receipt`, `checkpoint`, `diagnose`, `docs`, and `resource` only when their
|
|
34
|
+
detail changes the next decision or the user explicitly requests diagnostics.
|
|
35
|
+
Choose request IDs that encode intent and order, such as `submit-order-03`, and
|
|
36
|
+
never reuse one for changed code. A `run` frame carrying `code` must name its
|
|
37
|
+
`requestId`; only `bootstrap` may omit it and take the default `bootstrap`.
|
|
38
|
+
That default is reserved: submitting your own work under it fails with
|
|
39
|
+
`REQUEST_ID_RESERVED` rather than replaying the bootstrap receipt.
|
|
40
|
+
`timeoutMs` bounds the evaluation and must be positive and at most 120000;
|
|
41
|
+
`waitMs` bounds only the client-side wait for its receipt and is capped the
|
|
42
|
+
same way.
|
|
43
|
+
|
|
44
|
+
## Receipt states
|
|
45
|
+
|
|
46
|
+
- `succeeded`: consume inline `result.value`, or read its bounded `resourceId`
|
|
47
|
+
using the operation above.
|
|
48
|
+
- A terminal receipt's `transition` reports URL and Page changes plus the new
|
|
49
|
+
page count, target epoch, and document generation. Check it before recovery.
|
|
50
|
+
- `queued` or `running`: ordinary pending work, not failure.
|
|
51
|
+
Bootstrap code returns this prompt receipt by default after the workspace
|
|
52
|
+
is bound; its evaluation continues on the same receipt lane. Wait with
|
|
53
|
+
`{"op":"inspect","requestId":"save-01","waitMs":60000}`, repeat that wait as
|
|
54
|
+
long as it stays pending, and never submit the request again. Treat it as a
|
|
55
|
+
fault only once it outlives its own `timeoutMs`.
|
|
56
|
+
- `failed`: inspect its bounded error and `mutationState`. Correct and rerun
|
|
57
|
+
only when no uncertain mutation remains. A failed `bootstrap` still created
|
|
58
|
+
the task and bound the connection, and its response carries `bound: true`;
|
|
59
|
+
continue with a corrected `run` frame and never resend `bootstrap`, because
|
|
60
|
+
a second frame naming a task is rejected as an invalid bound request.
|
|
61
|
+
- `interrupted`, quarantine, or `mutationState: "possible"`: the action may
|
|
62
|
+
already have happened. Inspect the same request, then its canonical receipt
|
|
63
|
+
and checkpoint. Check `url`, `pageCount`, `targetEpoch`,
|
|
64
|
+
`documentGeneration`, and `mainFrameAttached`, then perform a new read-only
|
|
65
|
+
observation of application state.
|
|
66
|
+
Continue from evidence and retry only when evidence proves the original
|
|
67
|
+
mutation did not occur.
|
|
68
|
+
- A screenshot result includes a bounded screenshot delta and `nextAction`. If
|
|
69
|
+
`nextAction.type` is `load_image`, immediately load the named immutable PNG
|
|
70
|
+
with the host image reader before making visual claims.
|
|
71
|
+
|
|
72
|
+
A client-side wait expiry is not operation failure. Never evade pending or
|
|
73
|
+
quarantined work by changing request IDs, tasks, instances, or browser backends.
|
|
74
|
+
|
|
75
|
+
## Transport close
|
|
76
|
+
|
|
77
|
+
`TRANSPORT_INTERRUPTED` is the submitted frame's terminal stdout receipt: the
|
|
78
|
+
Runtime connection ended before its operation receipt arrived. It does not
|
|
79
|
+
prove that a possible mutation did not happen.
|
|
80
|
+
|
|
81
|
+
The authenticated connection owns the workspace. If it closes, the Runtime
|
|
82
|
+
finalizes the task and the native client exits. Do not reconnect, replay the
|
|
83
|
+
request, or issue bound receipt/checkpoint operations through a replacement
|
|
84
|
+
connection. A fresh workspace may inspect externally visible application state
|
|
85
|
+
read-only; retry a mutation only if that proves the original did not occur.
|
|
86
|
+
|
|
87
|
+
If a group disappeared while the current empty binding remains healthy, list
|
|
88
|
+
current tab/group descriptors and make a new explicit claim/resume decision.
|
|
89
|
+
If a user drags an owned tab out, accept its release and continue only with the
|
|
90
|
+
remaining owned Pages.
|
|
91
|
+
|
|
92
|
+
## Finish
|
|
93
|
+
|
|
94
|
+
Send `{"op":"finish"}` exactly once after verification or when abandoning the
|
|
95
|
+
workspace. Plain finish releases ownership and retains live tabs and the group;
|
|
96
|
+
its returned `groupId` can later be discovered and resumed. To honor an
|
|
97
|
+
explicit discard request, send `{"op":"finish","keep":false}`. Discard closes
|
|
98
|
+
only tabs created during this session, never claimed tabs or tabs inherited by
|
|
99
|
+
group resume.
|
|
100
|
+
|
|
101
|
+
A repeated finish on the same open connection returns the terminal finish
|
|
102
|
+
result and cannot restore execution. If the connection closes before finish is
|
|
103
|
+
confirmed, the Runtime finalizes the task; do not start another controller or
|
|
104
|
+
fallback browser merely to finish.
|
package/README.zh-CN.md
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# tabbit-browser for DeepSeek Harness
|
|
2
|
-
|
|
3
|
-
[English](README.md) | **简体中文**
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
这是一个为 DeepSeek Harness(DSH)打造的插件。安装后,DSH 中的 Agent 获得控制 Tabbit 浏览器的能力:通过 `tabbit-cli`——Tabbit 浏览器自带的、任务隔离的 Playwright CLI——操作真实网页、复用真实登录态,完成网页自动化、信息提取、QA 与基准测试等任务。
|
|
8
|
-
|
|
9
|
-
## 插件内容
|
|
10
|
-
|
|
11
|
-
| 组件 | 说明 |
|
|
12
|
-
| ---- | ---- |
|
|
13
|
-
| `tabbit-browser` skill | 浏览器自动化工作指南:持久化任务空间、locator 与等待、截图、回执与恢复。随插件安装自动发现和加载,无需单独安装。模型通过 `skill({ name: "tabbit-browser" })` 或 `/tabbit-browser` 加载。 |
|
|
14
|
-
| `tabbit_browser_install` 工具 | 环境预检:检测已安装的正式版 Tabbit、要求版本 ≥ `1.9.0`、检查 `tabbit-cli` 常驻运行时;未安装或版本过低时,创建 DSH 后台任务按地区下载对应安装包。 |
|
|
15
|
-
| `tabbit_plugin_update` 工具 | 插件更新检查:每天至多一次对比本地插件版本与仓库发布的 CHANGELOG,离线失败时静默跳过,并可记录用户已拒绝的版本。存在新版本时,skill 会附带更新提示加载,展示新版本的新增功能。 |
|
|
16
|
-
|
|
17
|
-
## 安装
|
|
18
|
-
|
|
19
|
-
### 1. 检查并安装 DeepSeek Harness
|
|
20
|
-
|
|
21
|
-
先检查本地是否已经安装 DSH:
|
|
22
|
-
|
|
23
|
-
```sh
|
|
24
|
-
dsh --version
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
如果命令能够正常输出版本号,直接进入下一步。如果提示找不到命令,请根据操作系统安装。
|
|
28
|
-
|
|
29
|
-
#### macOS
|
|
30
|
-
|
|
31
|
-
安装 Node.js 20 或更高版本,然后安装 DSH:
|
|
32
|
-
|
|
33
|
-
```sh
|
|
34
|
-
brew install node
|
|
35
|
-
npm install -g @deepseek-ai/dsh
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
#### Windows
|
|
39
|
-
|
|
40
|
-
在 PowerShell 中安装 Node.js LTS:
|
|
41
|
-
|
|
42
|
-
```powershell
|
|
43
|
-
winget install OpenJS.NodeJS.LTS
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
安装完成后重新打开 PowerShell,再安装 DSH:
|
|
47
|
-
|
|
48
|
-
```powershell
|
|
49
|
-
npm install -g @deepseek-ai/dsh
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
安装后再次运行 `dsh --version`,确认 DSH 可以正常使用。
|
|
53
|
-
|
|
54
|
-
### 2. 安装 tabbit-browser 插件
|
|
55
|
-
|
|
56
|
-
```sh
|
|
57
|
-
dsh plugin --profile web add dsh-tabbit
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
这会安装 npm 包(预构建 tarball,走 CDN 分发)。如果无法访问 npm
|
|
61
|
-
registry,可以回退到 GitHub 源:
|
|
62
|
-
|
|
63
|
-
```sh
|
|
64
|
-
dsh plugin --profile web add github:Tabbit-Browser/dsh-tabbit
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 3. 启动 DSH
|
|
68
|
-
|
|
69
|
-
```sh
|
|
70
|
-
dsh web
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## 工作原理
|
|
74
|
-
|
|
75
|
-
安装插件后,bundle 会自动加载 Skill Provider,模型可通过 `skill({ name: "tabbit-browser" })` 或 `/tabbit-browser` 加载说明。在任务中的第一次浏览器操作之前,skill 会先调用 `tabbit_browser_install` 做环境预检:
|
|
76
|
-
|
|
77
|
-
- **`ready`** — 已安装 `1.9.0` 或更高版本的正式版 Tabbit 且运行时正在运行,Agent 继续通过 `tabbit-cli` 操作浏览器。
|
|
78
|
-
- **`restart-required`** — 已安装的版本达标,但 `tabbit-cli` 常驻运行时未运行,提示用户重启一次 Tabbit 浏览器。
|
|
79
|
-
- **`background`** — 未安装任何正式版,或版本低于 `1.9.0`;工具会创建 DSH 后台任务,读取系统地区(macOS 读取系统地区,Windows 调用系统地区 API),中国大陆下载国内正式版安装包,其他地区或无法识别地区时下载国际正式版安装包;自动选择对应的 Windows x64、macOS Apple Silicon 或 macOS Intel 包,保存到用户的 `Downloads` 目录,下载过程会输出进度,完成后 DSH 会通知安装包的绝对路径。
|
|
80
|
-
|
|
81
|
-
环境检查还会:
|
|
82
|
-
|
|
83
|
-
- 多个 Tabbit 实例同时运行时,仍判定 Runtime 可用;模型根据 CLI 提示设置 `TABBIT_PLAYWRIGHT_INSTANCE`,不会把实例选择歧义误报为 Runtime 未运行。
|
|
84
|
-
- 诊断当前平台调用 CLI 所需的 DSH sandbox mode;Windows 返回 `cliSandboxMode: danger-full-access`,其他平台返回 `default`。
|
|
85
|
-
- 按 Agent session 缓存成功的环境检查;仅在 Runtime/launcher 失败或安装变化后通过 `refresh: true` 主动失效并重新检查。
|
|
86
|
-
|
|
87
|
-
## 前提
|
|
88
|
-
|
|
89
|
-
- 需要 `1.9.0` 或更高版本的正式版 Tabbit 浏览器。国际版 `Tabbit` 和国内版 `Tabbit Browser` 均支持,安装任意一个即可;如果未安装或版本过低,插件会自动下载对应安装包。
|
|
90
|
-
- 当前 DSH profile 已提供 `ctx.skills`、`ctx.tools`、`ctx.jobs` 以及对应模型工具。
|
|
91
|
-
- `dsh-tool-jobs` 已为当前 Agent 提供后台任务控制和完成通知。
|
|
92
|
-
- 当前 DSH profile 已提供运行在 Tabbit Browser 所在宿主机的 Bash/Shell 工具。
|
|
93
|
-
- Shell 的执行环境可以访问 Browser-owned Runtime Service。
|
|
94
|
-
- Windows 上 DSH 的 `read-only` 与 `workspace-write` 限制令牌无法写入 Runtime 命名管道。Skill 先正常执行 `tabbit-cli tasks` 连接探测;成功时完全不询问权限。仅当 Browser、launcher 和 Runtime 进程均已检测到但连接返回 `BROWSER_RUNTIME_UNAVAILABLE` 时,才要求用户把当前 DSH 会话切换到 Full Permission,并立即停止当前任务,不重试或继续浏览器操作。
|
|
95
|
-
|
|
96
|
-
## 行为说明与限制
|
|
97
|
-
|
|
98
|
-
- 中国大陆使用 `tabbit.com` 国内版下载源,其他地区使用 `tabbit.ai` 国际版下载源。
|
|
99
|
-
- 后台下载会输出进度,完成后通知安装包的绝对路径,但不会自动打开 `.dmg` 或 `.exe`。
|
|
100
|
-
- 不会检测开发版。
|
|
101
|
-
- 不提供 `tabbit_browser_evaluate` 等原生浏览器工具。
|
|
102
|
-
|
|
103
|
-
如果 DSH 的 Bash 运行在 E2B、远程容器或无法访问本机 GUI Browser 的沙箱中,本 Skill 不会使 Tabbit 自动化变得可用。
|
|
104
|
-
|
|
105
|
-
## 开发验证
|
|
106
|
-
|
|
107
|
-
```sh
|
|
108
|
-
npm test
|
|
109
|
-
npm pack --dry-run
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## 许可证
|
|
113
|
-
|
|
114
|
-
MIT
|