abelworkflow 1.2.3 → 1.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/README.md +26 -25
- package/extensions/gpt-responses-compat.ts +166 -27
- package/lib/cli/main.mjs +18 -86
- package/lib/cli/pi.mjs +43 -0
- package/lib/cli/prompts.mjs +3 -3
- package/lib/installer/assets.mjs +35 -31
- package/lib/installer/install.mjs +29 -9
- package/lib/installer/links.mjs +97 -84
- package/lib/paths.mjs +0 -2
- package/lib/providers/claude.mjs +14 -3
- package/lib/providers/codex.mjs +14 -9
- package/lib/providers/pi.mjs +61 -68
- package/lib/providers/skills.mjs +22 -22
- package/lib/providers/url.mjs +32 -1
- package/lib/templates/codex/agents/default.toml +16 -54
- package/lib/templates/codex/agents/explorer.toml +25 -52
- package/lib/templates/codex/agents/planner.toml +23 -77
- package/lib/templates/codex/agents/reviewer.toml +16 -62
- package/lib/templates/codex/agents/worker.toml +30 -64
- package/lib/templates/codex/config-base.toml +0 -6
- package/lib/templates/{workflow/gitignore.template → gitignore.template} +0 -3
- package/lib/tools/cli-installer.mjs +40 -12
- package/package.json +13 -18
- package/lib/templates/workflow/AGENTS.md +0 -50
- package/lib/templates/workflow/commands/abel-design.md +0 -175
- package/lib/templates/workflow/commands/abel-diagnose.md +0 -63
- package/lib/templates/workflow/commands/abel-implement.md +0 -170
- package/lib/templates/workflow/commands/abel-init.md +0 -25
- package/skills/dev-browser/SKILL.md +0 -281
- package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
- package/skills/dev-browser/dist/scripts/start.js +0 -90
- package/skills/dev-browser/dist/src/client.d.ts +0 -92
- package/skills/dev-browser/dist/src/client.js +0 -310
- package/skills/dev-browser/dist/src/entrypoint.d.ts +0 -29
- package/skills/dev-browser/dist/src/entrypoint.js +0 -113
- package/skills/dev-browser/dist/src/index.d.ts +0 -3
- package/skills/dev-browser/dist/src/index.js +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts +0 -24
- package/skills/dev-browser/dist/src/page-api.js +0 -103
- package/skills/dev-browser/dist/src/relay.d.ts +0 -26
- package/skills/dev-browser/dist/src/relay.js +0 -567
- package/skills/dev-browser/dist/src/runtime.d.ts +0 -34
- package/skills/dev-browser/dist/src/runtime.js +0 -44
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -22
- package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -868
- package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -13
- package/skills/dev-browser/dist/src/snapshot/index.js +0 -13
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -12
- package/skills/dev-browser/dist/src/snapshot/inject.js +0 -12
- package/skills/dev-browser/dist/src/standalone.d.ts +0 -31
- package/skills/dev-browser/dist/src/standalone.js +0 -173
- package/skills/dev-browser/dist/src/startup.d.ts +0 -46
- package/skills/dev-browser/dist/src/startup.js +0 -77
- package/skills/dev-browser/dist/src/target-registry.d.ts +0 -28
- package/skills/dev-browser/dist/src/target-registry.js +0 -134
- package/skills/dev-browser/dist/src/types.d.ts +0 -26
- package/skills/dev-browser/dist/src/types.js +0 -1
- package/skills/dev-browser/package-lock.json +0 -1545
- package/skills/dev-browser/package.json +0 -35
- package/skills/dev-browser/references/scraping.md +0 -144
- package/skills/git-commit/SKILL.md +0 -124
- package/skills/time/SKILL.md +0 -119
- package/skills/time/scripts/time_cli.py +0 -143
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: abel-implement
|
|
3
|
-
description: Implement approved changes with mandatory TDD.
|
|
4
|
-
category: abel
|
|
5
|
-
tags: [abel, implementation, TDD]
|
|
6
|
-
argument-hint: [change_name]
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<!-- ABEL:START -->
|
|
10
|
-
**Arguments**
|
|
11
|
-
- Requested: `<change_name>`
|
|
12
|
-
|
|
13
|
-
**Guardrails**
|
|
14
|
-
|
|
15
|
-
- Keep changes tightly scoped to the requested outcome; enforce side-effect review before applying any modification.
|
|
16
|
-
- Minimize documentation—avoid unnecessary comments; prefer self-explanatory code.
|
|
17
|
-
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) for additional OpenSpec conventions or clarifications.
|
|
18
|
-
|
|
19
|
-
**TDD Guardrails (mandatory)**
|
|
20
|
-
- **Red Phase**: Create or execute the task's failing executable verification ONLY; implementation code is FORBIDDEN.
|
|
21
|
-
- **Green Phase**: Write MINIMAL code to satisfy the verification; over-engineering is FORBIDDEN.
|
|
22
|
-
- **Refactor Phase**: Optimize code quality while keeping target and affected verification green and introducing no new full-suite failures.
|
|
23
|
-
- **Mandatory**: Run the task's executable verification after EVERY code change; never skip verification.
|
|
24
|
-
- **Test-First**: Each task MUST have a failing executable verification before implementation begins.
|
|
25
|
-
|
|
26
|
-
**Skill Integration**: See `Stage Skill Matrix` (Implement column)
|
|
27
|
-
|
|
28
|
-
**Readiness Preflight (before any code or test write)**
|
|
29
|
-
|
|
30
|
-
1. Explicit argument takes precedence. Resolve it directly; ask only when the argument is missing or cannot be resolved uniquely. Never require unconditional `openspec view` confirmation.
|
|
31
|
-
2. Run `openspec status --change <change-name> --json`; read `schemaName`, `changeRoot`, `artifactPaths`, `applyRequires`, and `artifacts`.
|
|
32
|
-
3. Run `openspec schema which <schemaName> --json`, read the resolved `schema.yaml`, and extract `apply.tracks`. Resolve `apply.tracks` relative to `changeRoot`; it must name one concrete existing regular file inside `changeRoot`. Missing, null, non-concrete, or escaping paths fail closed; do not infer the tracking path from apply-instruction `contextFiles` or `tasks`.
|
|
33
|
-
4. Every artifact id in `applyRequires` must have status `done`; array presence or a general completion flag is insufficient.
|
|
34
|
-
5. Run `openspec validate <change-name> --strict --type change`; require zero issues.
|
|
35
|
-
6. Read every planning artifact reported by `artifactPaths`, then run `openspec instructions apply --change <change-name> --json` and follow the returned apply contract.
|
|
36
|
-
7. Verify stable Requirement and Scenario references, the Requirement → Scenario → Verification → Task chain, and every task verification contract. Rebuild the Gate A and Gate B summaries; if approval cannot be proven in the current conversation, show the summaries and require explicit user confirmation.
|
|
37
|
-
8. Run and record affected baseline tests and the full test suite before any write. Record each command, exit status, and normalized failure identities/reasons. Keep existing failures from the full suite separate from the target Red; an existing failure never counts as Red.
|
|
38
|
-
9. If any preflight item fails, STOP and return `/abel-design --change <change-name>`. Do not invent or repair product, behavior, or architecture decisions here.
|
|
39
|
-
|
|
40
|
-
**Tool Routing**:
|
|
41
|
-
- **TDD Cycle**: Autonomous refactoring
|
|
42
|
-
- **Final Review** (after all tasks):
|
|
43
|
-
- Backend refactor (subagents)
|
|
44
|
-
- Frontend refactor (subagents)
|
|
45
|
-
- **E2E tasks** → `/dev-browser`
|
|
46
|
-
|
|
47
|
-
After preflight, detect the project's verification tooling and work through the tracked tasks sequentially.
|
|
48
|
-
|
|
49
|
-
**TDD Cycle (per task)**
|
|
50
|
-
|
|
51
|
-
Consume these ordinary indented bullets from the task verification contract, never Markdown checkboxes:
|
|
52
|
-
- verification type: property | example | E2E | static
|
|
53
|
-
- Red command and expected failure reason
|
|
54
|
-
- Green expected behavior
|
|
55
|
-
- affected-suite command
|
|
56
|
-
- target scope/files
|
|
57
|
-
|
|
58
|
-
- Red must fail because of the target defect described by the contract.
|
|
59
|
-
- If the failure reason differs or the command is invalid, STOP and return `/abel-design --change <change-name>`; do not improvise a replacement contract.
|
|
60
|
-
- A non-behavior-change task starts with its specified failing executable static verification.
|
|
61
|
-
- A manual-only task is not implementation-ready; STOP and return `/abel-design --change <change-name>`.
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
┌─────────────────────────────────────────────────────────┐
|
|
65
|
-
│ Step 1: 🔴 Red Phase - Execute Contract Verification │
|
|
66
|
-
│ ├─ Use the contract's verification type and scope │
|
|
67
|
-
│ ├─ Generate the required failing verification via │
|
|
68
|
-
│ │ PROMPT: "Generate failing verification for: {task}│
|
|
69
|
-
│ │ Context: {code_context} │
|
|
70
|
-
│ │ Output: unified diff patch │
|
|
71
|
-
│ │ FORBIDDEN: implementation code" │
|
|
72
|
-
│ ├─ Apply verification code (after review) │
|
|
73
|
-
│ ├─ Run Red command → MUST FAIL for expected reason │
|
|
74
|
-
│ └─ If it passes or fails differently → STOP │
|
|
75
|
-
├─────────────────────────────────────────────────────────┤
|
|
76
|
-
│ Step 2: 🟢 Green Phase - Minimal Implementation │
|
|
77
|
-
│ ├─ Generate minimal implementation │
|
|
78
|
-
│ │ PROMPT: "Generate minimal code for verification: │
|
|
79
|
-
│ │ Verification: {verification_context} │
|
|
80
|
-
│ │ Context: {code_context} │
|
|
81
|
-
│ │ Output: unified diff patch │
|
|
82
|
-
│ │ FORBIDDEN: over-engineering" │
|
|
83
|
-
│ ├─ Apply implementation (after review & rewrite) │
|
|
84
|
-
│ ├─ Run Red command → MUST PASS │
|
|
85
|
-
│ └─ If fails → analyze error, fix, retry │
|
|
86
|
-
├─────────────────────────────────────────────────────────┤
|
|
87
|
-
│ Step 3: 🔵 Refactor Phase │
|
|
88
|
-
│ ├─ Analyzes code quality │
|
|
89
|
-
│ ├─ Apply standard refactoring techniques: │
|
|
90
|
-
│ │ ├─ Eliminate code duplication │
|
|
91
|
-
│ │ ├─ Improve naming and structure │
|
|
92
|
-
│ │ ├─ Enhance readability │
|
|
93
|
-
│ │ └─ Simplify logic where possible │
|
|
94
|
-
│ ├─ Apply refactoring changes │
|
|
95
|
-
│ ├─ Run affected-suite command → MUST STILL PASS │
|
|
96
|
-
│ └─ If fails → rollback refactoring │
|
|
97
|
-
└─────────────────────────────────────────────────────────┘
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Before applying any change, perform mandatory side-effect review.
|
|
101
|
-
|
|
102
|
-
After a task's TDD cycle completes, locate the task ID's checkbox in the concrete tracking file resolved from schema `apply.tracks`; require exactly one match and update only it. Zero or multiple matches must STOP and return `/abel-design --change <change-name>`. Never hardcode an artifact filename or infer the tracking path from apply instructions.
|
|
103
|
-
|
|
104
|
-
**Final Review & Refactor** (after all tasks complete)
|
|
105
|
-
|
|
106
|
-
1. Require all target tests to be green, then run the affected suites.
|
|
107
|
-
2. Re-run the same full-suite command (the full test suite), compare normalized failure identities with the recorded full-suite baseline, and require no new failures.
|
|
108
|
-
|
|
109
|
-
3. Execute global code review via subagents:
|
|
110
|
-
|
|
111
|
-
4. Wait for background tasks to complete; review diff patches.
|
|
112
|
-
5. Rewrite patches into production-grade code (per rewriting principle).
|
|
113
|
-
6. Apply refactoring changes.
|
|
114
|
-
7. Re-run target, affected, and full-suite verification against the baseline.
|
|
115
|
-
8. If a target test fails or the full suite has a new failure, analyze the root cause and fix or roll back.
|
|
116
|
-
9. Perform final side-effect review.
|
|
117
|
-
10. Report that the change is ready for archive; do not archive until the user explicitly authorizes `/opsx:archive`.
|
|
118
|
-
|
|
119
|
-
**TDD Output Format**
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
## /abel-implement (TDD Mode)
|
|
123
|
-
|
|
124
|
-
### Task 1/N: {task_description}
|
|
125
|
-
|
|
126
|
-
🔴 Red Phase
|
|
127
|
-
├─ Type: {verification_type}
|
|
128
|
-
├─ Generated: {verification_files_or_none}
|
|
129
|
-
├─ Run: {red_command}
|
|
130
|
-
└─ Result: failed for {expected_failure_reason} ✓
|
|
131
|
-
|
|
132
|
-
🟢 Green Phase
|
|
133
|
-
├─ Generated: {implementation_files}
|
|
134
|
-
├─ Run: {red_command}
|
|
135
|
-
└─ Result: {green_expected_behavior} ✓
|
|
136
|
-
|
|
137
|
-
🔵 Refactor Phase (Agent autonomous)
|
|
138
|
-
├─ Optimized: {description}
|
|
139
|
-
├─ Run: {affected_suite_command}
|
|
140
|
-
└─ Result: target verification green ✓
|
|
141
|
-
|
|
142
|
-
✓ Task complete → Next task
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### All Tasks Complete
|
|
147
|
-
|
|
148
|
-
🔍 Final Review & Refactor
|
|
149
|
-
|
|
150
|
-
Backend Review
|
|
151
|
-
├─ Files: {backend_files}
|
|
152
|
-
├─ Status: Running in background...
|
|
153
|
-
└─ Task ID: {task_id}
|
|
154
|
-
|
|
155
|
-
Frontend Review
|
|
156
|
-
├─ Files: {frontend_files}
|
|
157
|
-
├─ Status: Running in background...
|
|
158
|
-
└─ Task ID: {task_id}
|
|
159
|
-
|
|
160
|
-
[Waiting for completion...]
|
|
161
|
-
|
|
162
|
-
✓ Reviews complete
|
|
163
|
-
├─ Applied: {refactoring_summary}
|
|
164
|
-
├─ Target tests: green ✓
|
|
165
|
-
├─ Full suite: no failures beyond baseline ✓
|
|
166
|
-
└─ Ready for user-authorized archive
|
|
167
|
-
|
|
168
|
-
✓ Implementation complete
|
|
169
|
-
```
|
|
170
|
-
<!-- ABEL:END -->
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: abel-init
|
|
3
|
-
description: Initialize OpenSpec environment and validate required tooling.
|
|
4
|
-
category: abel
|
|
5
|
-
tags: [openspec, init, setup]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
<!-- ABEL:START -->
|
|
9
|
-
**Guardrails**
|
|
10
|
-
- Complete steps in order; stop on failure
|
|
11
|
-
- Preserve existing config; do not overwrite without confirmation
|
|
12
|
-
- Provide actionable remediation when a step fails
|
|
13
|
-
|
|
14
|
-
**Steps**
|
|
15
|
-
1. Detect OS and adapt command style (Unix/PowerShell).
|
|
16
|
-
2. Verify `openspec` availability via `openspec --version`.
|
|
17
|
-
3. If missing, install globally with `bun add --global @fission-ai/openspec@latest` when `bun --version` succeeds; otherwise use `npm install -g @fission-ai/openspec@latest`, then re-check version.
|
|
18
|
-
4. Run `openspec init` (or `openspec update` for existing projects).
|
|
19
|
-
5. Verify required skills:
|
|
20
|
-
- Skills: `/dev-browser` `/context7-auto-research` `/grok-search`
|
|
21
|
-
6. Output summary with ✓/✗:
|
|
22
|
-
- OpenSpec installation
|
|
23
|
-
- Project initialization
|
|
24
|
-
- Required skills availability
|
|
25
|
-
<!-- ABEL:END -->
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: dev-browser
|
|
3
|
-
description: Browser automation with page state shared while the server process runs. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include "go to [url]", "click on", "fill out the form", "take a screenshot", "scrape", "automate", "test the website", "log into", or any browser interaction request.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Dev Browser Skill
|
|
7
|
-
|
|
8
|
-
Browser automation that maintains page state across script executions while the server process runs. Write small, focused scripts to accomplish tasks incrementally.
|
|
9
|
-
|
|
10
|
-
## Choosing Your Approach
|
|
11
|
-
|
|
12
|
-
- **Local/source-available sites**: Read the source code first to write selectors directly
|
|
13
|
-
- **Unknown page layouts**: Use `getAISnapshot()` to discover elements and `selectSnapshotRef()` to interact with them
|
|
14
|
-
- **Visual feedback**: Take screenshots to see what the user sees
|
|
15
|
-
|
|
16
|
-
## Setup
|
|
17
|
-
|
|
18
|
-
Two supported startup modes are available on **Linux** and **native Windows**. Run the following commands from the installed `dev-browser` skill directory. Bash, Git Bash, and WSL are not required.
|
|
19
|
-
|
|
20
|
-
Startup never installs packages or browsers and treats the skill directory as read-only. If a preflight reports missing dependencies, run its explicit setup command yourself. Typical setup commands from the installed skill directory are:
|
|
21
|
-
|
|
22
|
-
```text
|
|
23
|
-
npm ci --omit=dev
|
|
24
|
-
node "node_modules/playwright/cli.js" install chromium
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
The Playwright command is required only for standalone mode. Startup does not download Chromium automatically.
|
|
28
|
-
|
|
29
|
-
### Standalone Mode (Default)
|
|
30
|
-
|
|
31
|
-
Launches a new Chromium browser for fresh automation sessions.
|
|
32
|
-
|
|
33
|
-
```text
|
|
34
|
-
node dist/scripts/start.js standalone
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Add `--headless` if needed:
|
|
38
|
-
|
|
39
|
-
```text
|
|
40
|
-
node dist/scripts/start.js standalone --headless
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Wait for the stable readiness line `Ready` before running scripts.
|
|
44
|
-
|
|
45
|
-
### Extension Mode
|
|
46
|
-
|
|
47
|
-
Connects to the user's existing Chrome browser. Use this when:
|
|
48
|
-
|
|
49
|
-
- The user is already logged into sites and wants you to do things behind an authed experience that isn't local dev.
|
|
50
|
-
- The user asks you to use the extension.
|
|
51
|
-
|
|
52
|
-
Start the relay server with:
|
|
53
|
-
|
|
54
|
-
```text
|
|
55
|
-
node dist/scripts/start.js extension
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Wait for `Waiting for extension to connect...`. Once the browser extension attaches, the relay logs `Extension connected`.
|
|
59
|
-
|
|
60
|
-
If the extension hasn't connected yet, tell the user to launch and activate it. Download link: https://github.com/SawyerHood/dev-browser/releases
|
|
61
|
-
|
|
62
|
-
## Support Matrix
|
|
63
|
-
|
|
64
|
-
| Mode | Linux | Native Windows | Readiness signal | Notes |
|
|
65
|
-
|------|-------|----------------|------------------|-------|
|
|
66
|
-
| standalone mode | Supported | Supported | `Ready` | Uses a process-owned temporary Chromium profile and cleans it on exit |
|
|
67
|
-
| extension mode | Supported | Supported | `Waiting for extension to connect...` then `Extension connected` | Requires the external browser extension to attach |
|
|
68
|
-
|
|
69
|
-
## Verification Checklist
|
|
70
|
-
|
|
71
|
-
- Start `standalone mode` with `node dist/scripts/start.js standalone`
|
|
72
|
-
- Observe the readiness line `Ready`
|
|
73
|
-
- Connect with `connect()` and create a named page
|
|
74
|
-
- Start `extension mode` with `node dist/scripts/start.js extension`
|
|
75
|
-
- Observe `Waiting for extension to connect...`
|
|
76
|
-
- Attach the browser extension and confirm `Extension connected`
|
|
77
|
-
|
|
78
|
-
## Known Differences
|
|
79
|
-
|
|
80
|
-
- `standalone mode` owns a temporary Chromium profile only for the server process lifetime and cleans it on exit.
|
|
81
|
-
- `extension mode` depends on the external browser extension and the user's existing Chrome session.
|
|
82
|
-
- Extension mode rejects viewport requests with HTTP 400; viewport sizing is supported only in standalone mode.
|
|
83
|
-
- In `extension mode`, relay readiness means the server is waiting for the extension; it does not imply browser control is available until `Extension connected` appears.
|
|
84
|
-
- `standalone mode` is the default path for deterministic local automation; `extension mode` is for working inside an already-authenticated browser.
|
|
85
|
-
|
|
86
|
-
## Server Boundary
|
|
87
|
-
|
|
88
|
-
- Both server modes and their direct APIs accept only loopback hosts: `localhost`, IPv4 loopback addresses in `127.0.0.0/8`, and `::1` (bracketed IPv6 is accepted in CLI input).
|
|
89
|
-
- Wildcard, LAN, and other hosts are rejected. This loopback-only binding is the primary access boundary.
|
|
90
|
-
- Origin checks are defense-in-depth, not authentication.
|
|
91
|
-
|
|
92
|
-
- State-changing `/pages` requests accept only originless JSON clients; browser-origin and non-JSON mutations are rejected before backend calls.
|
|
93
|
-
- `/cdp` accepts only originless WebSocket clients, which is the normal behavior of the Node client. Browser pages always send an `Origin` and are rejected.
|
|
94
|
-
- `/extension` accepts originless protocol clients and valid `chrome-extension://` origins. HTTP(S), `null`, malformed, and other origins are rejected.
|
|
95
|
-
- Rejected sockets close with WebSocket policy code `1008` before they can own a client ID, replace the extension, or route buffered messages.
|
|
96
|
-
|
|
97
|
-
## Non-goal Environments
|
|
98
|
-
|
|
99
|
-
- `WSL` and `Git Bash` are not part of the supported Windows path for this skill.
|
|
100
|
-
- Native Windows support means PowerShell / Command Prompt can use the documented entrypoints directly.
|
|
101
|
-
- If a user runs inside WSL or Git Bash, treat that as a separate environment rather than the official Windows support contract.
|
|
102
|
-
|
|
103
|
-
## Writing Scripts
|
|
104
|
-
|
|
105
|
-
Treat the installed `dev-browser` skill directory as read-only. Store scripts and artifacts in the OS temporary directory or task workspace, never inside the skill directory, and delete files you created when the task ends.
|
|
106
|
-
|
|
107
|
-
External scripts cannot use package self-resolution, so pass the installed skill directory as the first argument and import its compiled client by file URL. Example:
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
import { tmpdir } from "node:os";
|
|
111
|
-
import { join } from "node:path";
|
|
112
|
-
import { pathToFileURL } from "node:url";
|
|
113
|
-
|
|
114
|
-
const skillDir = process.argv[2];
|
|
115
|
-
if (!skillDir) throw new Error("Pass the installed dev-browser skill directory");
|
|
116
|
-
const { connect, waitForPageLoad } = await import(
|
|
117
|
-
pathToFileURL(join(skillDir, "dist", "src", "client.js")).href
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const client = await connect();
|
|
121
|
-
const page = await client.page("example", { viewport: { width: 1920, height: 1080 } });
|
|
122
|
-
|
|
123
|
-
await page.goto("https://example.com");
|
|
124
|
-
await waitForPageLoad(page);
|
|
125
|
-
const screenshotPath = join(tmpdir(), "dev-browser-screenshot.png");
|
|
126
|
-
await page.screenshot({ path: screenshotPath });
|
|
127
|
-
|
|
128
|
-
console.log({ title: await page.title(), url: page.url(), screenshotPath });
|
|
129
|
-
await client.disconnect();
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
This invocation works in Linux shells, PowerShell, and Command Prompt when the two paths are quoted:
|
|
133
|
-
|
|
134
|
-
```text
|
|
135
|
-
node "PATH_TO_SCRIPT.mjs" "PATH_TO_INSTALLED_DEV_BROWSER"
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Key Principles
|
|
139
|
-
|
|
140
|
-
1. **Small scripts**: Each script does ONE thing (navigate, click, fill, check)
|
|
141
|
-
2. **Evaluate state**: Log/return state at the end to decide next steps
|
|
142
|
-
3. **Descriptive page names**: Use `"checkout"`, `"login"`, not `"main"`
|
|
143
|
-
4. **Disconnect to exit**: `await client.disconnect()` - pages persist while the server process runs
|
|
144
|
-
5. **Plain JS in evaluate**: `page.evaluate()` runs in browser - no TypeScript syntax
|
|
145
|
-
|
|
146
|
-
## Workflow Loop
|
|
147
|
-
|
|
148
|
-
Follow this pattern for complex tasks:
|
|
149
|
-
|
|
150
|
-
1. **Write a script** to perform one action
|
|
151
|
-
2. **Run it** and observe the output
|
|
152
|
-
3. **Evaluate** - did it work? What's the current state?
|
|
153
|
-
4. **Decide** - is the task complete or do we need another script?
|
|
154
|
-
5. **Repeat** until task is done
|
|
155
|
-
|
|
156
|
-
### No TypeScript in Browser Context
|
|
157
|
-
|
|
158
|
-
Code passed to `page.evaluate()` runs in the browser, which doesn't understand TypeScript:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
// ✅ Correct: plain JavaScript
|
|
162
|
-
const text = await page.evaluate(() => {
|
|
163
|
-
return document.body.innerText;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// ❌ Wrong: TypeScript syntax will fail at runtime
|
|
167
|
-
const text = await page.evaluate(() => {
|
|
168
|
-
const el: HTMLElement = document.body; // Type annotation breaks in browser!
|
|
169
|
-
return el.innerText;
|
|
170
|
-
});
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Scraping Data
|
|
174
|
-
|
|
175
|
-
For scraping large datasets, intercept and replay network requests rather than scrolling the DOM. See [references/scraping.md](references/scraping.md) for the complete guide covering request capture, schema discovery, and paginated API replay.
|
|
176
|
-
|
|
177
|
-
## Client API
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
const client = await connect();
|
|
181
|
-
|
|
182
|
-
// Get or create named page (viewport only applies to new pages)
|
|
183
|
-
const page = await client.page("name");
|
|
184
|
-
const pageWithSize = await client.page("name", { viewport: { width: 1920, height: 1080 } });
|
|
185
|
-
|
|
186
|
-
const pages = await client.list(); // List all page names
|
|
187
|
-
await client.close("name"); // Close a page
|
|
188
|
-
await client.disconnect(); // Disconnect (pages persist while the server runs)
|
|
189
|
-
|
|
190
|
-
// ARIA Snapshot methods
|
|
191
|
-
const snapshot = await client.getAISnapshot("name"); // Get accessibility tree
|
|
192
|
-
const element = await client.selectSnapshotRef("name", "e5"); // Get element by ref
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
The `page` object is a standard Playwright Page.
|
|
196
|
-
|
|
197
|
-
## Waiting
|
|
198
|
-
|
|
199
|
-
```javascript
|
|
200
|
-
await waitForPageLoad(page); // After navigation
|
|
201
|
-
await page.waitForSelector(".results"); // For specific elements
|
|
202
|
-
await page.waitForURL("**/success"); // For specific URL
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Inspecting Page State
|
|
206
|
-
|
|
207
|
-
### Screenshots
|
|
208
|
-
|
|
209
|
-
```javascript
|
|
210
|
-
import { tmpdir } from "node:os";
|
|
211
|
-
import { join } from "node:path";
|
|
212
|
-
|
|
213
|
-
const screenshotPath = join(tmpdir(), "dev-browser-screenshot.png");
|
|
214
|
-
const fullScreenshotPath = join(tmpdir(), "dev-browser-full.png");
|
|
215
|
-
await page.screenshot({ path: screenshotPath });
|
|
216
|
-
await page.screenshot({ path: fullScreenshotPath, fullPage: true });
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Inspect the files, then remove them no later than task completion.
|
|
220
|
-
|
|
221
|
-
### ARIA Snapshot (Element Discovery)
|
|
222
|
-
|
|
223
|
-
Use `getAISnapshot()` to discover page elements. Returns YAML-formatted accessibility tree:
|
|
224
|
-
|
|
225
|
-
```yaml
|
|
226
|
-
- banner:
|
|
227
|
-
- link "Hacker News" [ref=e1]
|
|
228
|
-
- navigation:
|
|
229
|
-
- link "new" [ref=e2]
|
|
230
|
-
- main:
|
|
231
|
-
- list:
|
|
232
|
-
- listitem:
|
|
233
|
-
- link "Article Title" [ref=e8]
|
|
234
|
-
- link "328 comments" [ref=e9]
|
|
235
|
-
- contentinfo:
|
|
236
|
-
- textbox [ref=e10]
|
|
237
|
-
- /placeholder: "Search"
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
**Interpreting refs:**
|
|
241
|
-
|
|
242
|
-
- `[ref=eN]` - Element reference for interaction (visible, clickable elements only)
|
|
243
|
-
- `[checked]`, `[disabled]`, `[expanded]` - Element states
|
|
244
|
-
- `[level=N]` - Heading level
|
|
245
|
-
- `/url:`, `/placeholder:` - Element properties
|
|
246
|
-
|
|
247
|
-
**Interacting with refs:**
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
const snapshot = await client.getAISnapshot("hackernews");
|
|
251
|
-
console.log(snapshot); // Find the ref you need
|
|
252
|
-
|
|
253
|
-
const element = await client.selectSnapshotRef("hackernews", "e2");
|
|
254
|
-
await element.click();
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## Error Recovery
|
|
258
|
-
|
|
259
|
-
Page state persists after failures while the server process runs. Reuse the external script pattern above from the OS temporary directory or task workspace:
|
|
260
|
-
|
|
261
|
-
```javascript
|
|
262
|
-
const client = await connect();
|
|
263
|
-
const page = await client.page("hackernews");
|
|
264
|
-
|
|
265
|
-
const debugScreenshotPath = join(tmpdir(), "dev-browser-debug.png");
|
|
266
|
-
await page.screenshot({ path: debugScreenshotPath });
|
|
267
|
-
console.log({
|
|
268
|
-
url: page.url(),
|
|
269
|
-
title: await page.title(),
|
|
270
|
-
bodyText: await page.textContent("body").then((t) => t?.slice(0, 200)),
|
|
271
|
-
debugScreenshotPath,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
await client.disconnect();
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
Run it with the same cross-platform invocation and delete the debug script and screenshot when finished:
|
|
278
|
-
|
|
279
|
-
```text
|
|
280
|
-
node "PATH_TO_DEBUG_SCRIPT.mjs" "PATH_TO_INSTALLED_DEV_BROWSER"
|
|
281
|
-
```
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { formatHttpUrl, parseEntrypointArgs, resolveHostForProbe } from "../src/entrypoint.js";
|
|
5
|
-
import { assertRuntimeDependenciesAvailable, getInvalidRuntimeDependencies, getLockedRuntimeDependencies, isPlaywrightChromiumInstalled, isTcpPortInUse, resolveSkillDirFromEntrypoint, } from "../src/runtime.js";
|
|
6
|
-
import { assertPlaywrightChromiumAvailable, preflightStandaloneStartup, runEntrypoint, } from "../src/startup.js";
|
|
7
|
-
const skillDir = resolveSkillDirFromEntrypoint(import.meta.url);
|
|
8
|
-
const require = createRequire(import.meta.url);
|
|
9
|
-
async function main() {
|
|
10
|
-
const args = parseEntrypointArgs(process.argv.slice(2));
|
|
11
|
-
await ensureSkillDependencies();
|
|
12
|
-
await runEntrypoint(args, {
|
|
13
|
-
serveStandalone: async (options) => {
|
|
14
|
-
const { serve } = await import("../src/index.js");
|
|
15
|
-
return serve(options);
|
|
16
|
-
},
|
|
17
|
-
serveExtension: async (options) => {
|
|
18
|
-
const { serveRelay } = await import("../src/relay.js");
|
|
19
|
-
return serveRelay(options);
|
|
20
|
-
},
|
|
21
|
-
registerShutdown,
|
|
22
|
-
keepAlive: () => new Promise(() => { }),
|
|
23
|
-
log: (line) => console.log(line),
|
|
24
|
-
ensureBrowser: async () => {
|
|
25
|
-
const { registry } = require("playwright-core/lib/server/registry/index");
|
|
26
|
-
await assertPlaywrightChromiumAvailable({
|
|
27
|
-
isInstalled: () => isPlaywrightChromiumInstalled({
|
|
28
|
-
headless: args.headless,
|
|
29
|
-
findExecutable: (name) => registry.findExecutable(name),
|
|
30
|
-
exists: existsSync,
|
|
31
|
-
}),
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
preflightStandalone: () => preflightStandaloneStartup(args, {
|
|
35
|
-
checkServer: async (host, port) => {
|
|
36
|
-
try {
|
|
37
|
-
const response = await fetch(formatHttpUrl(resolveHostForProbe(host), port), {
|
|
38
|
-
signal: AbortSignal.timeout(1000),
|
|
39
|
-
});
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
return { ok: false };
|
|
42
|
-
}
|
|
43
|
-
const info = (await response.json());
|
|
44
|
-
return { ok: true, info };
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return { ok: false };
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
isPortInUse: isTcpPortInUse,
|
|
51
|
-
log: (line) => console.log(line),
|
|
52
|
-
}),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
async function ensureSkillDependencies() {
|
|
56
|
-
const packageLock = JSON.parse(readFileSync(join(skillDir, "package-lock.json"), "utf8"));
|
|
57
|
-
const invalidDependencies = getInvalidRuntimeDependencies({
|
|
58
|
-
skillDir,
|
|
59
|
-
lockedDependencies: getLockedRuntimeDependencies(packageLock),
|
|
60
|
-
readPackageVersion: (path) => {
|
|
61
|
-
try {
|
|
62
|
-
const installed = JSON.parse(readFileSync(path, "utf8"));
|
|
63
|
-
return typeof installed.version === "string" ? installed.version : undefined;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
assertRuntimeDependenciesAvailable({
|
|
71
|
-
invalidDependencies,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
function registerShutdown(stop) {
|
|
75
|
-
let shutdownPromise;
|
|
76
|
-
const shutdown = () => {
|
|
77
|
-
shutdownPromise ??= (async () => {
|
|
78
|
-
await stop();
|
|
79
|
-
process.exit(0);
|
|
80
|
-
})();
|
|
81
|
-
return shutdownPromise;
|
|
82
|
-
};
|
|
83
|
-
process.on("SIGINT", shutdown);
|
|
84
|
-
process.on("SIGTERM", shutdown);
|
|
85
|
-
process.on("SIGHUP", shutdown);
|
|
86
|
-
}
|
|
87
|
-
main().catch((error) => {
|
|
88
|
-
console.error("Failed to start dev-browser:", error);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { type ElementHandle, type Page } from "playwright";
|
|
2
|
-
import type { ViewportSize } from "./types.js";
|
|
3
|
-
/**
|
|
4
|
-
* Options for waiting for page load
|
|
5
|
-
*/
|
|
6
|
-
export interface WaitForPageLoadOptions {
|
|
7
|
-
/** Maximum time to wait in ms (default: 10000) */
|
|
8
|
-
timeout?: number;
|
|
9
|
-
/** How often to check page state in ms (default: 50) */
|
|
10
|
-
pollInterval?: number;
|
|
11
|
-
/** Minimum time to wait even if page appears ready in ms (default: 100) */
|
|
12
|
-
minimumWait?: number;
|
|
13
|
-
/** Wait for network to be idle (no pending requests) (default: true) */
|
|
14
|
-
waitForNetworkIdle?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Result of waiting for page load
|
|
18
|
-
*/
|
|
19
|
-
export interface WaitForPageLoadResult {
|
|
20
|
-
/** Whether the page is considered loaded */
|
|
21
|
-
success: boolean;
|
|
22
|
-
/** Document ready state when finished */
|
|
23
|
-
readyState: string;
|
|
24
|
-
/** Number of pending network requests when finished */
|
|
25
|
-
pendingRequests: number;
|
|
26
|
-
/** Time spent waiting in ms */
|
|
27
|
-
waitTimeMs: number;
|
|
28
|
-
/** Whether timeout was reached */
|
|
29
|
-
timedOut: boolean;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Wait for a page to finish loading using document.readyState and Playwright request events.
|
|
33
|
-
*
|
|
34
|
-
* Uses browser-use's approach of:
|
|
35
|
-
* - Checking document.readyState for 'complete'
|
|
36
|
-
* - Monitoring pending network requests via Playwright
|
|
37
|
-
* - Filtering out ads, tracking, and non-critical resources
|
|
38
|
-
* - Graceful timeout handling (continues even if timeout reached)
|
|
39
|
-
*/
|
|
40
|
-
export declare function waitForPageLoad(page: Page, options?: WaitForPageLoadOptions): Promise<WaitForPageLoadResult>;
|
|
41
|
-
/** Server mode information */
|
|
42
|
-
export interface ServerInfo {
|
|
43
|
-
wsEndpoint: string;
|
|
44
|
-
mode: "standalone" | "extension";
|
|
45
|
-
extensionConnected?: boolean;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Options for creating or getting a page
|
|
49
|
-
*/
|
|
50
|
-
export interface PageOptions {
|
|
51
|
-
/** Viewport size for new pages */
|
|
52
|
-
viewport?: ViewportSize;
|
|
53
|
-
}
|
|
54
|
-
export interface DevBrowserClient {
|
|
55
|
-
page: (name: string, options?: PageOptions) => Promise<Page>;
|
|
56
|
-
list: () => Promise<string[]>;
|
|
57
|
-
close: (name: string) => Promise<void>;
|
|
58
|
-
disconnect: () => Promise<void>;
|
|
59
|
-
/**
|
|
60
|
-
* Get AI-friendly ARIA snapshot for a page.
|
|
61
|
-
* Returns YAML format with refs like [ref=e1], [ref=e2].
|
|
62
|
-
* Refs are stored on window.__devBrowserRefs for cross-connection persistence.
|
|
63
|
-
*/
|
|
64
|
-
getAISnapshot: (name: string) => Promise<string>;
|
|
65
|
-
/**
|
|
66
|
-
* Get an element handle by its ref from the last getAISnapshot call.
|
|
67
|
-
* Refs persist across Playwright connections.
|
|
68
|
-
*/
|
|
69
|
-
selectSnapshotRef: (name: string, ref: string) => Promise<ElementHandle | null>;
|
|
70
|
-
/**
|
|
71
|
-
* Get server information including mode and extension connection status.
|
|
72
|
-
*/
|
|
73
|
-
getServerInfo: () => Promise<ServerInfo>;
|
|
74
|
-
}
|
|
75
|
-
interface TargetLookupSession {
|
|
76
|
-
send(method: "Target.getTargetInfo"): Promise<{
|
|
77
|
-
targetInfo: {
|
|
78
|
-
targetId: string;
|
|
79
|
-
};
|
|
80
|
-
}>;
|
|
81
|
-
detach(): Promise<void>;
|
|
82
|
-
}
|
|
83
|
-
interface TargetLookupContext<TPage> {
|
|
84
|
-
pages(): TPage[];
|
|
85
|
-
newCDPSession(page: TPage): Promise<TargetLookupSession>;
|
|
86
|
-
}
|
|
87
|
-
interface TargetLookupBrowser<TPage> {
|
|
88
|
-
contexts(): Array<TargetLookupContext<TPage>>;
|
|
89
|
-
}
|
|
90
|
-
export declare function findPageByTargetId<TPage>(browser: TargetLookupBrowser<TPage>, targetId: string): Promise<TPage | null>;
|
|
91
|
-
export declare function connect(serverUrl?: string): Promise<DevBrowserClient>;
|
|
92
|
-
export {};
|