@harness-fe/mcp-server 3.0.1

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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/dist/auth.d.ts +53 -0
  4. package/dist/auth.js +212 -0
  5. package/dist/bridge.d.ts +302 -0
  6. package/dist/bridge.js +1580 -0
  7. package/dist/cli.d.ts +18 -0
  8. package/dist/cli.js +277 -0
  9. package/dist/daemon.d.ts +98 -0
  10. package/dist/daemon.js +80 -0
  11. package/dist/dashboardApi.d.ts +40 -0
  12. package/dist/dashboardApi.js +142 -0
  13. package/dist/dashboardSpa.d.ts +18 -0
  14. package/dist/dashboardSpa.js +180 -0
  15. package/dist/dashboardUrl.d.ts +13 -0
  16. package/dist/dashboardUrl.js +18 -0
  17. package/dist/eventsHandler.d.ts +24 -0
  18. package/dist/eventsHandler.js +114 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.js +6 -0
  21. package/dist/mcp.d.ts +15 -0
  22. package/dist/mcp.js +923 -0
  23. package/dist/mcpHttp.d.ts +39 -0
  24. package/dist/mcpHttp.js +49 -0
  25. package/dist/openBrowser.d.ts +33 -0
  26. package/dist/openBrowser.js +63 -0
  27. package/dist/remoteBridge.d.ts +61 -0
  28. package/dist/remoteBridge.js +307 -0
  29. package/dist/replayCreate.d.ts +36 -0
  30. package/dist/replayCreate.js +156 -0
  31. package/dist/replayViewer.d.ts +20 -0
  32. package/dist/replayViewer.js +168 -0
  33. package/dist/sessionRouter.d.ts +42 -0
  34. package/dist/sessionRouter.js +88 -0
  35. package/dist/store/JsonMemoryStore.d.ts +52 -0
  36. package/dist/store/JsonMemoryStore.js +119 -0
  37. package/dist/store/JsonTaskStore.d.ts +21 -0
  38. package/dist/store/JsonTaskStore.js +53 -0
  39. package/dist/store/JsonlStore.d.ts +128 -0
  40. package/dist/store/JsonlStore.js +1168 -0
  41. package/dist/store/MemoryEventStore.d.ts +47 -0
  42. package/dist/store/MemoryEventStore.js +111 -0
  43. package/dist/store/WriteQueue.d.ts +51 -0
  44. package/dist/store/WriteQueue.js +142 -0
  45. package/dist/store/index.d.ts +6 -0
  46. package/dist/store/index.js +5 -0
  47. package/dist/store/types.d.ts +416 -0
  48. package/dist/store/types.js +19 -0
  49. package/package.json +63 -0
  50. package/src/auth.test.ts +90 -0
  51. package/src/auth.ts +248 -0
  52. package/src/bridge-auth.test.ts +196 -0
  53. package/src/bridge.test.ts +1708 -0
  54. package/src/bridge.ts +1804 -0
  55. package/src/cli.ts +315 -0
  56. package/src/daemon.test.ts +123 -0
  57. package/src/daemon.ts +161 -0
  58. package/src/dashboardApi.test.ts +235 -0
  59. package/src/dashboardApi.ts +184 -0
  60. package/src/dashboardSpa.test.ts +239 -0
  61. package/src/dashboardSpa.ts +195 -0
  62. package/src/dashboardUrl.test.ts +46 -0
  63. package/src/dashboardUrl.ts +28 -0
  64. package/src/eventsHandler.test.ts +247 -0
  65. package/src/eventsHandler.ts +136 -0
  66. package/src/index.ts +26 -0
  67. package/src/mcp.ts +1407 -0
  68. package/src/mcpHttp.test.ts +101 -0
  69. package/src/mcpHttp.ts +88 -0
  70. package/src/openBrowser.test.ts +103 -0
  71. package/src/openBrowser.ts +81 -0
  72. package/src/remoteBridge.test.ts +119 -0
  73. package/src/remoteBridge.ts +404 -0
  74. package/src/replay.test.ts +271 -0
  75. package/src/replayCreate.ts +194 -0
  76. package/src/replayViewer.ts +173 -0
  77. package/src/sessionRouter.ts +116 -0
  78. package/src/store/JsonMemoryStore.test.ts +175 -0
  79. package/src/store/JsonMemoryStore.ts +128 -0
  80. package/src/store/JsonTaskStore.test.ts +212 -0
  81. package/src/store/JsonTaskStore.ts +59 -0
  82. package/src/store/JsonlStore.test.ts +1538 -0
  83. package/src/store/JsonlStore.ts +1321 -0
  84. package/src/store/MemoryEventStore.test.ts +119 -0
  85. package/src/store/MemoryEventStore.ts +151 -0
  86. package/src/store/WriteQueue.ts +165 -0
  87. package/src/store/index.ts +29 -0
  88. package/src/store/types.ts +517 -0
@@ -0,0 +1,517 @@
1
+ /**
2
+ * Store types — the public interface for the JSONL-based persistence layer.
3
+ *
4
+ * v0.4.0 layout (new, flat):
5
+ * {dataDir}/projects/{projectId}/meta.json
6
+ * {dataDir}/projects/{projectId}/tasks.json
7
+ * {dataDir}/projects/{projectId}/memory.json
8
+ * {dataDir}/projects/{projectId}/notes.jsonl
9
+ * {dataDir}/projects/{projectId}/builds/{buildId}/meta.json
10
+ * {dataDir}/tabs/{tabId}/meta.json
11
+ * {dataDir}/sessions/{sessionId}/meta.json ← one per pageload
12
+ * {dataDir}/sessions/{sessionId}/timeline.jsonl ← mixed parent+child events
13
+ * {dataDir}/sessions/{sessionId}/recording.jsonl ← rrweb chunks
14
+ * {dataDir}/visitors/{visitorId}/meta.json ← per-browser identity (0.5+)
15
+ *
16
+ * Legacy layout (v0.3.x, read-only fallback — daemon warns on startup):
17
+ * {dataDir}/{projectId}/sessions/{buildId}/tabs/{tabId}/...
18
+ */
19
+
20
+ import type { Task, VisitorEnv } from '@harness-fe/protocol';
21
+
22
+ // Re-export the MCP SDK's EventStore types so consumers (and embedders) can
23
+ // plug in custom resumable-stream backends without depending on the SDK
24
+ // directly. The default implementation is `MemoryEventStore`.
25
+ export type {
26
+ EventId,
27
+ EventStore,
28
+ StreamId,
29
+ } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
30
+
31
+ // ─── Event types ─────────────────────────────────────────────────────────────
32
+
33
+ /** Short type codes used in JSONL lines to keep files compact. */
34
+ export type EventType =
35
+ | 'log' // browser console
36
+ | 'err' // browser JS error
37
+ | 'req' // network request (start)
38
+ | 'res' // network response (end)
39
+ | 'cmd' // MCP command sent to runtime/plugin
40
+ | 'resp' // MCP command response
41
+ | 'hmr' // HMR update from build plugin
42
+ | 'task' // user annotation task submitted
43
+ | 'task:claim' // annotation task claimed by agent
44
+ | 'task:resolve' // annotation task resolved by agent
45
+ | 'rrweb' // rrweb recording chunk
46
+ | 'node:log' // Node.js stdout from build plugin
47
+ | 'node:err' // Node.js stderr from build plugin
48
+ | 'note' // project-level note written by agent/user
49
+ | 'load' // page-load initial snapshot
50
+ | 'storage' // localStorage/sessionStorage/cookie mutation
51
+ | 'server-log' // Node.js console log from node-runtime SDK
52
+ | 'server-err' // Node.js uncaughtException / unhandledRejection from node-runtime SDK
53
+ | 'server-action'// Route Handler / Server Action timing from withHarnessTracing()
54
+ | 'app-log' // Explicit log call via @harness-fe/log (user-initiated, distinct from auto-captured console)
55
+ | string; // extensible — future types don't need schema changes
56
+
57
+ /** A single event line in a JSONL file. Carries row-level projectId/buildId tags. */
58
+ export interface StoreEvent {
59
+ /**
60
+ * Server-assigned monotonic integer per session (assigned at enqueue time).
61
+ * Optional on input — the store layer assigns this.
62
+ * Always present on events returned by `tail` and `search`.
63
+ */
64
+ seq?: number;
65
+ /** Unix timestamp in milliseconds. */
66
+ ts: number;
67
+ /** Short event type code. */
68
+ t: EventType;
69
+ /** Tab ID — present for tab-scoped events. */
70
+ tab?: string;
71
+ /**
72
+ * Load/session ID on tab-scoped events. Kept for backward compat with
73
+ * v0.3.x event lines and bridge code that still stamps event.load.
74
+ */
75
+ load?: string;
76
+ /**
77
+ * Row-level project ID. Stamped by the bridge before calling appendEvent().
78
+ */
79
+ projectId?: string;
80
+ /**
81
+ * Row-level build ID. Stamped by the bridge.
82
+ */
83
+ buildId?: string;
84
+ /**
85
+ * Row-level visitor ID. Stamped by the bridge from the registered peer
86
+ * or the frame's own `visitorId`. Lets agents filter timeline by
87
+ * "everything from this user" without join lookups.
88
+ */
89
+ visitorId?: string;
90
+ /** Event payload — structure depends on `t`. */
91
+ d?: unknown;
92
+ }
93
+
94
+ // ─── Metadata shapes ─────────────────────────────────────────────────────────
95
+
96
+ export interface ProjectMeta {
97
+ id: string;
98
+ createdAt: number;
99
+ lastActiveAt: number;
100
+ parentProjectId?: string;
101
+ displayName?: string;
102
+ tags?: string[];
103
+ metadata?: Record<string, unknown>;
104
+ }
105
+
106
+ /**
107
+ * Per-build metadata. Lives at projects/{projectId}/builds/{buildId}/meta.json.
108
+ */
109
+ export interface BuildMeta {
110
+ id: string;
111
+ projectId: string;
112
+ builtAt: number;
113
+ gitSha?: string;
114
+ gitDirty?: boolean;
115
+ sourceDigest?: string;
116
+ nodeVersion?: string;
117
+ /** 'vite' | 'webpack' | 'esbuild' | 'rspack' | … */
118
+ bundler?: string;
119
+ bundlerVersion?: string;
120
+ /** Timestamp when this build's dev server was closed. */
121
+ endedAt?: number;
122
+ metadata?: Record<string, unknown>;
123
+ }
124
+
125
+ /**
126
+ * Node in a project tree returned by `getProjectTree`.
127
+ */
128
+ export interface ProjectTreeNode {
129
+ id: string;
130
+ displayName?: string;
131
+ tags?: string[];
132
+ children: ProjectTreeNode[];
133
+ }
134
+
135
+ /**
136
+ * Per-tab metadata. Lives at tabs/{tabId}/meta.json.
137
+ * A tab spans multiple sessions and may host multiple projects.
138
+ */
139
+ export interface TabMeta {
140
+ id: string;
141
+ userAgent?: string;
142
+ connectedAt: number;
143
+ disconnectedAt?: number;
144
+ metadata?: Record<string, unknown>;
145
+ }
146
+
147
+ /**
148
+ * Per-session (pageload) metadata. Lives at sessions/{sessionId}/meta.json.
149
+ * A session = one pageload. Multiple projects/iframes may participate
150
+ * (they share sessionId via tryInheritFromParent).
151
+ */
152
+ export interface SessionMeta {
153
+ /** sessionId generated by the runtime (shared across same-origin iframes). */
154
+ id: string;
155
+ tabId: string;
156
+ startedAt: number;
157
+ endedAt?: number;
158
+ url?: string;
159
+ title?: string;
160
+ referrer?: string;
161
+ userAgent?: string;
162
+ /**
163
+ * Every (projectId, buildId) pair that participated in this pageload.
164
+ * Merge semantics: new participants are appended on each upsertSession call.
165
+ */
166
+ participants: Array<{ projectId: string; buildId?: string; joinedAt: number }>;
167
+ initial?: {
168
+ viewport?: { w: number; h: number; dpr: number };
169
+ storageKeys?: { local?: number; session?: number; cookie?: number };
170
+ storageTruncated?: boolean;
171
+ };
172
+ metadata?: Record<string, unknown>;
173
+ }
174
+
175
+ /**
176
+ * Per-visitor identity. Lives at visitors/{visitorId}/meta.json. Stitches a
177
+ * user's activity across pageloads / refreshes / tabs.
178
+ */
179
+ export interface VisitorMeta {
180
+ /** visitorId — anonymous UUID persisted in browser localStorage. */
181
+ id: string;
182
+ /** App-supplied identifier (latest non-empty value wins). */
183
+ userId?: string;
184
+ firstSeenAt: number;
185
+ lastSeenAt: number;
186
+ /** Distinct sessions (pageloads) attributed to this visitor. */
187
+ sessionCount: number;
188
+ /** LRU-capped list of distinct tabIds seen (max 50). */
189
+ tabIds: string[];
190
+ /** Distinct projects this visitor has touched (max 50). */
191
+ projectIds: string[];
192
+ /** Last-seen environment snapshot. */
193
+ lastEnv?: VisitorEnv;
194
+ }
195
+
196
+ // ─── Query options ────────────────────────────────────────────────────────────
197
+
198
+ export interface TailOptions {
199
+ /** Number of lines to return from the end. Default 50. */
200
+ n?: number;
201
+ /** Filter by event type(s). */
202
+ type?: EventType | EventType[];
203
+ /** Only return events after this timestamp. */
204
+ since?: number;
205
+ /** Only return events before this timestamp. */
206
+ until?: number;
207
+ /** Filter by projectId (useful for multi-project session timelines). */
208
+ projectId?: string;
209
+ }
210
+
211
+ export interface SearchOptions {
212
+ /** Filter by event type(s). */
213
+ type?: EventType | EventType[];
214
+ /** Max results. Default 50. */
215
+ limit?: number;
216
+ }
217
+
218
+ export interface RecordingChunkSummary {
219
+ chunkId: string;
220
+ tabId: string;
221
+ startTs: number;
222
+ endTs: number;
223
+ eventCount: number;
224
+ }
225
+
226
+ export interface RecordingChunk extends RecordingChunkSummary {
227
+ events: unknown[];
228
+ }
229
+
230
+ /**
231
+ * Metadata for a saved replay export.
232
+ */
233
+ export interface ReplayExportMeta {
234
+ exportId: string;
235
+ projectId: string;
236
+ sessionId: string;
237
+ tabId?: string;
238
+ label?: string;
239
+ since: number;
240
+ until: number;
241
+ startTs: number;
242
+ endTs: number;
243
+ chunkCount: number;
244
+ eventCount: number;
245
+ bytes: number;
246
+ createdAt: number;
247
+ }
248
+
249
+ // ─── Summary ─────────────────────────────────────────────────────────────────
250
+
251
+ export interface SessionSummary {
252
+ session: SessionMeta;
253
+ counts: Partial<Record<EventType, number>>;
254
+ lastError?: StoreEvent;
255
+ lastActivity?: number;
256
+ tabs: string[];
257
+ }
258
+
259
+ // ─── Retention ───────────────────────────────────────────────────────────────
260
+
261
+ export interface RetentionPolicy {
262
+ /** Delete sessions older than this many days. Default 7. */
263
+ maxAgeDays?: number;
264
+ /** Keep at most this many sessions globally. Default 200. */
265
+ maxSessions?: number;
266
+ /** Delete recording.jsonl files older than this many days. Default 3. */
267
+ recordingRetentionDays?: number;
268
+ /** Keep at most this many recording chunks per session. */
269
+ maxRecordingChunksPerSession?: number;
270
+ /** Keep at most this many bytes of recording data per session. */
271
+ maxRecordingBytesPerSession?: number;
272
+ /** Prefer keeping chunks that overlap rrweb markers when trimming. */
273
+ preserveMarkedChunks?: boolean;
274
+ /** Keep at most this many replay exports per project. Default 50. */
275
+ maxExportsPerProject?: number;
276
+ /** Keep at most this many bytes of replay exports per project. Default 200MB. */
277
+ maxExportBytesPerProject?: number;
278
+ /** Keep at most this many BuildMeta records per project. Default 100. */
279
+ maxBuildsPerProject?: number;
280
+
281
+ // ─── Legacy aliases (v0.3.x backward compat for existing callers) ─────
282
+ /** @deprecated Use maxSessions. */
283
+ maxSessionsPerProject?: number;
284
+ /** @deprecated Use maxRecordingChunksPerSession. */
285
+ maxRecordingChunksPerTab?: number;
286
+ /** @deprecated Use maxRecordingBytesPerSession. */
287
+ maxRecordingBytesPerTab?: number;
288
+ }
289
+
290
+ export interface PurgeResult {
291
+ sessionsDeleted: number;
292
+ recordingsDeleted: number;
293
+ exportsDeleted: number;
294
+ buildsDeleted?: number;
295
+ bytesFreed: number;
296
+ }
297
+
298
+ // ─── Task store interface ─────────────────────────────────────────────────────
299
+
300
+ export interface ITaskStore {
301
+ loadTasks(projectId: string): Task[];
302
+ saveTasks(projectId: string, tasks: Task[]): void;
303
+ }
304
+
305
+ // ─── Memory store interface ───────────────────────────────────────────────────
306
+
307
+ export interface MemoryEntry {
308
+ key: string;
309
+ value: string;
310
+ updatedAt: number;
311
+ }
312
+
313
+ export interface IMemoryStore {
314
+ get(projectId: string, key: string): MemoryEntry | undefined;
315
+ set(projectId: string, key: string, value: string): MemoryEntry;
316
+ delete(projectId: string, key: string): boolean;
317
+ list(projectId: string): MemoryEntry[];
318
+ }
319
+
320
+ // ─── Store interface ──────────────────────────────────────────────────────────
321
+
322
+ export interface IStore {
323
+ // ── Build lifecycle ────────────────────────────────────────────────────
324
+
325
+ /**
326
+ * Open a new build (dev server start / prod build). Returns buildId.
327
+ * Writes projects/{projectId}/builds/{buildId}/meta.json.
328
+ * (Replaces openSession() from v0.3.x)
329
+ */
330
+ openBuild(projectId: string, patch?: Partial<Omit<BuildMeta, 'id' | 'projectId' | 'builtAt'>>): string;
331
+
332
+ /**
333
+ * Mark a build as ended.
334
+ * (Replaces closeSession() for build-plugin connections from v0.3.x)
335
+ */
336
+ closeBuild(buildId: string, closedAt?: number): void;
337
+
338
+ // ── Tab lifecycle ──────────────────────────────────────────────────────
339
+
340
+ /**
341
+ * Write or update tab metadata at tabs/{tabId}/meta.json.
342
+ * Merge semantics: caller-provided fields overwrite, others preserved.
343
+ */
344
+ upsertTab(tabId: string, patch: Partial<Omit<TabMeta, 'id'>>): TabMeta;
345
+
346
+ /** Get tab metadata. */
347
+ getTab(tabId: string): TabMeta | undefined;
348
+
349
+ /**
350
+ * Mark a tab as disconnected.
351
+ * New signature: (tabId, disconnectedAt?) — no sessionId param.
352
+ */
353
+ closeTab(tabId: string, disconnectedAt?: number): void;
354
+
355
+ // ── Session lifecycle (pageload) ───────────────────────────────────────
356
+
357
+ /**
358
+ * Open or update a session (one pageload). Writes sessions/{sessionId}/meta.json.
359
+ * participants list is extended (not replaced) on each call.
360
+ * (Replaces openLoad() from v0.3.x)
361
+ */
362
+ upsertSession(
363
+ sessionId: string,
364
+ meta: Partial<Omit<SessionMeta, 'id'>> & { tabId: string; startedAt: number },
365
+ ): SessionMeta;
366
+
367
+ /**
368
+ * Mark a session as ended.
369
+ * (Replaces closeLatestLoad() from v0.3.x)
370
+ */
371
+ closeSession(sessionId: string, endedAt?: number): void;
372
+
373
+ /** Get session metadata. */
374
+ getSession(sessionId: string): SessionMeta | undefined;
375
+
376
+ /**
377
+ * List sessions by recency.
378
+ * New signature: opts object with optional tabId / projectId / buildId / limit.
379
+ * (Replaces listSessions(projectId, limit?) from v0.3.x)
380
+ */
381
+ listSessions(opts?: { tabId?: string; projectId?: string; buildId?: string; limit?: number }): SessionMeta[];
382
+
383
+ // ── Write ──────────────────────────────────────────────────────────────
384
+
385
+ /**
386
+ * Append a single event to sessions/{sessionId}/timeline.jsonl.
387
+ * event.projectId and event.buildId should be pre-stamped by the bridge.
388
+ * (Replaces append(sessionId=buildId, event, tabId?) from v0.3.x)
389
+ */
390
+ appendEvent(sessionId: string, event: StoreEvent): void;
391
+
392
+ /**
393
+ * Append a batch of events.
394
+ * (Replaces appendBatch() from v0.3.x)
395
+ */
396
+ appendEventBatch(sessionId: string, events: StoreEvent[]): void;
397
+
398
+ /**
399
+ * Append an rrweb recording chunk to sessions/{sessionId}/recording.jsonl.
400
+ * (Replaces appendRecording(sessionId, tabId, chunk, loadId?) from v0.3.x)
401
+ */
402
+ appendRecording(sessionId: string, chunk: unknown): void;
403
+
404
+ /** Write a project-level note. */
405
+ writeNote(projectId: string, key: string, value: string): void;
406
+
407
+ // ── Project metadata ───────────────────────────────────────────────────
408
+
409
+ /**
410
+ * Upsert project metadata. `id` and `createdAt` are never overwritten.
411
+ * Throws if `patch.parentProjectId` would create a cycle.
412
+ */
413
+ upsertProject(projectId: string, patch: Partial<Omit<ProjectMeta, 'id' | 'createdAt'>>): ProjectMeta;
414
+
415
+ /** Read a single project's metadata. */
416
+ getProject(projectId: string): ProjectMeta | undefined;
417
+
418
+ /** List all known projects. */
419
+ listProjects(): ProjectMeta[];
420
+
421
+ // ── Build metadata ─────────────────────────────────────────────────────
422
+
423
+ /** Upsert build metadata. Creates the project dir if missing. */
424
+ upsertBuild(projectId: string, buildId: string, patch: Partial<Omit<BuildMeta, 'id' | 'projectId'>>): BuildMeta;
425
+
426
+ /** Read a single build's metadata. */
427
+ getBuild(projectId: string, buildId: string): BuildMeta | undefined;
428
+
429
+ /** List builds for a project, newest first. */
430
+ listBuilds(projectId: string, limit?: number): BuildMeta[];
431
+
432
+ // ── Project tree ───────────────────────────────────────────────────────
433
+
434
+ /** Get a forest (or sub-tree from `rootId`) from parentProjectId links. */
435
+ getProjectTree(rootId?: string): ProjectTreeNode[];
436
+
437
+ // ── Visitor metadata (0.5+) ─────────────────────────────────────────────
438
+
439
+ /**
440
+ * Upsert visitor metadata. Merges with existing meta:
441
+ * - `firstSeenAt` preserved; `lastSeenAt` advances
442
+ * - `sessionCount` increments when caller passes `incrementSession: true`
443
+ * - `tabIds` / `projectIds` deduped and LRU-capped at 50
444
+ * - `userId` overwritten if patch carries a non-empty value
445
+ * - `lastEnv` overwritten if patch carries one
446
+ */
447
+ upsertVisitor(
448
+ visitorId: string,
449
+ patch: {
450
+ userId?: string;
451
+ seenAt?: number;
452
+ incrementSession?: boolean;
453
+ addTabId?: string;
454
+ addProjectId?: string;
455
+ lastEnv?: VisitorEnv;
456
+ },
457
+ ): VisitorMeta;
458
+
459
+ /** Read a single visitor's metadata. */
460
+ getVisitor(visitorId: string): VisitorMeta | undefined;
461
+
462
+ /** List known visitors, newest lastSeenAt first. */
463
+ listVisitors(opts?: { projectId?: string; limit?: number }): VisitorMeta[];
464
+
465
+ // ── Read ───────────────────────────────────────────────────────────────
466
+
467
+ /**
468
+ * Read the last N events from a session timeline.
469
+ * New signature: no tabId param (tab is implicit per session).
470
+ */
471
+ tail(sessionId: string, opts?: TailOptions): StoreEvent[];
472
+
473
+ /** Search events in a session timeline by substring match. */
474
+ search(sessionId: string, query: string, opts?: SearchOptions): StoreEvent[];
475
+
476
+ /** List recording chunks for a session. */
477
+ listRecordings(sessionId: string): RecordingChunkSummary[];
478
+
479
+ /** Return recording chunks overlapping the requested time window. */
480
+ sliceRecordings(sessionId: string, since: number, until: number): RecordingChunk[];
481
+
482
+ /** Persist a replay export. */
483
+ writeExport(input: {
484
+ sessionId: string;
485
+ tabId?: string;
486
+ since: number;
487
+ until: number;
488
+ label?: string;
489
+ events: unknown[];
490
+ startTs: number;
491
+ endTs: number;
492
+ chunkCount: number;
493
+ }): ReplayExportMeta;
494
+
495
+ /** Read export metadata by id. */
496
+ getExport(exportId: string): ReplayExportMeta | undefined;
497
+
498
+ /** Read the raw events array for an export. */
499
+ readExportEvents(exportId: string): unknown[] | undefined;
500
+
501
+ /** List exports for a project, newest first. */
502
+ listExports(projectId: string, limit?: number): ReplayExportMeta[];
503
+
504
+ /** Get a summary of a session (counts, last error, etc.). */
505
+ summary(sessionId: string): SessionSummary;
506
+
507
+ /** Read project notes. */
508
+ listNotes(projectId: string): Array<{ key: string; value: string; ts: number }>;
509
+
510
+ // ── Maintenance ────────────────────────────────────────────────────────
511
+
512
+ /** Delete old sessions and recordings according to retention policy. */
513
+ purge(policy?: RetentionPolicy): PurgeResult;
514
+
515
+ /** Close any open file handles. */
516
+ close(): void | Promise<void>;
517
+ }