ghostrun-cli 1.0.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/LICENSE +21 -0
- package/MCP-SETUP.md +82 -0
- package/README.md +632 -0
- package/ghostrun.js +10458 -0
- package/mcp-server.js +377 -0
- package/package.json +53 -0
- package/templates/api-health.flow.json +26 -0
- package/templates/checkout-guest.flow.json +38 -0
- package/templates/contact-form.flow.json +34 -0
- package/templates/login-basic.flow.json +32 -0
- package/templates/logout.flow.json +28 -0
- package/templates/nav-smoke.flow.json +38 -0
- package/templates/password-reset.flow.json +30 -0
- package/templates/search-results.flow.json +30 -0
- package/templates/signup-verify.flow.json +34 -0
- package/templates/smoke-homepage.flow.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
# GhostRun
|
|
2
|
+
|
|
3
|
+
Record once. Replay as a ghost.
|
|
4
|
+
|
|
5
|
+
Memory-driven browser automation and API testing CLI — record real browser flows, replay them headlessly, test REST APIs with assertions and variable extraction, run load tests, detect failures with AI analysis, and chat with your test suite. Entirely local.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g ghostrun-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or run from source:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/your-org/ghostrun
|
|
21
|
+
cd ghostrun
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
node ghostrun.js init # guided setup
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ghostrun init # setup wizard: installs Chromium, configures AI
|
|
33
|
+
ghostrun learn https://yourapp.com # record a browser flow
|
|
34
|
+
ghostrun api:learn # import a .flow.json with API actions
|
|
35
|
+
ghostrun run <flow-id> # replay headlessly (browser or API)
|
|
36
|
+
ghostrun perf:run <flow-id> # load test an API flow (VU-based)
|
|
37
|
+
ghostrun chat # AI chat: ask questions, run flows by name
|
|
38
|
+
ghostrun serve --ui # web dashboard at http://localhost:3000
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## What Needs AI vs What Doesn't
|
|
44
|
+
|
|
45
|
+
Every core feature works with zero AI. AI is an optional enhancement.
|
|
46
|
+
|
|
47
|
+
| Feature | AI Required? | Notes |
|
|
48
|
+
|---------|:------------:|-------|
|
|
49
|
+
| Browser recording | No | Real click/input capture via Playwright |
|
|
50
|
+
| Flow execution | No | Headless Playwright replay |
|
|
51
|
+
| Screenshot capture | No | PNG per step, pass and fail |
|
|
52
|
+
| Failure detection | No | Stops on error, shows what failed |
|
|
53
|
+
| Selector repair (`flow:fix`) | No | Interactive browser-based fix |
|
|
54
|
+
| Screenshot diff (`run:diff`) | No | Pixel comparison, no AI needed |
|
|
55
|
+
| Flow scheduling | No | Cron-based, runs offline |
|
|
56
|
+
| Monitor & diff extracted data | No | `ghostrun monitor <flow>` |
|
|
57
|
+
| PII sanitization | No | Regex-based, local only |
|
|
58
|
+
| Web dashboard | No | `ghostrun serve --ui` |
|
|
59
|
+
| **API testing** | No | HTTP requests, assertions, variable extraction |
|
|
60
|
+
| **Environment profiles** | No | Named env sets, injected at runtime |
|
|
61
|
+
| **Load testing** | No | VU-based perf runs, p50/p95/p99 stats |
|
|
62
|
+
| **k6 export** | No | `perf:export` generates a k6 script |
|
|
63
|
+
| **Failure analysis** | Optional ✨ | Plain-English explanation of why it failed |
|
|
64
|
+
| **Auto run summary** | Optional ✨ | Attached to every failed run automatically |
|
|
65
|
+
| **Chat assistant** | Optional ✨ | Q&A + run flows by name via `ghostrun chat` |
|
|
66
|
+
|
|
67
|
+
**Bottom line:** Record, replay, schedule, diff, and fix flows entirely offline. AI adds explanations and a conversational interface.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## AI Setup
|
|
72
|
+
|
|
73
|
+
### Option 1 — Local (Default, Recommended)
|
|
74
|
+
|
|
75
|
+
GhostRun uses **Ollama** by default. No API key, no internet, runs on your machine.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
brew install ollama
|
|
79
|
+
ollama serve &
|
|
80
|
+
ollama pull gemma3:4b # 2.6 GB, fast on Apple Silicon
|
|
81
|
+
node ghostrun.js status # → AI Provider: Ollama (gemma3:4b)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Model options by hardware:**
|
|
85
|
+
|
|
86
|
+
| Model | Size | Best for |
|
|
87
|
+
|-------|------|---------|
|
|
88
|
+
| `gemma3:4b` | 2.6 GB | Apple Silicon M1/M2/M3, fast |
|
|
89
|
+
| `gemma2:9b` | 5.4 GB | Better quality, more RAM needed |
|
|
90
|
+
| `llama3.2:3b` | 2.0 GB | Fastest, lighter quality |
|
|
91
|
+
|
|
92
|
+
Override model: `export GHOSTRUN_OLLAMA_MODEL=llama3.2:3b`
|
|
93
|
+
|
|
94
|
+
### Option 2 — Anthropic Cloud (Fallback)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Fallback chain
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
run → try Ollama → if down → try Anthropic → if no key → skip AI silently
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Environment Variables
|
|
107
|
+
|
|
108
|
+
| Variable | Default | Description |
|
|
109
|
+
|----------|---------|-------------|
|
|
110
|
+
| `GHOSTRUN_AI_PROVIDER` | auto | `ollama`, `anthropic`, or auto |
|
|
111
|
+
| `GHOSTRUN_OLLAMA_URL` | `http://localhost:11434` | Ollama server URL |
|
|
112
|
+
| `GHOSTRUN_OLLAMA_MODEL` | auto-detected | Model to use |
|
|
113
|
+
| `ANTHROPIC_API_KEY` | — | Anthropic API key (cloud fallback) |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Commands
|
|
118
|
+
|
|
119
|
+
### Setup
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ghostrun init # guided setup wizard
|
|
123
|
+
ghostrun status # stats + AI provider info
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Recording
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
ghostrun learn <url> [name] # record a flow (real browser opens)
|
|
130
|
+
ghostrun learn <url> --name "Login" # with explicit name
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Running
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
ghostrun run <id|name> # headless execution
|
|
137
|
+
ghostrun run <id|name> --visible # show the browser window
|
|
138
|
+
ghostrun run <id|name> --output json # structured JSON output with extracted data
|
|
139
|
+
ghostrun run <id|name> --var key=val # inject variables
|
|
140
|
+
ghostrun run <id|name> --session-save mysession # save cookies/storage
|
|
141
|
+
ghostrun run <id|name> --session-load mysession # restore cookies/storage
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Flow Management
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
ghostrun flow:list # list all flows
|
|
148
|
+
ghostrun flow:show <id|name> # show steps
|
|
149
|
+
ghostrun flow:fix <id|name> # fix broken selectors interactively
|
|
150
|
+
ghostrun flow:delete <id|name> # delete a flow
|
|
151
|
+
ghostrun flow:export <id|name> # export to .flow.json
|
|
152
|
+
ghostrun flow:import <file> # import from .flow.json
|
|
153
|
+
ghostrun flow:clone <id|name> # duplicate a flow
|
|
154
|
+
ghostrun flow:rename <id|name> <new> # rename a flow
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Data Extraction & Monitoring
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
ghostrun monitor <id|name> # run + show extracted data, diff vs previous
|
|
161
|
+
ghostrun run:show <id> # step details + screenshots + extracted data
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Run History
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
ghostrun run:list # list recent runs
|
|
168
|
+
ghostrun run:show <id> # step details + screenshots
|
|
169
|
+
ghostrun run:diff <id1> <id2> # visual screenshot diff (no AI needed)
|
|
170
|
+
ghostrun run:analyze <id> # AI failure analysis (optional)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Scheduling
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
ghostrun flow:schedule <id> "<cron>" # e.g. "0 9 * * *" = daily 9am
|
|
177
|
+
ghostrun schedule:list # list all schedules
|
|
178
|
+
ghostrun schedule:remove <id> # remove a schedule
|
|
179
|
+
ghostrun serve # start the scheduler daemon
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Web Dashboard
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
ghostrun serve --ui # launch dashboard at http://localhost:3000
|
|
186
|
+
ghostrun serve --ui --port 8080 # custom port
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The dashboard shows:
|
|
190
|
+
- All flows with one-click run buttons
|
|
191
|
+
- Live run log with SSE streaming
|
|
192
|
+
- Run history with status and duration
|
|
193
|
+
- Chat tab for natural-language interaction
|
|
194
|
+
|
|
195
|
+
### Chat Assistant
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
ghostrun chat # interactive AI chat (requires Ollama or ANTHROPIC_API_KEY)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Ask questions in plain English:
|
|
202
|
+
- `did my login flow pass recently?`
|
|
203
|
+
- `what flows do I have?`
|
|
204
|
+
- `run the login flow` ← executes the flow with confirmation
|
|
205
|
+
|
|
206
|
+
### Test Suites
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
ghostrun suite:create <name> # create a suite
|
|
210
|
+
ghostrun suite:add <suite> <flow> # add a flow to a suite
|
|
211
|
+
ghostrun suite:list # list suites
|
|
212
|
+
ghostrun suite:show <suite> # show flows in suite
|
|
213
|
+
ghostrun suite:run <suite> # run all flows in suite
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Visual Baselines
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
ghostrun baseline:set <flow-id> # capture reference screenshots
|
|
220
|
+
ghostrun baseline:clear <flow-id> # clear baselines
|
|
221
|
+
ghostrun baseline:show <flow-id> # list baselines
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Importing Flows
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
ghostrun flow:from-curl # paste a curl command → instant flow
|
|
228
|
+
ghostrun flow:from-curl "curl -X POST https://api.example.com/users -H 'Content-Type: application/json' -d '{\"name\":\"Alice\"}'"
|
|
229
|
+
ghostrun flow:from-spec openapi.json # import all endpoints from an OpenAPI spec
|
|
230
|
+
ghostrun flow:from-spec swagger.yaml # YAML supported too
|
|
231
|
+
ghostrun flow:import <file> # import a .flow.json directly
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`flow:from-curl` parses the curl command (method, headers, body, bearer auth) and creates a ready-to-run flow with a status assertion. `flow:from-spec` reads an OpenAPI/Swagger spec and lets you choose: one flow per tag group, one per endpoint, or one big flow.
|
|
235
|
+
|
|
236
|
+
### Run Reports
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
ghostrun run <id> --report html # run + save HTML report
|
|
240
|
+
ghostrun perf:run <id> --report html # load test + save HTML report
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Reports are dark-themed HTML files saved to the current directory — shareable, self-contained, screenshot-inclusive.
|
|
244
|
+
|
|
245
|
+
### API Testing
|
|
246
|
+
|
|
247
|
+
Import an API flow from a `.flow.json` file (no browser needed):
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
ghostrun api:learn # interactive: pick a .flow.json file to import
|
|
251
|
+
ghostrun flow:import <file> # import directly by path
|
|
252
|
+
ghostrun run <id|name> # runs pure API flows without launching a browser
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
When all steps in a flow are API actions (`http:request`, `assert:response`, `set:variable`, `extract:json`, `env:switch`), GhostRun skips Playwright entirely — execution is ~30ms.
|
|
256
|
+
|
|
257
|
+
### Environments
|
|
258
|
+
|
|
259
|
+
Named variable sets injected at flow start. Great for dev / staging / prod:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
ghostrun env:create <name> [base-url] # create an environment profile
|
|
263
|
+
ghostrun env:list # list all environments
|
|
264
|
+
ghostrun env:show <name> # show variables in an environment
|
|
265
|
+
ghostrun env:set <name> <key=value> # add or update a variable
|
|
266
|
+
ghostrun env:use <name> # set as active environment
|
|
267
|
+
ghostrun env:delete <name> # delete an environment
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The active environment's variables are automatically injected before each flow run. Use `{{variableName}}` in any URL, header, or body field to reference them.
|
|
271
|
+
|
|
272
|
+
### Variable Inspection
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
ghostrun var:dump <run-id> # show all variables extracted during a run
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Load & Performance Testing
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
ghostrun perf:run <id|name> # run a load test against an API flow
|
|
282
|
+
ghostrun perf:run <id|name> --vus 20 --duration 30 --ramp-up 5
|
|
283
|
+
ghostrun perf:export <id|name> # generate a k6 script from the flow
|
|
284
|
+
ghostrun perf:export <id|name> --output mytest.js
|
|
285
|
+
ghostrun perf:list # list past perf runs
|
|
286
|
+
ghostrun perf:show <perf-run-id> # show stats for a specific perf run
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**perf:run options:**
|
|
290
|
+
|
|
291
|
+
| Flag | Default | Description |
|
|
292
|
+
|------|---------|-------------|
|
|
293
|
+
| `--vus <n>` | 10 | Number of virtual users |
|
|
294
|
+
| `--duration <s>` | 30 | Test duration in seconds |
|
|
295
|
+
| `--ramp-up <s>` | 5 | Ramp-up time (VUs staggered over this window) |
|
|
296
|
+
| `--timeout <ms>` | 10000 | Per-request timeout in ms |
|
|
297
|
+
|
|
298
|
+
Output includes: HTTP Requests, HTTP Success Rate, Avg RPS, p50/p95/p99 latency, min/max, per-step breakdown, and separate Checks Passed/Failed count.
|
|
299
|
+
|
|
300
|
+
**perf:compare** — diff two runs side by side to see if a deploy made things faster or slower:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
ghostrun perf:compare <run-A-id> <run-B-id>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Shows p50/p95/p99/RPS for both runs with color-coded deltas (green = better, red = worse).
|
|
307
|
+
|
|
308
|
+
**perf:export** generates a valid k6 JavaScript file with:
|
|
309
|
+
- VU stages matching your `--vus`/`--duration`/`--ramp-up` config
|
|
310
|
+
- `http.get`/`http.post` calls with headers and JSON body
|
|
311
|
+
- `check()` assertions mapped to your `assert:response` steps
|
|
312
|
+
- `Trend` metrics per step for p95 thresholds
|
|
313
|
+
- `{{variable}}` → template literal interpolation
|
|
314
|
+
|
|
315
|
+
### MCP Server
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
node mcp-server.js # start MCP server (Claude Desktop, Cursor, etc.)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
See [MCP-SETUP.md](MCP-SETUP.md) for connection setup.
|
|
322
|
+
|
|
323
|
+
Tools exposed: `list_flows`, `get_flow`, `run_flow`, `get_run_result`, `list_runs`, `delete_flow`, `get_status`
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## CI/CD Integration
|
|
328
|
+
|
|
329
|
+
### GitHub Actions
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
# .github/workflows/ghostrun.yml
|
|
333
|
+
name: GhostRun Tests
|
|
334
|
+
|
|
335
|
+
on: [push, pull_request]
|
|
336
|
+
|
|
337
|
+
jobs:
|
|
338
|
+
test:
|
|
339
|
+
runs-on: ubuntu-latest
|
|
340
|
+
steps:
|
|
341
|
+
- uses: actions/checkout@v4
|
|
342
|
+
|
|
343
|
+
- uses: actions/setup-node@v4
|
|
344
|
+
with:
|
|
345
|
+
node-version: 20
|
|
346
|
+
|
|
347
|
+
- name: Install GhostRun
|
|
348
|
+
run: npm install -g ghostrun-cli
|
|
349
|
+
|
|
350
|
+
- name: Install Playwright browsers
|
|
351
|
+
run: npx playwright install chromium --with-deps
|
|
352
|
+
|
|
353
|
+
- name: Import test flows
|
|
354
|
+
run: |
|
|
355
|
+
ghostrun flow:import test-flows/health-check.flow.json
|
|
356
|
+
ghostrun flow:import test-flows/auth-and-users.flow.json
|
|
357
|
+
|
|
358
|
+
- name: Run flows
|
|
359
|
+
run: |
|
|
360
|
+
ghostrun run "API Health Check" --report html
|
|
361
|
+
ghostrun run "Auth + User List" --report html
|
|
362
|
+
|
|
363
|
+
- name: Upload reports
|
|
364
|
+
uses: actions/upload-artifact@v4
|
|
365
|
+
if: always()
|
|
366
|
+
with:
|
|
367
|
+
name: ghostrun-reports
|
|
368
|
+
path: ghostrun-report-*.html
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Exit Codes
|
|
372
|
+
|
|
373
|
+
GhostRun exits with code `1` on failure and `0` on pass — standard CI behaviour, no extra flags needed.
|
|
374
|
+
|
|
375
|
+
### API-only flows in CI
|
|
376
|
+
|
|
377
|
+
API flows skip Playwright entirely — no `playwright install` step needed:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
- name: Install GhostRun (API testing only)
|
|
381
|
+
run: npm install -g ghostrun-cli
|
|
382
|
+
# No playwright install needed for pure API flows
|
|
383
|
+
|
|
384
|
+
- name: Run API tests
|
|
385
|
+
run: ghostrun run "Auth + User List"
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Selector Repair (`flow:fix`)
|
|
391
|
+
|
|
392
|
+
When a flow fails because a selector broke:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
ghostrun flow:fix <id|name>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Browser opens, replays all passing steps automatically, **pauses on broken ones**, and asks you to click the correct element. Selector is updated and saved. No manual editing.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Screenshot Diff (`run:diff`)
|
|
403
|
+
|
|
404
|
+
Compare any two runs pixel-by-pixel — no AI needed:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
ghostrun run:diff <run1-id> <run2-id>
|
|
408
|
+
|
|
409
|
+
Step Status Diff % Screenshot
|
|
410
|
+
──────────────────────────────────────────────────────────
|
|
411
|
+
1 same 0.0% Navigate to homepage
|
|
412
|
+
2 same 0.1% Click Login
|
|
413
|
+
3 changed 12.4% Fill email field
|
|
414
|
+
4 same 0.0% Submit form
|
|
415
|
+
|
|
416
|
+
3 same 1 changed
|
|
417
|
+
Diff images: ~/.ghostrun/diffs/abc123_vs_def456/
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Flow Actions Reference
|
|
423
|
+
|
|
424
|
+
All actions you can use in recorded or imported `.flow.json` files:
|
|
425
|
+
|
|
426
|
+
### Navigation
|
|
427
|
+
|
|
428
|
+
| Action | Fields | Description |
|
|
429
|
+
|--------|--------|-------------|
|
|
430
|
+
| `navigate` | `url` | Go to URL |
|
|
431
|
+
| `reload` | — | Reload the current page |
|
|
432
|
+
| `back` | — | Browser back |
|
|
433
|
+
| `forward` | — | Browser forward |
|
|
434
|
+
|
|
435
|
+
### Interaction
|
|
436
|
+
|
|
437
|
+
| Action | Fields | Description |
|
|
438
|
+
|--------|--------|-------------|
|
|
439
|
+
| `click` | `selector` | Left-click an element |
|
|
440
|
+
| `dblclick` | `selector` | Double-click an element |
|
|
441
|
+
| `fill` | `selector`, `value` | Clear field and type value |
|
|
442
|
+
| `type` | `selector`, `value`, `delay?` | Type with configurable key delay (ms) |
|
|
443
|
+
| `clear` | `selector` | Clear a field |
|
|
444
|
+
| `select` | `selector`, `value` | Select a dropdown option by value |
|
|
445
|
+
| `check` | `selector`, `value: "true"\|"false"` | Check/uncheck a checkbox |
|
|
446
|
+
| `focus` | `selector` | Focus an element |
|
|
447
|
+
| `hover` | `selector` | Mouse hover |
|
|
448
|
+
| `drag` | `selector`, `targetSelector` | Drag one element to another |
|
|
449
|
+
| `keyboard` | `key`, `selector?` | Press a key (e.g. `Enter`, `Tab`, `Control+a`) |
|
|
450
|
+
| `upload` | `selector`, `value` | Set file input (comma-separated paths) |
|
|
451
|
+
|
|
452
|
+
### Waiting
|
|
453
|
+
|
|
454
|
+
| Action | Fields | Description |
|
|
455
|
+
|--------|--------|-------------|
|
|
456
|
+
| `wait` | `selector` | Wait for element to appear |
|
|
457
|
+
| `wait:text` | `selector`, `value` | Wait until element contains text |
|
|
458
|
+
| `wait:url` | `value` | Wait for URL to match pattern |
|
|
459
|
+
| `wait:ms` | `value` | Wait for N milliseconds |
|
|
460
|
+
|
|
461
|
+
### Scrolling
|
|
462
|
+
|
|
463
|
+
| Action | Fields | Description |
|
|
464
|
+
|--------|--------|-------------|
|
|
465
|
+
| `scroll` | `selector?` | Scroll to element (or page) |
|
|
466
|
+
| `scroll:element` | `selector` | Scroll element into view |
|
|
467
|
+
| `scroll:bottom` | — | Scroll to bottom of page |
|
|
468
|
+
| `scroll:load` | `value?` | Scroll to bottom, wait for load (repeat N times) |
|
|
469
|
+
| `next:page` | `selector?` | Click next page link and wait |
|
|
470
|
+
|
|
471
|
+
### Assertions
|
|
472
|
+
|
|
473
|
+
| Action | Fields | Description |
|
|
474
|
+
|--------|--------|-------------|
|
|
475
|
+
| `assert:visible` | `selector` | Assert element is visible |
|
|
476
|
+
| `assert:hidden` | `selector` | Assert element is not visible |
|
|
477
|
+
| `assert:text` | `selector`, `value` | Assert element contains text |
|
|
478
|
+
| `assert:not-text` | `selector`, `value` | Assert element does NOT contain text |
|
|
479
|
+
| `assert:value` | `selector`, `value` | Assert input value |
|
|
480
|
+
| `assert:count` | `selector`, `value` | Assert number of matching elements |
|
|
481
|
+
| `assert:attr` | `selector`, `value: "attr=expected"` | Assert element attribute |
|
|
482
|
+
|
|
483
|
+
### Data Extraction
|
|
484
|
+
|
|
485
|
+
| Action | Fields | Description |
|
|
486
|
+
|--------|--------|-------------|
|
|
487
|
+
| `extract` | `selector`, `value: "variableName"` | Extract text → variable |
|
|
488
|
+
| `screenshot` | — | Capture screenshot at this step |
|
|
489
|
+
|
|
490
|
+
### Browser State
|
|
491
|
+
|
|
492
|
+
| Action | Fields | Description |
|
|
493
|
+
|--------|--------|-------------|
|
|
494
|
+
| `cookie:set` | `value: "name=value; domain=..."` | Set a cookie |
|
|
495
|
+
| `cookie:clear` | — | Clear all cookies |
|
|
496
|
+
| `storage:set` | `selector: "key"`, `value: "val"` | Set localStorage item |
|
|
497
|
+
| `eval` | `value` | Execute JavaScript on the page |
|
|
498
|
+
| `iframe:enter` | `selector` | Enter an iframe context |
|
|
499
|
+
| `iframe:exit` | — | Exit iframe context, return to main frame |
|
|
500
|
+
|
|
501
|
+
### API — HTTP Requests
|
|
502
|
+
|
|
503
|
+
| Action | Fields | Description |
|
|
504
|
+
|--------|--------|-------------|
|
|
505
|
+
| `http:request` | `method`, `url`, `headers?`, `body?`, `auth?`, `extract?` | Make an HTTP request. `auth` supports `{ type: "bearer", token: "{{var}}" }`. `extract` is a map of `variableName → $.jsonPath`. |
|
|
506
|
+
|
|
507
|
+
### API — Assertions
|
|
508
|
+
|
|
509
|
+
| Action | Fields | Description |
|
|
510
|
+
|--------|--------|-------------|
|
|
511
|
+
| `assert:response` | `assert: "status"`, `expected` | Assert HTTP status code |
|
|
512
|
+
| `assert:response` | `assert: "json:path"`, `path`, `expected` | Assert JSONPath value equals expected |
|
|
513
|
+
| `assert:response` | `assert: "json:exists"`, `path` | Assert JSONPath exists in response |
|
|
514
|
+
| `assert:response` | `assert: "header"`, `header`, `expected` | Assert response header value |
|
|
515
|
+
| `assert:response` | `assert: "body:contains"`, `expected` | Assert raw body contains string |
|
|
516
|
+
| `assert:response` | `assert: "time"`, `expected` | Assert response time < expected ms |
|
|
517
|
+
|
|
518
|
+
### API — Variables & Flow Control
|
|
519
|
+
|
|
520
|
+
| Action | Fields | Description |
|
|
521
|
+
|--------|--------|-------------|
|
|
522
|
+
| `set:variable` | `variable`, `value` | Set a named variable (supports `{{interpolation}}`) |
|
|
523
|
+
| `extract:json` | `variable`, `path` | Extract a value from the last response body via JSONPath |
|
|
524
|
+
| `env:switch` | `value` | Switch active environment mid-flow |
|
|
525
|
+
|
|
526
|
+
### Variables
|
|
527
|
+
|
|
528
|
+
Use `{{variableName}}` in any `value`, `url`, `selector`, or `body` field to inject variables:
|
|
529
|
+
|
|
530
|
+
```json
|
|
531
|
+
{ "action": "fill", "selector": "#email", "value": "{{userEmail}}" }
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Pass at runtime: `ghostrun run <id> --var userEmail=user@example.com`
|
|
535
|
+
|
|
536
|
+
Extracted values (from `extract:` steps) are automatically available as variables in subsequent steps.
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Unsupported / Limited Interactions
|
|
541
|
+
|
|
542
|
+
The following browser patterns have limited or no support today:
|
|
543
|
+
|
|
544
|
+
| Interaction | Status | Notes |
|
|
545
|
+
|------------|--------|-------|
|
|
546
|
+
| Canvas drawing | ❌ Not supported | `<canvas>` elements — no visual capture |
|
|
547
|
+
| WebGL / Three.js | ❌ Not supported | GPU-rendered content |
|
|
548
|
+
| Browser native dialogs | ⚠️ Partial | `alert()`/`confirm()`/`prompt()` auto-dismissed |
|
|
549
|
+
| File download verification | ⚠️ Partial | Download triggers but content is not validated |
|
|
550
|
+
| WebRTC / media streams | ❌ Not supported | Camera, mic, screen capture APIs |
|
|
551
|
+
| Browser extensions | ❌ Not supported | Extension UI is not accessible via Playwright |
|
|
552
|
+
| Shadow DOM (closed mode) | ⚠️ Limited | Open shadow DOM works; closed mode requires `eval:` workaround |
|
|
553
|
+
| Multi-tab / popup flows | ⚠️ Partial | New tabs opened by click are not automatically followed |
|
|
554
|
+
| OS-level dialogs | ❌ Not supported | Native file picker, print dialog, OS auth prompts |
|
|
555
|
+
| CAPTCHAs | ❌ Not supported | By design — no circumvention |
|
|
556
|
+
| Biometric auth | ❌ Not supported | Touch ID, Face ID, WebAuthn |
|
|
557
|
+
| Browser gestures (pinch/zoom) | ❌ Not supported | Mobile multi-touch gestures |
|
|
558
|
+
| Hover-only menus (CSS `:hover`) | ✅ Works | Use `hover` action before clicking submenu items |
|
|
559
|
+
| Right-click context menus | ⚠️ Limited | Browser context menus not accessible; app-level menus often work |
|
|
560
|
+
| Drag and drop | ✅ Works | Use `drag` action with `selector` + `targetSelector` |
|
|
561
|
+
| Infinite scroll / lazy load | ✅ Works | Use `scroll:load` with repeat count |
|
|
562
|
+
|
|
563
|
+
**Workarounds for unsupported interactions:**
|
|
564
|
+
- Use `eval:` to run JavaScript directly: `{ "action": "eval", "value": "document.querySelector('#btn').click()" }`
|
|
565
|
+
- Use `wait:ms:` to pause before difficult timing-sensitive interactions
|
|
566
|
+
- For shadow DOM: `{ "action": "eval", "value": "document.querySelector('my-el').shadowRoot.querySelector('button').click()" }`
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Data Storage
|
|
571
|
+
|
|
572
|
+
All data is local in `~/.ghostrun/`:
|
|
573
|
+
|
|
574
|
+
```
|
|
575
|
+
~/.ghostrun/
|
|
576
|
+
├── data/ghostrun.db # SQLite: flows, runs, steps, schedules, extracted data,
|
|
577
|
+
│ # environments, api_responses, perf_runs
|
|
578
|
+
├── screenshots/ # PNG per step per run
|
|
579
|
+
├── baselines/ # Visual baseline reference screenshots
|
|
580
|
+
└── diffs/ # Screenshot diff images
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Privacy
|
|
586
|
+
|
|
587
|
+
PII is sanitized before storage — emails, phones, cards, JWTs, API keys, passwords are replaced with `[EMAIL]`, `[PHONE]`, etc. Nothing sensitive is sent to AI unless you explicitly call `run:analyze`.
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Build from Source
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
npm install
|
|
595
|
+
npm run build # compiles .ts → .js via esbuild
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Publishing to npm
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
# 1. Log in to npm
|
|
604
|
+
npm login
|
|
605
|
+
|
|
606
|
+
# 2. Make sure the build is fresh
|
|
607
|
+
npm run build
|
|
608
|
+
|
|
609
|
+
# 3. Dry-run to verify what gets published
|
|
610
|
+
npm publish --dry-run
|
|
611
|
+
|
|
612
|
+
# 4. Publish
|
|
613
|
+
npm publish --access public
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
After publishing, users can install with:
|
|
617
|
+
```bash
|
|
618
|
+
npm install -g ghostrun-cli
|
|
619
|
+
ghostrun init
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
## Built with
|
|
625
|
+
|
|
626
|
+
This project was built with the help of [Claude](https://claude.ai) and [Goose](https://goose-docs.ai).
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## License
|
|
631
|
+
|
|
632
|
+
MIT
|