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/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
+ ![GhostRun Demo](demo/out/ghostrun-demo.gif)
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