ghostrun-cli 1.0.1 → 1.0.3

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.
Files changed (4) hide show
  1. package/README.md +252 -431
  2. package/REFERENCE.md +165 -0
  3. package/ghostrun.js +65 -28
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -5,9 +5,60 @@
5
5
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
6
6
  [![Downloads](https://img.shields.io/npm/dm/ghostrun-cli)](https://www.npmjs.com/package/ghostrun-cli)
7
7
 
8
- Record once. Replay as a ghost.
8
+ **Record once. Replay as a ghost.**
9
9
 
10
- Browser automation + API testing + load testing in one CLI record real browser flows, test REST APIs with assertions and variable extraction, run VU-based load tests, export to k6, detect failures with AI analysis, and chat with your test suite. Entirely local.
10
+ GhostRun is a local-first CLI for browser automation, API testing, and load testing — all in one tool. Record a real browser flow, replay it headlessly, test REST APIs with assertions, and run VU-based load tests. No cloud. No account. Runs entirely on your machine.
11
+
12
+ ![GhostRun Demo](https://raw.githubusercontent.com/TechBuiltBySharan/ghostrun/master/demo/out/ghostrun-demo.gif)
13
+
14
+ ---
15
+
16
+ ## Table of Contents
17
+
18
+ - [What is GhostRun?](#what-is-ghostrun)
19
+ - [Install](#install)
20
+ - [Quick Start](#quick-start)
21
+ - [The Three Modes](#the-three-modes)
22
+ - [Browser Automation](#1-browser-automation)
23
+ - [API Testing](#2-api-testing)
24
+ - [Load Testing](#3-load-testing)
25
+ - [Commands](#commands)
26
+ - [Setup](#setup)
27
+ - [Recording](#recording)
28
+ - [Running Flows](#running-flows)
29
+ - [Flow Management](#flow-management)
30
+ - [Environments](#environments)
31
+ - [Run History](#run-history)
32
+ - [Scheduling](#scheduling)
33
+ - [Test Suites](#test-suites)
34
+ - [Web Dashboard](#web-dashboard)
35
+ - [Chat Assistant](#chat-assistant)
36
+ - [MCP Server](#mcp-server)
37
+ - [Reports](#reports)
38
+ - [Selector Repair](#selector-repair)
39
+ - [Screenshot Diff](#screenshot-diff)
40
+ - [CI/CD Integration](#cicd-integration)
41
+ - [AI Setup](#ai-setup)
42
+ - [Data & Privacy](#data--privacy)
43
+ - [Contributing](#contributing)
44
+ - [Trust & Transparency](#trust--transparency)
45
+ - [License](#license)
46
+
47
+ ---
48
+
49
+ ## What is GhostRun?
50
+
51
+ Most testing tools make you choose — browser OR API OR load testing. GhostRun does all three from a single CLI, using the same flow format.
52
+
53
+ | What you want to do | How GhostRun helps |
54
+ |---------------------|-------------------|
55
+ | Test a web UI regression | Record a browser flow once, replay headlessly on every deploy |
56
+ | Test a REST API | Write or import flows with HTTP requests, assertions, and variable extraction |
57
+ | Stress-test an endpoint | Run any API flow as a load test with configurable VUs and duration |
58
+ | Monitor a live site | Schedule flows to run on a cron and alert on failure |
59
+ | Give AI agents your test suite | MCP server exposes all flows as tools for Claude, Cursor, etc. |
60
+
61
+ Everything is stored locally in SQLite (`~/.ghostrun/`). No accounts, no telemetry, no cloud.
11
62
 
12
63
  ---
13
64
 
@@ -15,105 +66,116 @@ Browser automation + API testing + load testing in one CLI — record real brows
15
66
 
16
67
  ```bash
17
68
  npm install -g ghostrun-cli
69
+ ghostrun init # guided setup: installs Chromium, configures AI (optional)
18
70
  ```
19
71
 
20
72
  Or run from source:
21
73
 
22
74
  ```bash
23
75
  git clone https://github.com/TechBuiltBySharan/ghostrun
24
- cd ghostrun
25
- npm install
26
- npm run build
27
- node ghostrun.js init # guided setup
76
+ cd ghostrun && npm install && npm run build
77
+ node ghostrun.js init
28
78
  ```
29
79
 
80
+ **Requirements:** Node 18+, macOS/Linux/Windows
81
+
30
82
  ---
31
83
 
32
84
  ## Quick Start
33
85
 
86
+ **Record a browser flow:**
87
+
34
88
  ```bash
35
- ghostrun init # setup wizard: installs Chromium, configures AI
36
- ghostrun learn https://yourapp.com # record a browser flow
37
- ghostrun api:learn # import a .flow.json with API actions
38
- ghostrun run <flow-id> # replay headlessly (browser or API)
39
- ghostrun perf:run <flow-id> # load test an API flow (VU-based)
40
- ghostrun chat # AI chat: ask questions, run flows by name
41
- ghostrun serve --ui # web dashboard at http://localhost:3000
89
+ ghostrun learn https://yourapp.com # real browser opens, you interact, GhostRun records
90
+ ghostrun run "My Flow" # replay headlessly
91
+ ```
92
+
93
+ **Test an API:**
94
+
95
+ ```bash
96
+ ghostrun flow:from-curl "curl -X POST https://api.example.com/login \
97
+ -H 'Content-Type: application/json' \
98
+ -d '{\"email\":\"user@example.com\",\"password\":\"secret\"}'"
99
+ ghostrun run "POST /login"
100
+ ```
101
+
102
+ **Run a load test:**
103
+
104
+ ```bash
105
+ ghostrun perf:run "POST /login" --vus 20 --duration 30
42
106
  ```
43
107
 
44
108
  ---
45
109
 
46
- ## What Needs AI vs What Doesn't
47
-
48
- Every core feature works with zero AI. AI is an optional enhancement.
49
-
50
- | Feature | AI Required? | Notes |
51
- |---------|:------------:|-------|
52
- | Browser recording | No | Real click/input capture via Playwright |
53
- | Flow execution | No | Headless Playwright replay |
54
- | Screenshot capture | No | PNG per step, pass and fail |
55
- | Failure detection | No | Stops on error, shows what failed |
56
- | Selector repair (`flow:fix`) | No | Interactive browser-based fix |
57
- | Screenshot diff (`run:diff`) | No | Pixel comparison, no AI needed |
58
- | Flow scheduling | No | Cron-based, runs offline |
59
- | Monitor & diff extracted data | No | `ghostrun monitor <flow>` |
60
- | PII sanitization | No | Regex-based, local only |
61
- | Web dashboard | No | `ghostrun serve --ui` |
62
- | **API testing** | No | HTTP requests, assertions, variable extraction |
63
- | **Environment profiles** | No | Named env sets, injected at runtime |
64
- | **Load testing** | No | VU-based perf runs, p50/p95/p99 stats |
65
- | **k6 export** | No | `perf:export` generates a k6 script |
66
- | **Failure analysis** | Optional ✨ | Plain-English explanation of why it failed |
67
- | **Auto run summary** | Optional ✨ | Attached to every failed run automatically |
68
- | **Chat assistant** | Optional ✨ | Q&A + run flows by name via `ghostrun chat` |
69
-
70
- **Bottom line:** Record, replay, schedule, diff, and fix flows entirely offline. AI adds explanations and a conversational interface.
110
+ ## The Three Modes
111
+
112
+ ### 1. Browser Automation
113
+
114
+ GhostRun opens a real browser (Playwright/Chromium), watches your interactions, and saves them as a flow. Replay runs headlessly.
115
+
116
+ ```bash
117
+ ghostrun learn https://yourapp.com # record
118
+ ghostrun run <id|name> # replay headlessly
119
+ ghostrun run <id|name> --visible # replay with browser window visible
120
+ ```
121
+
122
+ **What gets recorded:** clicks, form fills, navigation, waits, checkboxes, dropdowns, keyboard input, file uploads, scroll, drag and drop.
123
+
124
+ **Selector repair:** If a flow breaks after a UI update, `ghostrun flow:fix <id>` opens the browser, replays up to the broken step, and lets you click the correct element. Selector is updated automatically — no manual JSON editing.
71
125
 
72
126
  ---
73
127
 
74
- ## AI Setup
128
+ ### 2. API Testing
75
129
 
76
- ### Option 1 Local (Default, Recommended)
130
+ When all steps in a flow are API actions (no `click`, `fill`, etc.), GhostRun skips Playwright entirely. Execution is ~30ms per run.
77
131
 
78
- GhostRun uses **Ollama** by default. No API key, no internet, runs on your machine.
132
+ **Three ways to create an API flow:**
79
133
 
80
134
  ```bash
81
- brew install ollama
82
- ollama serve &
83
- ollama pull gemma3:4b # 2.6 GB, fast on Apple Silicon
84
- node ghostrun.js status # → AI Provider: Ollama (gemma3:4b)
135
+ # From a curl command
136
+ ghostrun flow:from-curl "curl -X GET https://api.example.com/users \
137
+ -H 'Authorization: Bearer {{token}}'"
138
+
139
+ # From an OpenAPI/Swagger spec
140
+ ghostrun flow:from-spec openapi.json # JSON or YAML
141
+ ghostrun flow:from-spec swagger.yaml
142
+
143
+ # Import a hand-crafted .flow.json
144
+ ghostrun flow:import my-api-tests.flow.json
85
145
  ```
86
146
 
87
- **Model options by hardware:**
147
+ **API flow features:**
148
+ - HTTP requests with custom headers, JSON body, bearer auth
149
+ - Response assertions (status code, JSON path, headers, response time)
150
+ - Variable extraction from responses — use in subsequent steps
151
+ - Named environment profiles (dev / staging / prod)
88
152
 
89
- | Model | Size | Best for |
90
- |-------|------|---------|
91
- | `gemma3:4b` | 2.6 GB | Apple Silicon M1/M2/M3, fast |
92
- | `gemma2:9b` | 5.4 GB | Better quality, more RAM needed |
93
- | `llama3.2:3b` | 2.0 GB | Fastest, lighter quality |
153
+ ---
94
154
 
95
- Override model: `export GHOSTRUN_OLLAMA_MODEL=llama3.2:3b`
155
+ ### 3. Load Testing
96
156
 
97
- ### Option 2 Anthropic Cloud (Fallback)
157
+ Run any API flow as a load test. GhostRun sends parallel VU requests, collects timing, and prints a latency breakdown.
98
158
 
99
159
  ```bash
100
- export ANTHROPIC_API_KEY=sk-ant-...
160
+ ghostrun perf:run <id|name> # defaults: 10 VUs, 30s
161
+ ghostrun perf:run <id|name> --vus 50 --duration 60 --ramp-up 10
162
+ ghostrun perf:compare <run-A-id> <run-B-id> # diff two runs
163
+ ghostrun perf:export <id|name> # generate a k6 script
101
164
  ```
102
165
 
103
- ### Fallback chain
166
+ **Output includes:** HTTP requests, success rate, avg RPS, p50 / p95 / p99 latency, min/max, per-step breakdown, checks passed/failed.
167
+
168
+ **perf:compare** shows side-by-side deltas with color-coded improvement/regression:
104
169
 
105
170
  ```
106
- run → try Ollama → if down → try Anthropic → if no key → skip AI silently
171
+ Before After Delta
172
+ p50 latency 142ms 98ms ↓ 44ms ✓
173
+ p95 latency 310ms 201ms ↓ 109ms ✓
174
+ p99 latency 580ms 390ms ↓ 190ms ✓
175
+ avg RPS 47.2 68.1 ↑ 20.9 ✓
107
176
  ```
108
177
 
109
- ### Environment Variables
110
-
111
- | Variable | Default | Description |
112
- |----------|---------|-------------|
113
- | `GHOSTRUN_AI_PROVIDER` | auto | `ollama`, `anthropic`, or auto |
114
- | `GHOSTRUN_OLLAMA_URL` | `http://localhost:11434` | Ollama server URL |
115
- | `GHOSTRUN_OLLAMA_MODEL` | auto-detected | Model to use |
116
- | `ANTHROPIC_API_KEY` | — | Anthropic API key (cloud fallback) |
178
+ **perf:export** generates a valid k6 script with VU stages, `http.get`/`http.post` calls, `check()` assertions, and `Trend` metrics per step.
117
179
 
118
180
  ---
119
181
 
@@ -123,65 +185,87 @@ run → try Ollama → if down → try Anthropic → if no key → skip AI silen
123
185
 
124
186
  ```bash
125
187
  ghostrun init # guided setup wizard
126
- ghostrun status # stats + AI provider info
188
+ ghostrun status # stats, AI provider, data path
127
189
  ```
128
190
 
129
191
  ### Recording
130
192
 
131
193
  ```bash
132
- ghostrun learn <url> [name] # record a flow (real browser opens)
133
- ghostrun learn <url> --name "Login" # with explicit name
194
+ ghostrun learn <url> # open browser and record a flow
195
+ ghostrun learn <url> --name "Login" # with an explicit name
134
196
  ```
135
197
 
136
- ### Running
198
+ ### Running Flows
137
199
 
138
200
  ```bash
139
- ghostrun run <id|name> # headless execution
140
- ghostrun run <id|name> --visible # show the browser window
141
- ghostrun run <id|name> --output json # structured JSON output with extracted data
142
- ghostrun run <id|name> --var key=val # inject variables
143
- ghostrun run <id|name> --session-save mysession # save cookies/storage
144
- ghostrun run <id|name> --session-load mysession # restore cookies/storage
201
+ ghostrun run <id|name> # headless execution
202
+ ghostrun run <id|name> --visible # show the browser window
203
+ ghostrun run <id|name> --var key=val # inject a variable
204
+ ghostrun run <id|name> --output json # JSON output (for scripting/CI)
205
+ ghostrun run <id|name> --report html # save an HTML run report
206
+ ghostrun run <id|name> --session-save <name> # save browser cookies/storage
207
+ ghostrun run <id|name> --session-load <name> # restore browser cookies/storage
145
208
  ```
146
209
 
147
210
  ### Flow Management
148
211
 
149
212
  ```bash
150
- ghostrun flow:list # list all flows
151
- ghostrun flow:show <id|name> # show steps
152
- ghostrun flow:fix <id|name> # fix broken selectors interactively
153
- ghostrun flow:delete <id|name> # delete a flow
154
- ghostrun flow:export <id|name> # export to .flow.json
155
- ghostrun flow:import <file> # import from .flow.json
156
- ghostrun flow:clone <id|name> # duplicate a flow
157
- ghostrun flow:rename <id|name> <new> # rename a flow
213
+ ghostrun flow:list # list all flows
214
+ ghostrun flow:rename <id|name> <new-name> # rename a flow
215
+ ghostrun flow:clone <id|name> # duplicate a flow
216
+ ghostrun flow:delete <id|name> # delete a flow
217
+ ghostrun flow:export <id|name> # export to .flow.json
218
+ ghostrun flow:import <file> # import from .flow.json
219
+ ghostrun flow:fix <id|name> # fix broken selectors interactively
220
+ ghostrun flow:from-curl "<curl>" # create a flow from a curl command
221
+ ghostrun flow:from-spec <file> # create flows from an OpenAPI spec
158
222
  ```
159
223
 
160
- ### Data Extraction & Monitoring
224
+ ### Environments
225
+
226
+ Named variable sets injected at run time. Perfect for dev / staging / prod.
161
227
 
162
228
  ```bash
163
- ghostrun monitor <id|name> # run + show extracted data, diff vs previous
164
- ghostrun run:show <id> # step details + screenshots + extracted data
229
+ ghostrun env:create <name> # create an environment (dev/staging/prod)
230
+ ghostrun env:set <name> <key> <value> # add or update a variable
231
+ ghostrun env:list # list all environments
232
+ ghostrun env:show <name> # show variables in an environment
233
+ ghostrun env:use <name> # set as the active environment
165
234
  ```
166
235
 
236
+ Reference variables in any URL, header, or body field with `{{variableName}}`. The active environment's variables are injected automatically before each run.
237
+
167
238
  ### Run History
168
239
 
169
240
  ```bash
170
241
  ghostrun run:list # list recent runs
171
- ghostrun run:show <id> # step details + screenshots
172
- ghostrun run:diff <id1> <id2> # visual screenshot diff (no AI needed)
173
- ghostrun run:analyze <id> # AI failure analysis (optional)
242
+ ghostrun run:show <id> # step-by-step detail, screenshots, extracted data
243
+ ghostrun run:diff <id1> <id2> # pixel-level screenshot comparison
244
+ ghostrun run:analyze <id> # AI failure analysis (requires AI setup)
245
+ ghostrun var:dump <run-id> # show all variables extracted during a run
174
246
  ```
175
247
 
176
248
  ### Scheduling
177
249
 
178
250
  ```bash
179
- ghostrun flow:schedule <id> "<cron>" # e.g. "0 9 * * *" = daily 9am
251
+ ghostrun flow:schedule <id> "<cron>" # e.g. "0 9 * * *" = daily at 9am
180
252
  ghostrun schedule:list # list all schedules
181
253
  ghostrun schedule:remove <id> # remove a schedule
182
254
  ghostrun serve # start the scheduler daemon
183
255
  ```
184
256
 
257
+ ### Test Suites
258
+
259
+ Group flows and run them together:
260
+
261
+ ```bash
262
+ ghostrun suite:create <name> # create a suite
263
+ ghostrun suite:add <suite> <flow> # add a flow to the suite
264
+ ghostrun suite:run <suite> # run all flows in the suite
265
+ ghostrun suite:list # list suites
266
+ ghostrun suite:show <suite> # show flows in a suite
267
+ ```
268
+
185
269
  ### Web Dashboard
186
270
 
187
271
  ```bash
@@ -189,152 +273,91 @@ ghostrun serve --ui # launch dashboard at http://localhost:30
189
273
  ghostrun serve --ui --port 8080 # custom port
190
274
  ```
191
275
 
192
- The dashboard shows:
193
- - All flows with one-click run buttons
194
- - Live run log with SSE streaming
195
- - Run history with status and duration
196
- - Chat tab for natural-language interaction
276
+ The dashboard shows all flows with one-click run, a live log stream, run history, and a chat tab.
197
277
 
198
278
  ### Chat Assistant
199
279
 
280
+ Ask questions about your flows in plain English:
281
+
200
282
  ```bash
201
- ghostrun chat # interactive AI chat (requires Ollama or ANTHROPIC_API_KEY)
283
+ ghostrun chat
202
284
  ```
203
285
 
204
- Ask questions in plain English:
286
+ Examples:
205
287
  - `did my login flow pass recently?`
206
288
  - `what flows do I have?`
207
- - `run the login flow` ← executes the flow with confirmation
289
+ - `run the login flow` ← executes with confirmation
208
290
 
209
- ### Test Suites
291
+ Requires Ollama (local, free) or an Anthropic API key. See [AI Setup](#ai-setup).
210
292
 
211
- ```bash
212
- ghostrun suite:create <name> # create a suite
213
- ghostrun suite:add <suite> <flow> # add a flow to a suite
214
- ghostrun suite:list # list suites
215
- ghostrun suite:show <suite> # show flows in suite
216
- ghostrun suite:run <suite> # run all flows in suite
217
- ```
293
+ ### MCP Server
218
294
 
219
- ### Visual Baselines
295
+ Expose GhostRun to AI agents (Claude Desktop, Cursor, etc.):
220
296
 
221
297
  ```bash
222
- ghostrun baseline:set <flow-id> # capture reference screenshots
223
- ghostrun baseline:clear <flow-id> # clear baselines
224
- ghostrun baseline:show <flow-id> # list baselines
298
+ node mcp-server.js
225
299
  ```
226
300
 
227
- ### Importing Flows
301
+ Tools exposed: `list_flows`, `get_flow`, `run_flow`, `get_run_result`, `list_runs`, `delete_flow`, `get_status`.
228
302
 
229
- ```bash
230
- ghostrun flow:from-curl # paste a curl command → instant flow
231
- ghostrun flow:from-curl "curl -X POST https://api.example.com/users -H 'Content-Type: application/json' -d '{\"name\":\"Alice\"}'"
232
- ghostrun flow:from-spec openapi.json # import all endpoints from an OpenAPI spec
233
- ghostrun flow:from-spec swagger.yaml # YAML supported too
234
- ghostrun flow:import <file> # import a .flow.json directly
235
- ```
303
+ See [MCP-SETUP.md](MCP-SETUP.md) for connection setup.
236
304
 
237
- `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.
305
+ ---
238
306
 
239
- ### Run Reports
307
+ ## Reports
240
308
 
241
309
  ```bash
242
- ghostrun run <id> --report html # run + save HTML report
243
- ghostrun perf:run <id> --report html # load test + save HTML report
310
+ ghostrun run <id> --report html # browser or API run report
311
+ ghostrun perf:run <id> --report html # load test report
244
312
  ```
245
313
 
246
- Reports are dark-themed HTML files saved to the current directory shareable, self-contained, screenshot-inclusive.
247
-
248
- ### API Testing
249
-
250
- Import an API flow from a `.flow.json` file (no browser needed):
251
-
252
- ```bash
253
- ghostrun api:learn # interactive: pick a .flow.json file to import
254
- ghostrun flow:import <file> # import directly by path
255
- ghostrun run <id|name> # runs pure API flows without launching a browser
256
- ```
314
+ Dark-themed, self-contained HTML files saved to the current directory. Include per-step timing, status, screenshots (for browser flows), and extracted data. Shareable without any external dependencies.
257
315
 
258
- 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.
316
+ ---
259
317
 
260
- ### Environments
318
+ ## Selector Repair
261
319
 
262
- Named variable sets injected at flow start. Great for dev / staging / prod:
320
+ When a UI update breaks a selector:
263
321
 
264
322
  ```bash
265
- ghostrun env:create <name> [base-url] # create an environment profile
266
- ghostrun env:list # list all environments
267
- ghostrun env:show <name> # show variables in an environment
268
- ghostrun env:set <name> <key=value> # add or update a variable
269
- ghostrun env:use <name> # set as active environment
270
- ghostrun env:delete <name> # delete an environment
323
+ ghostrun flow:fix <id|name>
271
324
  ```
272
325
 
273
- The active environment's variables are automatically injected before each flow run. Use `{{variableName}}` in any URL, header, or body field to reference them.
326
+ The browser opens, replays all passing steps automatically, then **pauses on the broken step** and asks you to click the correct element. The selector is updated and saved. No JSON editing needed.
274
327
 
275
- ### Variable Inspection
328
+ ---
276
329
 
277
- ```bash
278
- ghostrun var:dump <run-id> # show all variables extracted during a run
279
- ```
330
+ ## Screenshot Diff
280
331
 
281
- ### Load & Performance Testing
332
+ Compare any two runs pixel-by-pixel — no AI needed:
282
333
 
283
334
  ```bash
284
- ghostrun perf:run <id|name> # run a load test against an API flow
285
- ghostrun perf:run <id|name> --vus 20 --duration 30 --ramp-up 5
286
- ghostrun perf:export <id|name> # generate a k6 script from the flow
287
- ghostrun perf:export <id|name> --output mytest.js
288
- ghostrun perf:list # list past perf runs
289
- ghostrun perf:show <perf-run-id> # show stats for a specific perf run
335
+ ghostrun run:diff <run1-id> <run2-id>
290
336
  ```
291
337
 
292
- **perf:run options:**
293
-
294
- | Flag | Default | Description |
295
- |------|---------|-------------|
296
- | `--vus <n>` | 10 | Number of virtual users |
297
- | `--duration <s>` | 30 | Test duration in seconds |
298
- | `--ramp-up <s>` | 5 | Ramp-up time (VUs staggered over this window) |
299
- | `--timeout <ms>` | 10000 | Per-request timeout in ms |
300
-
301
- Output includes: HTTP Requests, HTTP Success Rate, Avg RPS, p50/p95/p99 latency, min/max, per-step breakdown, and separate Checks Passed/Failed count.
302
-
303
- **perf:compare** — diff two runs side by side to see if a deploy made things faster or slower:
304
-
305
- ```bash
306
- ghostrun perf:compare <run-A-id> <run-B-id>
307
338
  ```
339
+ Step Status Diff % Name
340
+ ───────────────────────────────────────────
341
+ 1 same 0.0% Navigate to homepage
342
+ 2 same 0.1% Click Login
343
+ 3 changed 12.4% Fill email field
344
+ 4 same 0.0% Submit form
308
345
 
309
- Shows p50/p95/p99/RPS for both runs with color-coded deltas (green = better, red = worse).
310
-
311
- **perf:export** generates a valid k6 JavaScript file with:
312
- - VU stages matching your `--vus`/`--duration`/`--ramp-up` config
313
- - `http.get`/`http.post` calls with headers and JSON body
314
- - `check()` assertions mapped to your `assert:response` steps
315
- - `Trend` metrics per step for p95 thresholds
316
- - `{{variable}}` → template literal interpolation
317
-
318
- ### MCP Server
319
-
320
- ```bash
321
- node mcp-server.js # start MCP server (Claude Desktop, Cursor, etc.)
346
+ 3 same 1 changed
347
+ Diff images: ~/.ghostrun/diffs/abc123_vs_def456/
322
348
  ```
323
349
 
324
- See [MCP-SETUP.md](MCP-SETUP.md) for connection setup.
325
-
326
- Tools exposed: `list_flows`, `get_flow`, `run_flow`, `get_run_result`, `list_runs`, `delete_flow`, `get_status`
327
-
328
350
  ---
329
351
 
330
352
  ## CI/CD Integration
331
353
 
332
- ### GitHub Actions
354
+ GhostRun exits with code `1` on failure and `0` on success — standard CI behaviour, no extra flags needed.
355
+
356
+ ### GitHub Actions example
333
357
 
334
358
  ```yaml
335
359
  # .github/workflows/ghostrun.yml
336
360
  name: GhostRun Tests
337
-
338
361
  on: [push, pull_request]
339
362
 
340
363
  jobs:
@@ -342,7 +365,6 @@ jobs:
342
365
  runs-on: ubuntu-latest
343
366
  steps:
344
367
  - uses: actions/checkout@v4
345
-
346
368
  - uses: actions/setup-node@v4
347
369
  with:
348
370
  node-version: 20
@@ -352,16 +374,12 @@ jobs:
352
374
 
353
375
  - name: Install Playwright browsers
354
376
  run: npx playwright install chromium --with-deps
377
+ # Skip this step for pure API flows — no browser needed
355
378
 
356
- - name: Import test flows
357
- run: |
358
- ghostrun flow:import test-flows/health-check.flow.json
359
- ghostrun flow:import test-flows/auth-and-users.flow.json
360
-
361
- - name: Run flows
379
+ - name: Import and run flows
362
380
  run: |
363
- ghostrun run "API Health Check" --report html
364
- ghostrun run "Auth + User List" --report html
381
+ ghostrun flow:import test-flows/auth.flow.json
382
+ ghostrun run "Auth Flow" --report html
365
383
 
366
384
  - name: Upload reports
367
385
  uses: actions/upload-artifact@v4
@@ -371,279 +389,82 @@ jobs:
371
389
  path: ghostrun-report-*.html
372
390
  ```
373
391
 
374
- ### Exit Codes
375
-
376
- GhostRun exits with code `1` on failure and `0` on pass — standard CI behaviour, no extra flags needed.
377
-
378
- ### API-only flows in CI
379
-
380
- API flows skip Playwright entirely — no `playwright install` step needed:
381
-
382
- ```yaml
383
- - name: Install GhostRun (API testing only)
384
- run: npm install -g ghostrun-cli
385
- # No playwright install needed for pure API flows
392
+ ---
386
393
 
387
- - name: Run API tests
388
- run: ghostrun run "Auth + User List"
389
- ```
394
+ ## AI Setup
390
395
 
391
- ---
396
+ Every core feature works with zero AI. AI adds failure explanations and a chat interface — both are optional.
392
397
 
393
- ## Selector Repair (`flow:fix`)
398
+ ### Option 1 — Ollama (local, recommended)
394
399
 
395
- When a flow fails because a selector broke:
400
+ No API key, no internet required. Runs on your machine.
396
401
 
397
402
  ```bash
398
- ghostrun flow:fix <id|name>
403
+ brew install ollama
404
+ ollama serve &
405
+ ollama pull gemma3:4b # 2.6 GB, fast on Apple Silicon
399
406
  ```
400
407
 
401
- 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.
402
-
403
- ---
408
+ | Model | Size | Best for |
409
+ |-------|------|---------|
410
+ | `gemma3:4b` | 2.6 GB | Apple Silicon M1/M2/M3 |
411
+ | `gemma2:9b` | 5.4 GB | Better quality |
412
+ | `llama3.2:3b` | 2.0 GB | Fastest, lighter quality |
404
413
 
405
- ## Screenshot Diff (`run:diff`)
414
+ Override: `export GHOSTRUN_OLLAMA_MODEL=llama3.2:3b`
406
415
 
407
- Compare any two runs pixel-by-pixel no AI needed:
416
+ ### Option 2Anthropic (cloud fallback)
408
417
 
409
418
  ```bash
410
- ghostrun run:diff <run1-id> <run2-id>
411
-
412
- Step Status Diff % Screenshot
413
- ──────────────────────────────────────────────────────────
414
- 1 same 0.0% Navigate to homepage
415
- 2 same 0.1% Click Login
416
- 3 changed 12.4% Fill email field
417
- 4 same 0.0% Submit form
418
-
419
- 3 same 1 changed
420
- Diff images: ~/.ghostrun/diffs/abc123_vs_def456/
419
+ export ANTHROPIC_API_KEY=sk-ant-...
421
420
  ```
422
421
 
423
- ---
424
-
425
- ## Flow Actions Reference
426
-
427
- All actions you can use in recorded or imported `.flow.json` files:
428
-
429
- ### Navigation
430
-
431
- | Action | Fields | Description |
432
- |--------|--------|-------------|
433
- | `navigate` | `url` | Go to URL |
434
- | `reload` | — | Reload the current page |
435
- | `back` | — | Browser back |
436
- | `forward` | — | Browser forward |
437
-
438
- ### Interaction
439
-
440
- | Action | Fields | Description |
441
- |--------|--------|-------------|
442
- | `click` | `selector` | Left-click an element |
443
- | `dblclick` | `selector` | Double-click an element |
444
- | `fill` | `selector`, `value` | Clear field and type value |
445
- | `type` | `selector`, `value`, `delay?` | Type with configurable key delay (ms) |
446
- | `clear` | `selector` | Clear a field |
447
- | `select` | `selector`, `value` | Select a dropdown option by value |
448
- | `check` | `selector`, `value: "true"\|"false"` | Check/uncheck a checkbox |
449
- | `focus` | `selector` | Focus an element |
450
- | `hover` | `selector` | Mouse hover |
451
- | `drag` | `selector`, `targetSelector` | Drag one element to another |
452
- | `keyboard` | `key`, `selector?` | Press a key (e.g. `Enter`, `Tab`, `Control+a`) |
453
- | `upload` | `selector`, `value` | Set file input (comma-separated paths) |
454
-
455
- ### Waiting
456
-
457
- | Action | Fields | Description |
458
- |--------|--------|-------------|
459
- | `wait` | `selector` | Wait for element to appear |
460
- | `wait:text` | `selector`, `value` | Wait until element contains text |
461
- | `wait:url` | `value` | Wait for URL to match pattern |
462
- | `wait:ms` | `value` | Wait for N milliseconds |
463
-
464
- ### Scrolling
422
+ ### Fallback order
465
423
 
466
- | Action | Fields | Description |
467
- |--------|--------|-------------|
468
- | `scroll` | `selector?` | Scroll to element (or page) |
469
- | `scroll:element` | `selector` | Scroll element into view |
470
- | `scroll:bottom` | — | Scroll to bottom of page |
471
- | `scroll:load` | `value?` | Scroll to bottom, wait for load (repeat N times) |
472
- | `next:page` | `selector?` | Click next page link and wait |
473
-
474
- ### Assertions
475
-
476
- | Action | Fields | Description |
477
- |--------|--------|-------------|
478
- | `assert:visible` | `selector` | Assert element is visible |
479
- | `assert:hidden` | `selector` | Assert element is not visible |
480
- | `assert:text` | `selector`, `value` | Assert element contains text |
481
- | `assert:not-text` | `selector`, `value` | Assert element does NOT contain text |
482
- | `assert:value` | `selector`, `value` | Assert input value |
483
- | `assert:count` | `selector`, `value` | Assert number of matching elements |
484
- | `assert:attr` | `selector`, `value: "attr=expected"` | Assert element attribute |
485
-
486
- ### Data Extraction
487
-
488
- | Action | Fields | Description |
489
- |--------|--------|-------------|
490
- | `extract` | `selector`, `value: "variableName"` | Extract text → variable |
491
- | `screenshot` | — | Capture screenshot at this step |
492
-
493
- ### Browser State
494
-
495
- | Action | Fields | Description |
496
- |--------|--------|-------------|
497
- | `cookie:set` | `value: "name=value; domain=..."` | Set a cookie |
498
- | `cookie:clear` | — | Clear all cookies |
499
- | `storage:set` | `selector: "key"`, `value: "val"` | Set localStorage item |
500
- | `eval` | `value` | Execute JavaScript on the page |
501
- | `iframe:enter` | `selector` | Enter an iframe context |
502
- | `iframe:exit` | — | Exit iframe context, return to main frame |
503
-
504
- ### API — HTTP Requests
505
-
506
- | Action | Fields | Description |
507
- |--------|--------|-------------|
508
- | `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`. |
509
-
510
- ### API — Assertions
511
-
512
- | Action | Fields | Description |
513
- |--------|--------|-------------|
514
- | `assert:response` | `assert: "status"`, `expected` | Assert HTTP status code |
515
- | `assert:response` | `assert: "json:path"`, `path`, `expected` | Assert JSONPath value equals expected |
516
- | `assert:response` | `assert: "json:exists"`, `path` | Assert JSONPath exists in response |
517
- | `assert:response` | `assert: "header"`, `header`, `expected` | Assert response header value |
518
- | `assert:response` | `assert: "body:contains"`, `expected` | Assert raw body contains string |
519
- | `assert:response` | `assert: "time"`, `expected` | Assert response time < expected ms |
520
-
521
- ### API — Variables & Flow Control
522
-
523
- | Action | Fields | Description |
524
- |--------|--------|-------------|
525
- | `set:variable` | `variable`, `value` | Set a named variable (supports `{{interpolation}}`) |
526
- | `extract:json` | `variable`, `path` | Extract a value from the last response body via JSONPath |
527
- | `env:switch` | `value` | Switch active environment mid-flow |
528
-
529
- ### Variables
530
-
531
- Use `{{variableName}}` in any `value`, `url`, `selector`, or `body` field to inject variables:
532
-
533
- ```json
534
- { "action": "fill", "selector": "#email", "value": "{{userEmail}}" }
535
- ```
536
-
537
- Pass at runtime: `ghostrun run <id> --var userEmail=user@example.com`
538
-
539
- Extracted values (from `extract:` steps) are automatically available as variables in subsequent steps.
424
+ ```
425
+ Run → try Ollama → if down → try Anthropic → if no key → skip AI silently
426
+ ```
540
427
 
541
- ---
428
+ ### Environment variables
542
429
 
543
- ## Unsupported / Limited Interactions
544
-
545
- The following browser patterns have limited or no support today:
546
-
547
- | Interaction | Status | Notes |
548
- |------------|--------|-------|
549
- | Canvas drawing | ❌ Not supported | `<canvas>` elements — no visual capture |
550
- | WebGL / Three.js | ❌ Not supported | GPU-rendered content |
551
- | Browser native dialogs | ⚠️ Partial | `alert()`/`confirm()`/`prompt()` auto-dismissed |
552
- | File download verification | ⚠️ Partial | Download triggers but content is not validated |
553
- | WebRTC / media streams | ❌ Not supported | Camera, mic, screen capture APIs |
554
- | Browser extensions | ❌ Not supported | Extension UI is not accessible via Playwright |
555
- | Shadow DOM (closed mode) | ⚠️ Limited | Open shadow DOM works; closed mode requires `eval:` workaround |
556
- | Multi-tab / popup flows | ⚠️ Partial | New tabs opened by click are not automatically followed |
557
- | OS-level dialogs | ❌ Not supported | Native file picker, print dialog, OS auth prompts |
558
- | CAPTCHAs | ❌ Not supported | By design — no circumvention |
559
- | Biometric auth | ❌ Not supported | Touch ID, Face ID, WebAuthn |
560
- | Browser gestures (pinch/zoom) | ❌ Not supported | Mobile multi-touch gestures |
561
- | Hover-only menus (CSS `:hover`) | ✅ Works | Use `hover` action before clicking submenu items |
562
- | Right-click context menus | ⚠️ Limited | Browser context menus not accessible; app-level menus often work |
563
- | Drag and drop | ✅ Works | Use `drag` action with `selector` + `targetSelector` |
564
- | Infinite scroll / lazy load | ✅ Works | Use `scroll:load` with repeat count |
565
-
566
- **Workarounds for unsupported interactions:**
567
- - Use `eval:` to run JavaScript directly: `{ "action": "eval", "value": "document.querySelector('#btn').click()" }`
568
- - Use `wait:ms:` to pause before difficult timing-sensitive interactions
569
- - For shadow DOM: `{ "action": "eval", "value": "document.querySelector('my-el').shadowRoot.querySelector('button').click()" }`
430
+ | Variable | Default | Description |
431
+ |----------|---------|-------------|
432
+ | `GHOSTRUN_AI_PROVIDER` | `auto` | `ollama`, `anthropic`, or `auto` |
433
+ | `GHOSTRUN_OLLAMA_URL` | `http://localhost:11434` | Ollama server URL |
434
+ | `GHOSTRUN_OLLAMA_MODEL` | auto-detected | Model to use with Ollama |
435
+ | `ANTHROPIC_API_KEY` | — | Anthropic API key (cloud fallback) |
570
436
 
571
437
  ---
572
438
 
573
- ## Data Storage
439
+ ## Data & Privacy
574
440
 
575
- All data is local in `~/.ghostrun/`:
441
+ All data lives locally in `~/.ghostrun/`:
576
442
 
577
443
  ```
578
444
  ~/.ghostrun/
579
- ├── data/ghostrun.db # SQLite: flows, runs, steps, schedules, extracted data,
580
- │ # environments, api_responses, perf_runs
445
+ ├── data/ghostrun.db # SQLite: flows, runs, steps, schedules, environments
581
446
  ├── screenshots/ # PNG per step per run
582
447
  ├── baselines/ # Visual baseline reference screenshots
583
448
  └── diffs/ # Screenshot diff images
584
449
  ```
585
450
 
586
- ---
587
-
588
- ## Privacy
589
-
590
- 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`.
591
-
592
- ---
593
-
594
- ## Build from Source
595
-
596
- ```bash
597
- npm install
598
- npm run build # compiles .ts → .js via esbuild
599
- ```
600
-
601
- ---
602
-
603
- ## Publishing to npm
604
-
605
- ```bash
606
- # 1. Log in to npm
607
- npm login
608
-
609
- # 2. Make sure the build is fresh
610
- npm run build
611
-
612
- # 3. Dry-run to verify what gets published
613
- npm publish --dry-run
614
-
615
- # 4. Publish
616
- npm publish --access public
617
- ```
618
-
619
- After publishing, users can install with:
620
- ```bash
621
- npm install -g ghostrun-cli
622
- ghostrun init
623
- ```
624
-
625
- ---
626
-
627
- ## Trust & Transparency
628
-
629
- - **100% local by default** — No cloud, no telemetry, no tracking. Everything runs on your machine.
630
- - **Open source (MIT)** — Full source code at [github.com/TechBuiltBySharan/ghostrun](https://github.com/TechBuiltBySharan/ghostrun)
631
- - **No surprise costs** — AI works offline with [Ollama](https://ollama.com) (free). Anthropic API key is optional.
632
- - **PII sanitization built-in** — Emails, passwords, tokens, and API keys are redacted before being stored in the local SQLite database.
633
- - **No vendor lock-in** — Flows are plain JSON files you own. Export, import, version-control them like code.
634
- - **I use this for** — Regression testing my own web apps, monitoring live API endpoints, and running load tests before deploys.
451
+ **PII sanitization:** Emails, phone numbers, credit cards, JWTs, API keys, and passwords are redacted before storage. Nothing sensitive is sent to AI unless you explicitly call `run:analyze`.
635
452
 
636
453
  ---
637
454
 
638
455
  ## Contributing
639
456
 
640
- See [CONTRIBUTING.md](CONTRIBUTING.md) for how to get started.
457
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for how to get started, and [REFERENCE.md](REFERENCE.md) for the full flow actions reference.
641
458
 
642
459
  ---
643
460
 
644
- ## Built with
461
+ ## Trust & Transparency
645
462
 
646
- This project was built with the help of [Claude](https://claude.ai) and [Goose](https://goose-docs.ai).
463
+ - **100% local by default** no cloud, no telemetry, no tracking
464
+ - **Open source (MIT)** — full source at [github.com/TechBuiltBySharan/ghostrun](https://github.com/TechBuiltBySharan/ghostrun)
465
+ - **No surprise costs** — AI works offline via [Ollama](https://ollama.com) (free); Anthropic key is optional
466
+ - **No vendor lock-in** — flows are plain JSON files you own; export, import, version-control them like code
467
+ - Built with the help of [Claude](https://claude.ai) and [Goose](https://goose-docs.ai)
647
468
 
648
469
  ---
649
470
 
package/REFERENCE.md ADDED
@@ -0,0 +1,165 @@
1
+ # GhostRun — Flow Actions Reference
2
+
3
+ Complete reference for all actions you can use in recorded or hand-crafted `.flow.json` files.
4
+
5
+ ---
6
+
7
+ ## Browser Actions
8
+
9
+ ### Navigation
10
+
11
+ | Action | Fields | Description |
12
+ |--------|--------|-------------|
13
+ | `navigate` | `url` | Go to URL |
14
+ | `reload` | — | Reload the current page |
15
+ | `back` | — | Browser back |
16
+ | `forward` | — | Browser forward |
17
+
18
+ ### Interaction
19
+
20
+ | Action | Fields | Description |
21
+ |--------|--------|-------------|
22
+ | `click` | `selector` | Left-click an element |
23
+ | `dblclick` | `selector` | Double-click an element |
24
+ | `fill` | `selector`, `value` | Clear field and type value |
25
+ | `type` | `selector`, `value`, `delay?` | Type with configurable key delay (ms) |
26
+ | `clear` | `selector` | Clear a field |
27
+ | `select` | `selector`, `value` | Select a dropdown option by value |
28
+ | `check` | `selector`, `value: "true"\|"false"` | Check/uncheck a checkbox |
29
+ | `focus` | `selector` | Focus an element |
30
+ | `hover` | `selector` | Mouse hover |
31
+ | `drag` | `selector`, `targetSelector` | Drag one element to another |
32
+ | `keyboard` | `key`, `selector?` | Press a key (e.g. `Enter`, `Tab`, `Control+a`) |
33
+ | `upload` | `selector`, `value` | Set file input (comma-separated paths) |
34
+
35
+ ### Waiting
36
+
37
+ | Action | Fields | Description |
38
+ |--------|--------|-------------|
39
+ | `wait` | `selector` | Wait for element to appear |
40
+ | `wait:text` | `selector`, `value` | Wait until element contains text |
41
+ | `wait:url` | `value` | Wait for URL to match pattern |
42
+ | `wait:ms` | `value` | Wait for N milliseconds |
43
+
44
+ ### Scrolling
45
+
46
+ | Action | Fields | Description |
47
+ |--------|--------|-------------|
48
+ | `scroll` | `selector?` | Scroll to element (or page) |
49
+ | `scroll:element` | `selector` | Scroll element into view |
50
+ | `scroll:bottom` | — | Scroll to bottom of page |
51
+ | `scroll:load` | `value?` | Scroll to bottom, wait for load (repeat N times) |
52
+ | `next:page` | `selector?` | Click next page link and wait |
53
+
54
+ ### Assertions
55
+
56
+ | Action | Fields | Description |
57
+ |--------|--------|-------------|
58
+ | `assert:visible` | `selector` | Assert element is visible |
59
+ | `assert:hidden` | `selector` | Assert element is not visible |
60
+ | `assert:text` | `selector`, `value` | Assert element contains text |
61
+ | `assert:not-text` | `selector`, `value` | Assert element does NOT contain text |
62
+ | `assert:value` | `selector`, `value` | Assert input value |
63
+ | `assert:count` | `selector`, `value` | Assert number of matching elements |
64
+ | `assert:attr` | `selector`, `value: "attr=expected"` | Assert element attribute |
65
+
66
+ ### Data Extraction
67
+
68
+ | Action | Fields | Description |
69
+ |--------|--------|-------------|
70
+ | `extract` | `selector`, `variable: "variableName"` | Extract element text → variable |
71
+ | `screenshot` | — | Capture screenshot at this step |
72
+
73
+ ### Browser State
74
+
75
+ | Action | Fields | Description |
76
+ |--------|--------|-------------|
77
+ | `cookie:set` | `value: "name=value; domain=..."` | Set a cookie |
78
+ | `cookie:clear` | — | Clear all cookies |
79
+ | `storage:set` | `selector: "key"`, `value: "val"` | Set localStorage item |
80
+ | `eval` | `value` | Execute JavaScript on the page |
81
+ | `iframe:enter` | `selector` | Enter an iframe context |
82
+ | `iframe:exit` | — | Exit iframe context, return to main frame |
83
+
84
+ ---
85
+
86
+ ## API Actions
87
+
88
+ ### HTTP Requests
89
+
90
+ | Action | Fields | Description |
91
+ |--------|--------|-------------|
92
+ | `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`. |
93
+
94
+ ### Assertions
95
+
96
+ | Action | Fields | Description |
97
+ |--------|--------|-------------|
98
+ | `assert:response` | `assert: "status"`, `expected` | Assert HTTP status code |
99
+ | `assert:response` | `assert: "json:path"`, `path`, `expected` | Assert JSONPath value equals expected |
100
+ | `assert:response` | `assert: "json:exists"`, `path` | Assert JSONPath exists in response |
101
+ | `assert:response` | `assert: "header"`, `header`, `expected` | Assert response header value |
102
+ | `assert:response` | `assert: "body:contains"`, `expected` | Assert raw body contains string |
103
+ | `assert:response` | `assert: "time"`, `expected` | Assert response time < expected ms |
104
+
105
+ ### Variables & Flow Control
106
+
107
+ | Action | Fields | Description |
108
+ |--------|--------|-------------|
109
+ | `set:variable` | `variable`, `value` | Set a named variable (supports `{{interpolation}}`) |
110
+ | `extract:json` | `variable`, `path` | Extract a value from the last response body via JSONPath |
111
+ | `env:switch` | `value` | Switch active environment mid-flow |
112
+
113
+ ---
114
+
115
+ ## Variables
116
+
117
+ Use `{{variableName}}` in any `value`, `url`, `selector`, or `body` field:
118
+
119
+ ```json
120
+ { "action": "fill", "selector": "#email", "value": "{{userEmail}}" }
121
+ ```
122
+
123
+ Pass at runtime:
124
+
125
+ ```bash
126
+ ghostrun run <id> --var userEmail=user@example.com
127
+ ```
128
+
129
+ Values extracted with `extract:` and `extract:json` are automatically available as variables in subsequent steps.
130
+
131
+ ---
132
+
133
+ ## Limitations
134
+
135
+ | Interaction | Status | Notes |
136
+ |------------|--------|-------|
137
+ | Canvas drawing | ❌ | `<canvas>` elements — no visual capture |
138
+ | WebGL / Three.js | ❌ | GPU-rendered content |
139
+ | Browser native dialogs | ⚠️ Partial | `alert()`/`confirm()`/`prompt()` auto-dismissed |
140
+ | File download verification | ⚠️ Partial | Download triggers but content is not validated |
141
+ | WebRTC / media streams | ❌ | Camera, mic, screen capture APIs |
142
+ | Browser extensions | ❌ | Extension UI not accessible via Playwright |
143
+ | Shadow DOM (closed mode) | ⚠️ Limited | Open shadow DOM works; closed mode needs `eval` workaround |
144
+ | Multi-tab / popup flows | ⚠️ Partial | New tabs opened by click are not automatically followed |
145
+ | OS-level dialogs | ❌ | Native file picker, print dialog, OS auth prompts |
146
+ | CAPTCHAs | ❌ | By design — no circumvention |
147
+ | Biometric auth | ❌ | Touch ID, Face ID, WebAuthn |
148
+ | Browser gestures (pinch/zoom) | ❌ | Mobile multi-touch gestures |
149
+ | Hover-only menus (CSS `:hover`) | ✅ | Use `hover` action before clicking submenu items |
150
+ | Right-click context menus | ⚠️ Limited | Browser context menus inaccessible; app-level menus often work |
151
+ | Drag and drop | ✅ | Use `drag` with `selector` + `targetSelector` |
152
+ | Infinite scroll / lazy load | ✅ | Use `scroll:load` with repeat count |
153
+
154
+ **Workarounds:**
155
+
156
+ ```json
157
+ // Run JS directly
158
+ { "action": "eval", "value": "document.querySelector('#btn').click()" }
159
+
160
+ // Shadow DOM
161
+ { "action": "eval", "value": "document.querySelector('my-el').shadowRoot.querySelector('button').click()" }
162
+
163
+ // Timing-sensitive steps
164
+ { "action": "wait:ms", "value": "500" }
165
+ ```
package/ghostrun.js CHANGED
@@ -3644,7 +3644,7 @@ var import_uuid = require("uuid");
3644
3644
  var HOME_DIR = process.env.HOME || process.env.USERPROFILE || ".";
3645
3645
  var DATA_PATH = path.join(HOME_DIR, ".ghostrun");
3646
3646
  var DB_PATH = path.join(DATA_PATH, "data", "ghostrun.db");
3647
- var DatabaseManager = class {
3647
+ var DatabaseManager = class _DatabaseManager {
3648
3648
  db;
3649
3649
  constructor() {
3650
3650
  fs.mkdirSync(path.join(DATA_PATH, "data"), { recursive: true });
@@ -3982,35 +3982,72 @@ var DatabaseManager = class {
3982
3982
  };
3983
3983
  }
3984
3984
  // ---- DB migrations ----
3985
+ //
3986
+ // Uses SQLite's built-in PRAGMA user_version as a schema version counter.
3987
+ // Each migration runs exactly once: we read the current version, apply every
3988
+ // migration whose index is >= that version (in order), then write the new version.
3989
+ //
3990
+ // HOW TO ADD A NEW MIGRATION:
3991
+ // 1. Append a new string to the MIGRATIONS array below.
3992
+ // 2. That's it. The runner handles the rest.
3993
+ //
3994
+ // Never edit or reorder existing entries — just append.
3995
+ static MIGRATIONS = [
3996
+ // v1: add diff_percent to steps
3997
+ "ALTER TABLE steps ADD COLUMN diff_percent REAL",
3998
+ // v2: add created_by to flows
3999
+ "ALTER TABLE flows ADD COLUMN created_by TEXT NOT NULL DEFAULT 'human'",
4000
+ // v3: add verified flag to flows
4001
+ "ALTER TABLE flows ADD COLUMN verified INTEGER NOT NULL DEFAULT 0",
4002
+ // v4: add captured_at to run_data
4003
+ "ALTER TABLE run_data ADD COLUMN captured_at TEXT DEFAULT (datetime('now'))",
4004
+ // v5: environments table
4005
+ `CREATE TABLE IF NOT EXISTS environments (
4006
+ id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, base_url TEXT,
4007
+ variables TEXT NOT NULL DEFAULT '{}', is_active INTEGER NOT NULL DEFAULT 0,
4008
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
4009
+ )`,
4010
+ // v6: api_responses table
4011
+ `CREATE TABLE IF NOT EXISTS api_responses (
4012
+ id TEXT PRIMARY KEY, run_id TEXT NOT NULL, step_number INTEGER NOT NULL,
4013
+ method TEXT NOT NULL, url TEXT NOT NULL, status_code INTEGER,
4014
+ response_time_ms INTEGER, response_headers TEXT, response_body TEXT,
4015
+ error_message TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now'))
4016
+ )`,
4017
+ // v7: perf_runs table
4018
+ `CREATE TABLE IF NOT EXISTS perf_runs (
4019
+ id TEXT PRIMARY KEY, flow_id TEXT NOT NULL, flow_name TEXT NOT NULL,
4020
+ config TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'running',
4021
+ total_requests INTEGER, success_requests INTEGER, failed_requests INTEGER,
4022
+ avg_rps REAL, p50_ms INTEGER, p95_ms INTEGER, p99_ms INTEGER,
4023
+ min_ms INTEGER, max_ms INTEGER, per_step_stats TEXT,
4024
+ started_at TEXT NOT NULL DEFAULT (datetime('now')), completed_at TEXT
4025
+ )`
4026
+ // --- add new migrations below this line ---
4027
+ ];
4028
+ // Number of migrations that existed before we introduced versioning.
4029
+ // Existing databases have these applied already (via old try/catch approach)
4030
+ // but their user_version is 0. We detect this and fast-forward rather than
4031
+ // re-running them (which would throw "duplicate column" errors).
4032
+ static LEGACY_MIGRATION_COUNT = 7;
4033
+ columnExists(table, column) {
4034
+ const cols = this.db.pragma(`table_info(${table})`);
4035
+ return cols.some((c) => c.name === column);
4036
+ }
3985
4037
  runMigrations() {
3986
- try {
3987
- this.db.exec("ALTER TABLE steps ADD COLUMN diff_percent REAL");
3988
- } catch {
3989
- }
3990
- try {
3991
- this.db.exec("ALTER TABLE flows ADD COLUMN created_by TEXT NOT NULL DEFAULT 'human'");
3992
- } catch {
3993
- }
3994
- try {
3995
- this.db.exec("ALTER TABLE flows ADD COLUMN verified INTEGER NOT NULL DEFAULT 0");
3996
- } catch {
3997
- }
3998
- try {
3999
- this.db.exec("ALTER TABLE run_data ADD COLUMN captured_at TEXT DEFAULT (datetime('now'))");
4000
- } catch {
4001
- }
4002
- try {
4003
- this.db.exec(`CREATE TABLE IF NOT EXISTS environments (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, base_url TEXT, variables TEXT NOT NULL DEFAULT '{}', is_active INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')))`);
4004
- } catch {
4005
- }
4006
- try {
4007
- this.db.exec(`CREATE TABLE IF NOT EXISTS api_responses (id TEXT PRIMARY KEY, run_id TEXT NOT NULL, step_number INTEGER NOT NULL, method TEXT NOT NULL, url TEXT NOT NULL, status_code INTEGER, response_time_ms INTEGER, response_headers TEXT, response_body TEXT, error_message TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')))`);
4008
- } catch {
4009
- }
4010
- try {
4011
- this.db.exec(`CREATE TABLE IF NOT EXISTS perf_runs (id TEXT PRIMARY KEY, flow_id TEXT NOT NULL, flow_name TEXT NOT NULL, config TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'running', total_requests INTEGER, success_requests INTEGER, failed_requests INTEGER, avg_rps REAL, p50_ms INTEGER, p95_ms INTEGER, p99_ms INTEGER, min_ms INTEGER, max_ms INTEGER, per_step_stats TEXT, started_at TEXT NOT NULL DEFAULT (datetime('now')), completed_at TEXT)`);
4012
- } catch {
4038
+ let currentVersion = this.db.pragma("user_version", { simple: true }) ?? 0;
4039
+ if (currentVersion === 0 && this.columnExists("steps", "diff_percent")) {
4040
+ currentVersion = _DatabaseManager.LEGACY_MIGRATION_COUNT;
4041
+ this.db.pragma(`user_version = ${currentVersion}`);
4013
4042
  }
4043
+ if (currentVersion >= _DatabaseManager.MIGRATIONS.length) return;
4044
+ const applyAll = this.db.transaction(() => {
4045
+ for (let i = currentVersion; i < _DatabaseManager.MIGRATIONS.length; i++) {
4046
+ this.db.exec(_DatabaseManager.MIGRATIONS[i]);
4047
+ }
4048
+ this.db.pragma(`user_version = ${_DatabaseManager.MIGRATIONS.length}`);
4049
+ });
4050
+ applyAll();
4014
4051
  }
4015
4052
  // ---- Suites ----
4016
4053
  createSuite(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostrun-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Browser automation + API testing + load testing in one tool. Record flows, test REST APIs, run VU-based load tests, export to k6. Entirely local.",
5
5
  "main": "ghostrun.js",
6
6
  "bin": {