sessionsnap 0.0.3 → 0.0.4

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 CHANGED
@@ -10,13 +10,14 @@ Designed for test automation, internal tooling, and any workflow that requires p
10
10
 
11
11
  - **Human-in-the-loop** — You log in manually (CAPTCHA / 2FA friendly)
12
12
  - **Persistent profiles** — Sessions saved to disk and reusable across runs
13
- - **Auto-detect login completion** — URL and cookie heuristics
13
+ - **Auto-detect login completion** — URL and cookie heuristics, optional regex target
14
14
  - **Session auto-update** — Session snapshot is refreshed when the browser closes
15
- - **Automatic screenshots** — Captures a PNG after login and on session open
16
- - **Action recording** — Inject a recorder to capture clicks, inputs, navigations
17
- - **Action replay** — Replay recorded actions with configurable speed
15
+ - **Automatic screenshots** — Captures a PNG after login, on session open, and on record
16
+ - **Interactive recording** — On-page overlay UI with start/stop controls and named recordings
17
+ - **Named recordings** — Save recordings with names, replay by name
18
+ - **Action replay** — Replay recorded actions with configurable speed and visual cursor
18
19
  - **Desktop viewport** — 1440×900 default for realistic rendering
19
- - **Dual runner support** — Puppeteer (bundled) and Playwright (optional)
20
+ - **Dual runner support** — Puppeteer (via puppeteer-core) and Playwright (optional)
20
21
  - **Works for any website** — SaaS dashboards, admin panels, internal tools
21
22
 
22
23
  ## Install
@@ -25,15 +26,15 @@ Designed for test automation, internal tooling, and any workflow that requires p
25
26
  npm install
26
27
  ```
27
28
 
28
- Puppeteer is included as a direct dependency. For Playwright support, install it separately:
29
+ `puppeteer-core` is included as a direct dependency and uses your system's Chrome/Chromium. For Playwright support, install it separately:
29
30
 
30
31
  ```bash
31
32
  npm install playwright
32
33
  ```
33
34
 
34
- ## CLI Usage
35
+ ## CLI Commands
35
36
 
36
- ### Capture a session
37
+ ### `capture` — Capture a session
37
38
 
38
39
  Launch a headed browser, log in manually, and save the session:
39
40
 
@@ -43,10 +44,11 @@ sessionsnap capture <url> \
43
44
  [--runner puppeteer|playwright] \
44
45
  [--out <file>] \
45
46
  [--wait <minutes>] \
46
- [--record]
47
+ [--target <regex>]
47
48
  ```
48
49
 
49
- `--runner` defaults to `puppeteer` since it is bundled as a direct dependency.
50
+ - `--runner` defaults to `puppeteer`
51
+ - `--target` allows specifying a regex pattern for the post-login URL (e.g. `"/dashboard"` or `"^https://.*/app"`)
50
52
 
51
53
  **Example:**
52
54
 
@@ -62,13 +64,18 @@ The tool will:
62
64
  5. Take a screenshot of the post-login state
63
65
  6. Capture cookies and save a session snapshot
64
66
 
65
- ### Open a URL with a saved session
67
+ With a specific target URL pattern:
68
+
69
+ ```bash
70
+ sessionsnap capture https://app.acmecorp.io/login --profile acme --target "/dashboard"
71
+ ```
72
+
73
+ ### `open` — Open a URL with a saved session
66
74
 
67
75
  ```bash
68
76
  sessionsnap open <url> \
69
77
  --profile <name> \
70
- [--runner puppeteer|playwright] \
71
- [--record]
78
+ [--runner puppeteer|playwright]
72
79
  ```
73
80
 
74
81
  **Example:**
@@ -79,183 +86,121 @@ sessionsnap open https://app.acmecorp.io/dashboard --profile acme
79
86
 
80
87
  When you close the browser, the session snapshot is automatically updated with the latest cookies.
81
88
 
82
- ### List all profiles
83
-
84
- ```bash
85
- sessionsnap list [--json]
86
- ```
87
-
88
- Shows all saved profiles with a summary: runner, URL, relative date, screenshot/recording counts.
89
-
90
- **Example output:**
91
-
92
- ```
93
- [sessionsnap] 2 profile(s) found:
94
-
95
- acme [puppeteer] https://app.acmecorp.io/dashboard (2h ago) 📸 3 🔴 2
96
- staging [playwright] https://staging.acmecorp.io/home (1d ago) 📸 1
97
- ```
98
-
99
- Use `--json` for machine-readable output (useful for scripting).
89
+ ### `record` Record user actions
100
90
 
101
- ### Show profile status
91
+ Open a browser with a saved session and record user actions via an on-page overlay UI:
102
92
 
103
93
  ```bash
104
- sessionsnap status --profile <name>
105
- ```
106
-
107
- Prints profile details including directory path, session metadata, cookie/origin counts, and a list of saved screenshots and recordings.
108
-
109
- **Example output:**
110
-
111
- ```
112
- Profile: acme
113
- Directory: /Users/you/.sessionsnap/profiles/acme
114
- Runner: puppeteer
115
- Captured at: 2026-02-09T12:00:00.000Z
116
- Updated at: 2026-02-09T12:05:00.000Z
117
- Start URL: https://app.acmecorp.io/login
118
- Final URL: https://app.acmecorp.io/dashboard
119
- Cookies: 12
120
- Origins: 0
121
- Screenshots:
122
- - capture-2026-02-09T12-00-00-000Z.png
123
- - open-2026-02-09T12-05-00-000Z.png
124
- Recordings:
125
- - actions-capture-2026-02-09T12-00-00-000Z.json
94
+ sessionsnap record <url> \
95
+ --profile <name> \
96
+ [--runner puppeteer|playwright] \
97
+ [--name <recording-name>]
126
98
  ```
127
99
 
128
- ## Snapshot Format
129
-
130
- ```json
131
- {
132
- "meta": {
133
- "runner": "puppeteer",
134
- "profile": "acme",
135
- "capturedAt": "2026-02-09T12:00:00.000Z",
136
- "startUrl": "https://app.acmecorp.io/login",
137
- "finalUrl": "https://app.acmecorp.io/dashboard"
138
- },
139
- "cookies": [
140
- {
141
- "name": "session_token",
142
- "value": "abc123",
143
- "domain": ".acmecorp.io",
144
- "path": "/",
145
- "expires": 1700000000,
146
- "httpOnly": true,
147
- "secure": true,
148
- "sameSite": "Lax"
149
- }
150
- ],
151
- "origins": []
152
- }
153
- ```
100
+ **Interactive mode** (no `--name`):
101
+ 1. Browser opens with an overlay showing a **"Start Recording"** button
102
+ 2. Click Start → overlay switches to recording mode (timer + action counter + Stop button)
103
+ 3. Perform your actions on the page
104
+ 4. Click Stop → a dialog prompts you for a recording name
105
+ 5. Enter a name and click Save → recording saved to disk
154
106
 
155
- After using `open`, an `updatedAt` field is added to `meta`.
107
+ **Pre-named mode** (`--name` provided):
108
+ 1. Recording starts automatically with the given name
109
+ 2. Click Stop when done → recording saved immediately
156
110
 
157
- ## Screenshots
111
+ **Examples:**
158
112
 
159
- Screenshots are automatically saved to the profile directory on every `capture` and `open` command:
113
+ ```bash
114
+ # Interactive: name the recording when you stop
115
+ sessionsnap record https://app.acmecorp.io/dashboard --profile acme
160
116
 
117
+ # Pre-named: recording starts immediately as "checkout-flow"
118
+ sessionsnap record https://app.acmecorp.io/dashboard --profile acme --name "checkout-flow"
161
119
  ```
162
- ~/.sessionsnap/profiles/acme/
163
- snapshot.json
164
- capture-2026-02-09T12-00-00-000Z.png
165
- open-2026-02-09T12-05-00-000Z.png
166
- ```
167
-
168
- - **capture** — taken right after login is detected
169
- - **open** — taken after the page loads with the saved session
170
-
171
- ## Action Recording
172
120
 
173
- Use `--record` on `capture` or `open` to inject a lightweight recorder into the page. It captures 11 action types:
121
+ The recorder captures 12 action types:
174
122
 
175
123
  | Action | What it records | Replay |
176
124
  |--------|----------------|--------|
177
125
  | **click** | selector, tag, text, coordinates | CSS selector → text → coordinates fallback |
178
126
  | **dblclick** | selector, tag, text, coordinates | Same 3-tier fallback as click |
179
- | **hover** | selector, tag, text | `page.hover(selector)` with text fallback |
127
+ | **hover** | selector, tag, text, coordinates | CSS selector text → coordinates fallback |
128
+ | **mousemove** | x, y coordinates | `page.mouse.move(x, y)` |
180
129
  | **input** | selector, value (passwords masked) | Clear + type / fill |
181
130
  | **select** | selector, selected value & text | `page.select` / `selectOption` |
182
- | **checkbox** | selector, checked state, type (checkbox/radio) | Smart toggle (checks current state) |
183
- | **keydown** | key, combo (Ctrl/Meta/Alt+key), selector | `page.keyboard.press` with modifiers |
131
+ | **checkbox** | selector, checked state, type | Smart toggle (checks current state) |
132
+ | **keydown** | key, combo (Ctrl/Meta/Alt+key) | `page.keyboard.press` with modifiers |
184
133
  | **submit** | form selector, action, method | `form.requestSubmit()` / Enter fallback |
185
- | **scroll** | scrollX, scrollY, scrollHeight, viewportHeight | `window.scrollTo(x, y)` |
186
- | **file** | selector, file names, file count | Log only (cannot replay file selection) |
134
+ | **scroll** | scroll position (window + inner elements) | `window.scrollTo` / `element.scrollTo` |
135
+ | **file** | selector, file names, count | Log only (cannot replay file selection) |
187
136
  | **navigation** | from/to URLs (SPA & traditional) | `page.goto(url)` |
188
137
 
189
- Noise reduction: hover is debounced (200ms, interactive elements only), scroll is debounced (400ms), keydown only captures special keys (Enter/Escape/Tab/arrows/F-keys) and modifier combos (Ctrl+key, Meta+key).
138
+ Noise reduction: hover debounced (150ms, interactive elements only), scroll debounced (400ms), mousemove throttled (~60fps), keydown only captures special keys and modifier combos.
190
139
 
191
- Recorded actions are saved as timestamped JSON files in the profile directory:
140
+ Named recordings are tracked in `recordings.json` inside the profile directory:
192
141
 
193
142
  ```
194
143
  ~/.sessionsnap/profiles/acme/
195
- actions-capture-2026-02-09T12-00-00-000Z.json
196
- actions-open-2026-02-09T12-05-00-000Z.json
197
- ```
198
-
199
- **Example:**
200
-
201
- ```bash
202
- sessionsnap capture https://app.acmecorp.io/login --profile acme --record
144
+ recordings.json # metadata: name → file mapping
145
+ actions-checkout-flow.json # named recording
146
+ actions-recording-2026-...json # unnamed recording
203
147
  ```
204
148
 
205
- ### Replaying actions
206
-
207
- Replay a previously recorded session:
149
+ ### `replay` — Replay recorded actions
208
150
 
209
151
  ```bash
210
152
  sessionsnap replay \
211
153
  --profile <name> \
212
- [--runner puppeteer|playwright] \
154
+ [--name <recording-name>] \
213
155
  [--file <actions-file>] \
156
+ [--runner puppeteer|playwright] \
214
157
  [--speed <multiplier>] \
215
158
  [--headless] \
216
- [--bail]
159
+ [--bail] \
160
+ [--visual]
217
161
  ```
218
162
 
219
163
  **Examples:**
220
164
 
221
165
  ```bash
222
- # Replay the latest recording at normal speed
166
+ # Replay a named recording
167
+ sessionsnap replay --profile acme --name "checkout-flow"
168
+
169
+ # Replay the latest recording
223
170
  sessionsnap replay --profile acme
224
171
 
225
- # Replay at 2x speed
226
- sessionsnap replay --profile acme --speed 2
172
+ # Replay at 2x speed with visual cursor
173
+ sessionsnap replay --profile acme --name "checkout-flow" --speed 2 --visual
227
174
 
228
- # Replay a specific recording file
229
- sessionsnap replay --profile acme --file actions-capture-2026-02-09T12-00-00-000Z.json
175
+ # Replay a specific file
176
+ sessionsnap replay --profile acme --file actions-recording-2026-02-09T12-00-00-000Z.json
230
177
 
231
- # Replay in headless mode (no visible browser)
232
- sessionsnap replay --profile acme --headless
178
+ # Replay in headless mode
179
+ sessionsnap replay --profile acme --name "checkout-flow" --headless
233
180
 
234
181
  # Fail fast — stop on first failure (exit code 1)
235
- sessionsnap replay --profile acme --bail
182
+ sessionsnap replay --profile acme --name "checkout-flow" --bail
236
183
  ```
237
184
 
238
185
  The replayer:
239
186
  1. Launches a browser with the saved session profile
240
187
  2. Navigates to the starting URL from the recording
241
- 3. Executes each action in order (click, dblclick, hover, type, select, checkbox, keydown, submit, scroll, navigate)
188
+ 3. Executes each action in order
242
189
  4. Uses real timing from the recording (clamped to 50ms–5s per action)
243
190
  5. Takes a screenshot after replay completes
244
191
  6. Password fields are skipped during replay for security
245
192
 
246
193
  #### Error handling
247
194
 
248
- By default, replay is **fault-tolerant**. If an individual action fails (element not found, timeout, etc.), it is logged as `FAILED` and the replay **continues with the next action** — the whole flow does not crash.
195
+ By default, replay is **fault-tolerant**. If an individual action fails (element not found, timeout, etc.), it is logged as `FAILED` and the replay **continues with the next action**.
249
196
 
250
- Use `--bail` for **fail-fast** mode: the replay stops immediately on the first failure and exits with code `1`. This is useful in CI/CD pipelines or when you need strict action accuracy.
197
+ Use `--bail` for **fail-fast** mode: the replay stops immediately on the first failure and exits with code `1`. Useful in CI/CD pipelines.
251
198
 
252
199
  For click actions, there is a 3-step fallback strategy:
253
200
  1. **CSS selector** — tries the recorded selector (2s timeout)
254
201
  2. **Text content** — searches all visible elements for matching text
255
202
  3. **Coordinates** — clicks at the recorded x/y position
256
203
 
257
- If all three strategies fail, the action is logged as `FAILED` and skipped.
258
-
259
204
  ```
260
205
  [sessionsnap] [1/5] click: #submit-btn "Submit"
261
206
  [sessionsnap] [2/5] click FAILED: Element not found: #old-selector (text: "Gone")
@@ -265,20 +210,417 @@ If all three strategies fail, the action is logged as `FAILED` and skipped.
265
210
  [sessionsnap] Replay complete.
266
211
  ```
267
212
 
268
- Each action entry includes a `type`, `timestamp`, `url`, and type-specific fields:
213
+ ### `recordings` List recordings for a profile
214
+
215
+ ```bash
216
+ sessionsnap recordings --profile <name> [--json]
217
+ ```
218
+
219
+ Shows all recordings with name (if named), action count, age, action type summary, and a replay hint:
220
+
221
+ ```
222
+ [sessionsnap] 3 recording(s) for profile "acme":
223
+
224
+ 1. checkout-flow (actions-checkout-flow.json)
225
+ 42 actions (2h ago) replay: --name "checkout-flow"
226
+ click:12 input:8 navigation:3 hover:15 scroll:4
227
+
228
+ 2. actions-recording-2026-02-11T14-30-00-000Z.json
229
+ 18 actions (1d ago) replay: --file "actions-recording-2026-02-11T14-30-00-000Z.json"
230
+ click:6 input:4 navigation:2 mousemove:6
231
+ ```
232
+
233
+ ### `list` — List all profiles
234
+
235
+ ```bash
236
+ sessionsnap list [--json]
237
+ ```
238
+
239
+ Shows all saved profiles with runner, URL, relative date, screenshot/recording counts:
240
+
241
+ ```
242
+ [sessionsnap] 2 profile(s) found:
243
+
244
+ acme [puppeteer] https://app.acmecorp.io/dashboard (2h ago) 📸 3 🔴 2
245
+ staging [playwright] https://staging.acmecorp.io/home (1d ago) 📸 1
246
+ ```
247
+
248
+ ### `status` — Show profile details
249
+
250
+ ```bash
251
+ sessionsnap status --profile <name>
252
+ ```
253
+
254
+ Prints profile details: directory path, session metadata, cookie/origin counts, screenshots and recordings.
255
+
256
+ ### `clean` — Delete a profile
257
+
258
+ ```bash
259
+ sessionsnap clean --profile <name> [--force]
260
+ ```
261
+
262
+ Deletes a profile and all its data (snapshot, screenshots, recordings). Prompts for confirmation unless `--force` is used.
263
+
264
+ ## Typical Workflow
265
+
266
+ ```bash
267
+ # 1. Capture — log in and save the session
268
+ sessionsnap capture https://app.acmecorp.io/login --profile acme
269
+
270
+ # 2. Record — open with saved session, record a user flow
271
+ sessionsnap record https://app.acmecorp.io/dashboard --profile acme --name "checkout-flow"
272
+
273
+ # 3. Replay — replay the recorded flow
274
+ sessionsnap replay --profile acme --name "checkout-flow"
275
+
276
+ # 4. Open — browse with saved session (no recording)
277
+ sessionsnap open https://app.acmecorp.io/settings --profile acme
278
+
279
+ # 5. List recordings
280
+ sessionsnap recordings --profile acme
281
+
282
+ # 6. Clean up
283
+ sessionsnap clean --profile acme --force
284
+ ```
285
+
286
+ ## Snapshot Format
269
287
 
270
288
  ```json
271
- [
272
- { "type": "click", "selector": "#login-button", "tag": "button", "text": "Sign In", "x": 720, "y": 450 },
273
- { "type": "dblclick", "selector": ".cell", "tag": "td", "text": "Edit me", "x": 300, "y": 200 },
274
- { "type": "hover", "selector": "nav > .dropdown", "tag": "button", "text": "Menu" },
275
- { "type": "input", "selector": "input[name=\"email\"]", "tag": "input", "inputType": "email", "value": "user@example.com" },
276
- { "type": "checkbox", "selector": "input[name=\"remember\"]", "inputType": "checkbox", "checked": true },
277
- { "type": "keydown", "key": "Escape", "combo": "Escape", "selector": ".modal", "tag": "div" },
278
- { "type": "scroll", "scrollX": 0, "scrollY": 800, "scrollHeight": 3200, "viewportHeight": 900 },
279
- { "type": "file", "selector": "input[type=\"file\"]", "fileNames": ["photo.jpg"], "fileCount": 1 },
280
- { "type": "navigation", "from": "/login", "to": "/dashboard" }
281
- ]
289
+ {
290
+ "meta": {
291
+ "runner": "puppeteer",
292
+ "profile": "acme",
293
+ "capturedAt": "2026-02-09T12:00:00.000Z",
294
+ "startUrl": "https://app.acmecorp.io/login",
295
+ "finalUrl": "https://app.acmecorp.io/dashboard"
296
+ },
297
+ "cookies": [
298
+ {
299
+ "name": "session_token",
300
+ "value": "abc123",
301
+ "domain": ".acmecorp.io",
302
+ "path": "/",
303
+ "expires": 1700000000,
304
+ "httpOnly": true,
305
+ "secure": true,
306
+ "sameSite": "Lax"
307
+ }
308
+ ],
309
+ "origins": []
310
+ }
311
+ ```
312
+
313
+ After using `open`, an `updatedAt` field is added to `meta`.
314
+
315
+ ## Architecture
316
+
317
+ ### High-Level Overview
318
+
319
+ ```mermaid
320
+ graph TB
321
+ subgraph CLI["CLI Layer — bin/sessionsnap.js"]
322
+ CMD_CAPTURE["capture"]
323
+ CMD_OPEN["open"]
324
+ CMD_RECORD["record"]
325
+ CMD_REPLAY["replay"]
326
+ CMD_LIST["list"]
327
+ CMD_RECORDINGS["recordings"]
328
+ CMD_STATUS["status"]
329
+ CMD_CLEAN["clean"]
330
+ end
331
+
332
+ subgraph RUNNERS["Runner Layer — Puppeteer / Playwright"]
333
+ subgraph PUPPETEER["puppeteer/"]
334
+ P_BROWSER["browser.js\n(Chrome discovery)"]
335
+ P_CAPTURE["capture.js"]
336
+ P_OPEN["open.js"]
337
+ P_RECORD["record.js"]
338
+ P_REPLAY["replay.js"]
339
+ end
340
+ subgraph PLAYWRIGHT["playwright/"]
341
+ PW_CAPTURE["capture.js"]
342
+ PW_OPEN["open.js"]
343
+ PW_RECORD["record.js"]
344
+ PW_REPLAY["replay.js"]
345
+ end
346
+ end
347
+
348
+ subgraph CORE["Core Layer — Shared Logic"]
349
+ SNAPSHOT["snapshot.js\n(normalize cookies,\ncreate/update snapshot)"]
350
+ HEURISTICS["heuristics.js\n(login detection:\nURL + cookie + regex)"]
351
+ RECORDER["recorder.js\n(overlay UI + action\ncapture + writeFileSync)"]
352
+ REPLAYER["replayer.js\n(action execution +\nvisual cursor + fallback)"]
353
+ CSS_GEN["css-selector-generator\n(robust CSS selectors)"]
354
+ end
355
+
356
+ subgraph STORAGE["Storage Layer — store.js"]
357
+ PROFILES["~/.sessionsnap/profiles/"]
358
+ SNAP_FILE["snapshot.json"]
359
+ SCREENSHOTS["*.png"]
360
+ ACTIONS["actions-*.json"]
361
+ REC_META["recordings.json"]
362
+ end
363
+
364
+ subgraph BROWSER["Browser"]
365
+ PAGE["Browser Page"]
366
+ OVERLAY["Recording Overlay UI"]
367
+ CDP["CDP / storageState"]
368
+ end
369
+
370
+ CMD_CAPTURE --> P_CAPTURE & PW_CAPTURE
371
+ CMD_OPEN --> P_OPEN & PW_OPEN
372
+ CMD_RECORD --> P_RECORD & PW_RECORD
373
+ CMD_REPLAY --> P_REPLAY & PW_REPLAY
374
+ CMD_LIST & CMD_RECORDINGS & CMD_STATUS & CMD_CLEAN --> STORAGE
375
+
376
+ P_CAPTURE & P_OPEN & P_RECORD & P_REPLAY --> P_BROWSER
377
+ P_BROWSER --> PAGE
378
+
379
+ P_CAPTURE --> HEURISTICS & SNAPSHOT
380
+ P_OPEN --> SNAPSHOT
381
+ P_RECORD --> RECORDER
382
+ P_REPLAY --> REPLAYER
383
+
384
+ PW_CAPTURE --> HEURISTICS & SNAPSHOT
385
+ PW_OPEN --> SNAPSHOT
386
+ PW_RECORD --> RECORDER
387
+ PW_REPLAY --> REPLAYER
388
+
389
+ RECORDER --> CSS_GEN
390
+ RECORDER --> OVERLAY
391
+ RECORDER --> PAGE
392
+ REPLAYER --> PAGE
393
+
394
+ HEURISTICS --> CDP
395
+ SNAPSHOT --> STORAGE
396
+
397
+ P_CAPTURE & P_OPEN & P_RECORD & P_REPLAY --> STORAGE
398
+ PW_CAPTURE & PW_OPEN & PW_RECORD & PW_REPLAY --> STORAGE
399
+
400
+ STORAGE --> SNAP_FILE & SCREENSHOTS & ACTIONS & REC_META
401
+
402
+ style CLI fill:#1e293b,stroke:#475569,color:#f8fafc
403
+ style CORE fill:#1e3a5f,stroke:#3b82f6,color:#f8fafc
404
+ style RUNNERS fill:#1a2e1a,stroke:#22c55e,color:#f8fafc
405
+ style STORAGE fill:#3b1f0b,stroke:#f97316,color:#f8fafc
406
+ style BROWSER fill:#3b0764,stroke:#a855f7,color:#f8fafc
407
+ ```
408
+
409
+ ### Capture Flow
410
+
411
+ ```mermaid
412
+ sequenceDiagram
413
+ actor User
414
+ participant CLI as CLI (commander)
415
+ participant Runner as Runner (Puppeteer/Playwright)
416
+ participant Browser as Headed Browser
417
+ participant Heuristics as heuristics.js
418
+ participant Store as store.js
419
+ participant Disk as ~/.sessionsnap/profiles/
420
+
421
+ User->>CLI: sessionsnap capture <url> --profile acme
422
+ CLI->>Store: ensureProfileDir("acme")
423
+ Store->>Disk: mkdir ~/.sessionsnap/profiles/acme/
424
+ CLI->>Runner: capture(url, opts)
425
+ Runner->>Browser: launch (headed, 1440×900)
426
+ Runner->>Browser: page.goto(url)
427
+ Browser-->>User: Login page displayed
428
+ User->>Browser: Manual login (CAPTCHA, 2FA, etc.)
429
+
430
+ loop Every 2 seconds
431
+ Runner->>Heuristics: waitForLoginComplete(page)
432
+ Heuristics->>Browser: Check URL pattern + cookie count
433
+ alt --target regex provided
434
+ Heuristics->>Browser: Check URL matches regex
435
+ end
436
+ end
437
+
438
+ Heuristics-->>Runner: Login detected ✓
439
+ Runner->>Browser: waitForNetworkIdle()
440
+ Runner->>Browser: page.screenshot()
441
+ Runner->>Store: saveScreenshot(capture-*.png)
442
+ Runner->>Browser: getAllCookies (CDP) / context.cookies()
443
+ Runner->>Store: createSnapshot + saveSnapshot
444
+ Store->>Disk: Write snapshot.json + screenshot
445
+ Runner->>Browser: browser.close()
446
+ CLI-->>User: Profile saved ✓
447
+ ```
448
+
449
+ ### Record & Replay Flow
450
+
451
+ ```mermaid
452
+ sequenceDiagram
453
+ actor User
454
+ participant CLI as CLI
455
+ participant Runner as Runner
456
+ participant Browser as Browser Page
457
+ participant Recorder as recorder.js
458
+ participant Replayer as replayer.js
459
+ participant Store as store.js
460
+ participant Disk as Disk
461
+
462
+ Note over User,Disk: ── RECORD PHASE ──
463
+
464
+ User->>CLI: sessionsnap record <url> --profile acme
465
+ CLI->>Store: loadSnapshot("acme") — verify session exists
466
+ CLI->>Runner: record(url, opts)
467
+ Runner->>Browser: launch with userDataDir
468
+ Runner->>Recorder: setupRecorder(page, runner, dir)
469
+ Recorder->>Browser: inject css-selector-generator lib
470
+ Recorder->>Browser: inject action listener script
471
+ Recorder->>Browser: inject overlay UI (Start/Stop)
472
+ Recorder->>Browser: exposeFunction(__sessionsnap_report_action)
473
+ Runner->>Browser: page.goto(url)
474
+ Browser-->>User: Page with overlay displayed
475
+
476
+ User->>Browser: Click "Start Recording"
477
+ Browser->>Recorder: __sessionsnap_start_recording()
478
+
479
+ User->>Browser: Interact (click, type, hover, scroll...)
480
+ loop Each user action
481
+ Browser->>Recorder: __sessionsnap_report_action(action)
482
+ Recorder->>Disk: writeFileSync (crash-safe)
483
+ Recorder-->>CLI: Log: [click] #btn "Submit"
484
+ end
485
+
486
+ User->>Browser: Click "Stop"
487
+ Browser->>Browser: Show name dialog
488
+ User->>Browser: Enter "checkout-flow" → Save
489
+ Browser->>Recorder: __sessionsnap_stop_recording("checkout-flow")
490
+ Recorder->>Disk: Write actions-checkout_flow.json
491
+ Recorder->>Store: saveRecordingMetadata
492
+ Store->>Disk: Update recordings.json
493
+
494
+ Note over User,Disk: ── REPLAY PHASE ──
495
+
496
+ User->>CLI: sessionsnap replay --profile acme --name "checkout-flow"
497
+ CLI->>Store: loadRecordingByName("acme", "checkout-flow")
498
+ Store-->>CLI: actions[] (N actions)
499
+ CLI->>Runner: replay(actions, opts)
500
+ Runner->>Browser: launch with userDataDir
501
+ Runner->>Browser: goto(startUrl)
502
+ Runner->>Replayer: replayActions(page, actions)
503
+
504
+ loop Each action
505
+ Replayer->>Replayer: Calculate delay from timestamps
506
+ alt click / dblclick
507
+ Replayer->>Browser: mouse.move(x, y) → CSS / text / coords fallback
508
+ else input
509
+ Replayer->>Browser: clear + type(value)
510
+ else hover
511
+ Replayer->>Browser: mouse.move(x, y) → hover(selector)
512
+ else navigation
513
+ Replayer->>Browser: page.goto(url) → waitForNetworkIdle
514
+ else scroll
515
+ Replayer->>Browser: scrollTo(x, y)
516
+ end
517
+ Replayer-->>CLI: Log: [1/N] action description
518
+ end
519
+
520
+ Replayer-->>Runner: Replay complete
521
+ Runner->>Browser: screenshot()
522
+ Runner->>Store: saveScreenshot(replay-*.png)
523
+ Runner->>Browser: browser.close()
524
+ CLI-->>User: Replay finished ✓
525
+ ```
526
+
527
+ ### Storage Structure
528
+
529
+ ```mermaid
530
+ graph LR
531
+ subgraph HOME["~/.sessionsnap/"]
532
+ subgraph PROFILES["profiles/"]
533
+ subgraph ACME["acme/"]
534
+ SNAP["snapshot.json\n(cookies, meta, origins)"]
535
+ REC_JSON["recordings.json\n(name → file mapping)"]
536
+ CAP_PNG["capture-2026-*.png"]
537
+ OPEN_PNG["open-2026-*.png"]
538
+ REPLAY_PNG["replay-2026-*.png"]
539
+ RECORD_PNG["record-2026-*.png"]
540
+ ACTIONS1["actions-checkout_flow.json"]
541
+ ACTIONS2["actions-recording-*.json"]
542
+ USERDATA["Chrome userDataDir\n(cookies, localStorage,\nindexedDB, cache)"]
543
+ end
544
+ subgraph STAGING["staging/"]
545
+ SNAP2["snapshot.json"]
546
+ OTHER["..."]
547
+ end
548
+ end
549
+ end
550
+
551
+ style HOME fill:#1c1917,stroke:#78716c,color:#f5f5f4
552
+ style PROFILES fill:#292524,stroke:#a8a29e,color:#f5f5f4
553
+ style ACME fill:#1e3a5f,stroke:#3b82f6,color:#f8fafc
554
+ style STAGING fill:#1e3a5f,stroke:#3b82f6,color:#f8fafc
555
+ ```
556
+
557
+ ### Action Recording Pipeline
558
+
559
+ ```mermaid
560
+ flowchart LR
561
+ subgraph PAGE["Browser Page Context"]
562
+ EVENTS["DOM Events\nclick · input · hover\nscroll · keydown\ndblclick · submit\ncheckbox · file\nmousemove"]
563
+ CSS["css-selector-generator\n(robust selectors)"]
564
+ DEBOUNCE["Debounce / Throttle\nhover: 150ms\nscroll: 400ms\nmousemove: ~16ms"]
565
+ OVERLAY["Overlay UI\nStart ⏺ / Stop ⏹\nTimer · Counter"]
566
+ FILTER["Filter\n- isOverlayEvent?\n- isRecording?\n- noise reduction"]
567
+ end
568
+
569
+ subgraph BRIDGE["Page ↔ Node.js Bridge"]
570
+ EXPOSE["page.exposeFunction\n__sessionsnap_report_action"]
571
+ end
572
+
573
+ subgraph NODEJS["Node.js Process"]
574
+ HANDLER["Action Handler\n(log + accumulate)"]
575
+ SYNC_WRITE["writeFileSync\n(crash-safe)"]
576
+ METADATA["saveRecordingMetadata\n(recordings.json)"]
577
+ end
578
+
579
+ EVENTS --> CSS --> DEBOUNCE --> FILTER --> EXPOSE
580
+ OVERLAY -.->|start/stop| FILTER
581
+ EXPOSE --> HANDLER --> SYNC_WRITE
582
+ HANDLER --> METADATA
583
+
584
+ style PAGE fill:#3b0764,stroke:#a855f7,color:#f8fafc
585
+ style BRIDGE fill:#713f12,stroke:#eab308,color:#f8fafc
586
+ style NODEJS fill:#1e3a5f,stroke:#3b82f6,color:#f8fafc
587
+ ```
588
+
589
+ ### Replay Fallback Strategy
590
+
591
+ ```mermaid
592
+ flowchart TD
593
+ START["Replay Action"] --> TYPE{Action Type?}
594
+
595
+ TYPE -->|click / dblclick| MOVE["mouse.move(x, y)"]
596
+ MOVE --> SEL{"CSS Selector\nfound?"}
597
+ SEL -->|Yes| CLICK_SEL["Click via selector ✓"]
598
+ SEL -->|No / timeout| TEXT{"Text content\nmatch?"}
599
+ TEXT -->|Yes| CLICK_TEXT["Click via text ✓"]
600
+ TEXT -->|No| COORDS{"Coordinates\navailable?"}
601
+ COORDS -->|Yes| CLICK_XY["Click at (x, y) ✓"]
602
+ COORDS -->|No| FAIL["FAILED ✗"]
603
+
604
+ TYPE -->|input| INPUT["Clear field → type value"]
605
+ TYPE -->|hover| HOVER["mouse.move → hover(selector)"]
606
+ TYPE -->|navigation| NAV["page.goto → waitForNetworkIdle"]
607
+ TYPE -->|scroll| SCROLL["window.scrollTo / element.scrollTo"]
608
+ TYPE -->|keydown| KEY["keyboard.press with modifiers"]
609
+ TYPE -->|checkbox| CHECK["Check state → toggle if needed"]
610
+ TYPE -->|submit| SUBMIT["requestSubmit / Enter key"]
611
+ TYPE -->|mousemove| MMOVE["mouse.move(x, y) per point"]
612
+
613
+ FAIL --> BAIL{--bail?}
614
+ BAIL -->|Yes| EXIT["Exit code 1"]
615
+ BAIL -->|No| NEXT["Continue to next action"]
616
+
617
+ style START fill:#1e3a5f,stroke:#3b82f6,color:#f8fafc
618
+ style FAIL fill:#7f1d1d,stroke:#ef4444,color:#f8fafc
619
+ style EXIT fill:#7f1d1d,stroke:#ef4444,color:#f8fafc
620
+ style CLICK_SEL fill:#14532d,stroke:#22c55e,color:#f8fafc
621
+ style CLICK_TEXT fill:#14532d,stroke:#22c55e,color:#f8fafc
622
+ style CLICK_XY fill:#14532d,stroke:#22c55e,color:#f8fafc
623
+ style NEXT fill:#713f12,stroke:#eab308,color:#f8fafc
282
624
  ```
283
625
 
284
626
  ## Project Structure
@@ -291,17 +633,20 @@ sessionsnap/
291
633
  core/
292
634
  snapshot.js # Canonical session format & normalization
293
635
  heuristics.js # Login detection logic (URL + cookie polling)
294
- recorder.js # Injected script for recording user actions
636
+ recorder.js # Injected script + overlay UI for recording
295
637
  replayer.js # Replay engine for recorded actions
296
638
  puppeteer/
639
+ browser.js # Chrome/Chromium executable discovery
297
640
  capture.js # Capture session via CDP
298
641
  open.js # Open URL with saved profile + update on close
642
+ record.js # Record user actions via Puppeteer
299
643
  replay.js # Replay recorded actions via Puppeteer
300
644
  playwright/
301
645
  capture.js # Capture session via storageState
302
646
  open.js # Open URL with saved profile + update on close
647
+ record.js # Record user actions via Playwright
303
648
  replay.js # Replay recorded actions via Playwright
304
- store.js # Profile directory & JSON I/O
649
+ store.js # Profile directory, metadata & JSON I/O
305
650
  test/
306
651
  store.test.js
307
652
  snapshot.test.js