@vibecheckai/cli 3.2.2 → 3.2.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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,263 +1,263 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Replay Module
|
|
3
|
-
*
|
|
4
|
-
* Provides a unified API for recording and replaying user interactions
|
|
5
|
-
* with the ability to verify behavior and detect regressions.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const fs = require('fs').promises;
|
|
10
|
-
const debug = require('debug')('vibecheck:replay');
|
|
11
|
-
|
|
12
|
-
// Core components
|
|
13
|
-
const CapsuleManager = require('./capsule-manager');
|
|
14
|
-
const ReplayRecorder = require('./recorder');
|
|
15
|
-
const ReplayPlayer = require('./player');
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Main Replay API
|
|
19
|
-
*/
|
|
20
|
-
class ReplayEngine {
|
|
21
|
-
constructor(options = {}) {
|
|
22
|
-
this.options = {
|
|
23
|
-
basePath: process.cwd(),
|
|
24
|
-
...options
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
this.manager = new CapsuleManager(this.options.basePath);
|
|
28
|
-
this.recorder = null;
|
|
29
|
-
this.player = null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Initialize the replay system
|
|
34
|
-
*/
|
|
35
|
-
async init() {
|
|
36
|
-
await this.manager.init();
|
|
37
|
-
debug('Replay engine initialized');
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Start a new recording session
|
|
43
|
-
*/
|
|
44
|
-
startRecording(options = {}) {
|
|
45
|
-
if (this.recorder) {
|
|
46
|
-
throw new Error('Recording already in progress');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
this.recorder = new ReplayRecorder({
|
|
50
|
-
...options,
|
|
51
|
-
basePath: this.options.basePath
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
this.recorder.start();
|
|
55
|
-
debug('Started new recording session');
|
|
56
|
-
|
|
57
|
-
return this.recorder;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Stop the current recording and save as a capsule
|
|
62
|
-
*/
|
|
63
|
-
async stopRecording(saveOptions = {}) {
|
|
64
|
-
if (!this.recorder) {
|
|
65
|
-
throw new Error('No active recording to stop');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const result = this.recorder.stop();
|
|
69
|
-
const { id, path: capsulePath } = await this.recorder.save(this.options.basePath);
|
|
70
|
-
|
|
71
|
-
debug(`Recording saved as capsule: ${id} (${capsulePath})`);
|
|
72
|
-
|
|
73
|
-
this.recorder = null;
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
id,
|
|
77
|
-
path: capsulePath,
|
|
78
|
-
metadata: result.metadata
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* List available replay capsules
|
|
84
|
-
*/
|
|
85
|
-
async listCapsules() {
|
|
86
|
-
return this.manager.listCapsules();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get a replay capsule by ID or path
|
|
91
|
-
*/
|
|
92
|
-
async getCapsule(idOrPath) {
|
|
93
|
-
return this.manager.loadCapsule(idOrPath);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Delete a replay capsule
|
|
98
|
-
*/
|
|
99
|
-
async deleteCapsule(idOrPath) {
|
|
100
|
-
return this.manager.deleteCapsule(idOrPath);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Export a capsule to a portable format
|
|
105
|
-
*/
|
|
106
|
-
async exportCapsule(idOrPath, outputPath) {
|
|
107
|
-
return this.manager.exportCapsule(idOrPath, outputPath);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Import a capsule from a file
|
|
112
|
-
*/
|
|
113
|
-
async importCapsule(filePath) {
|
|
114
|
-
return this.manager.importCapsule(filePath);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Create a new replay player for a capsule
|
|
119
|
-
*/
|
|
120
|
-
createPlayer(options = {}) {
|
|
121
|
-
if (this.player) {
|
|
122
|
-
throw new Error('A player is already active');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.player = new ReplayPlayer({
|
|
126
|
-
...this.options,
|
|
127
|
-
...options
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return this.player;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Play a replay capsule
|
|
135
|
-
*/
|
|
136
|
-
async playCapsule(capsule, options = {}) {
|
|
137
|
-
const player = this.createPlayer(options);
|
|
138
|
-
await player.loadCapsule(capsule);
|
|
139
|
-
|
|
140
|
-
if (options.browser) {
|
|
141
|
-
await player.setupPlaywright(options.browser);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return player.play();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Create a Playwright recorder
|
|
149
|
-
*/
|
|
150
|
-
createPlaywrightRecorder(page, options = {}) {
|
|
151
|
-
return ReplayRecorder.fromPlaywright(page, {
|
|
152
|
-
...this.options,
|
|
153
|
-
...options
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Clean up resources
|
|
159
|
-
*/
|
|
160
|
-
async cleanup() {
|
|
161
|
-
if (this.player) {
|
|
162
|
-
await this.player.close().catch(console.error);
|
|
163
|
-
this.player = null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (this.recorder) {
|
|
167
|
-
this.recorder.stop();
|
|
168
|
-
this.recorder = null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Export everything
|
|
174
|
-
module.exports = {
|
|
175
|
-
// Main API
|
|
176
|
-
ReplayEngine,
|
|
177
|
-
|
|
178
|
-
// Core components
|
|
179
|
-
CapsuleManager,
|
|
180
|
-
ReplayRecorder,
|
|
181
|
-
ReplayPlayer,
|
|
182
|
-
|
|
183
|
-
// Factory function for quick setup
|
|
184
|
-
createReplayEngine: async (options = {}) => {
|
|
185
|
-
const engine = new ReplayEngine(options);
|
|
186
|
-
await engine.init();
|
|
187
|
-
return engine;
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
// Utility functions
|
|
191
|
-
utils: {
|
|
192
|
-
/**
|
|
193
|
-
* Generate a human-readable report from replay results
|
|
194
|
-
*/
|
|
195
|
-
generateReport: (results) => {
|
|
196
|
-
const { metadata, stats } = results;
|
|
197
|
-
const { name, description, duration, steps } = metadata;
|
|
198
|
-
const { passed, failed, skipped } = stats;
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
title: `Replay Report: ${name}`,
|
|
202
|
-
summary: {
|
|
203
|
-
description,
|
|
204
|
-
duration: `${(duration / 1000).toFixed(2)}s`,
|
|
205
|
-
totalSteps: steps,
|
|
206
|
-
passed,
|
|
207
|
-
failed,
|
|
208
|
-
skipped,
|
|
209
|
-
success: failed === 0
|
|
210
|
-
},
|
|
211
|
-
timestamp: new Date().toISOString(),
|
|
212
|
-
details: results.steps ? results.steps.map(step => ({
|
|
213
|
-
id: step.id,
|
|
214
|
-
type: step.type,
|
|
215
|
-
status: step.error ? 'failed' : 'passed',
|
|
216
|
-
duration: step.duration ? `${step.duration.toFixed(2)}ms` : 'N/A',
|
|
217
|
-
error: step.error ? step.error.message : null
|
|
218
|
-
})) : []
|
|
219
|
-
};
|
|
220
|
-
},
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Compare two replay results to detect regressions
|
|
224
|
-
*/
|
|
225
|
-
compareResults: (baseline, current) => {
|
|
226
|
-
const regressions = [];
|
|
227
|
-
|
|
228
|
-
// Check step counts
|
|
229
|
-
if (baseline.stats.totalSteps !== current.stats.totalSteps) {
|
|
230
|
-
regressions.push({
|
|
231
|
-
type: 'step_count_mismatch',
|
|
232
|
-
message: `Different number of steps: ${baseline.stats.totalSteps} vs ${current.stats.totalSteps}`
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Check for new failures
|
|
237
|
-
if (current.stats.failed > baseline.stats.failed) {
|
|
238
|
-
regressions.push({
|
|
239
|
-
type: 'new_failures',
|
|
240
|
-
message: `New failures: ${current.stats.failed - baseline.stats.failed} more than baseline`
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Check for performance regressions (if timing data is available)
|
|
245
|
-
if (baseline.metadata.duration && current.metadata.duration) {
|
|
246
|
-
const threshold = baseline.metadata.duration * 1.5; // 50% slower
|
|
247
|
-
if (current.metadata.duration > threshold) {
|
|
248
|
-
regressions.push({
|
|
249
|
-
type: 'performance_regression',
|
|
250
|
-
message: `Performance regression: ${(current.metadata.duration / 1000).toFixed(2)}s vs baseline ${(baseline.metadata.duration / 1000).toFixed(2)}s`
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return {
|
|
256
|
-
hasRegressions: regressions.length > 0,
|
|
257
|
-
regressions,
|
|
258
|
-
baseline: baseline.stats,
|
|
259
|
-
current: current.stats
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Replay Module
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified API for recording and replaying user interactions
|
|
5
|
+
* with the ability to verify behavior and detect regressions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs').promises;
|
|
10
|
+
const debug = require('debug')('vibecheck:replay');
|
|
11
|
+
|
|
12
|
+
// Core components
|
|
13
|
+
const CapsuleManager = require('./capsule-manager');
|
|
14
|
+
const ReplayRecorder = require('./recorder');
|
|
15
|
+
const ReplayPlayer = require('./player');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Main Replay API
|
|
19
|
+
*/
|
|
20
|
+
class ReplayEngine {
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.options = {
|
|
23
|
+
basePath: process.cwd(),
|
|
24
|
+
...options
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.manager = new CapsuleManager(this.options.basePath);
|
|
28
|
+
this.recorder = null;
|
|
29
|
+
this.player = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the replay system
|
|
34
|
+
*/
|
|
35
|
+
async init() {
|
|
36
|
+
await this.manager.init();
|
|
37
|
+
debug('Replay engine initialized');
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Start a new recording session
|
|
43
|
+
*/
|
|
44
|
+
startRecording(options = {}) {
|
|
45
|
+
if (this.recorder) {
|
|
46
|
+
throw new Error('Recording already in progress');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.recorder = new ReplayRecorder({
|
|
50
|
+
...options,
|
|
51
|
+
basePath: this.options.basePath
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.recorder.start();
|
|
55
|
+
debug('Started new recording session');
|
|
56
|
+
|
|
57
|
+
return this.recorder;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Stop the current recording and save as a capsule
|
|
62
|
+
*/
|
|
63
|
+
async stopRecording(saveOptions = {}) {
|
|
64
|
+
if (!this.recorder) {
|
|
65
|
+
throw new Error('No active recording to stop');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const result = this.recorder.stop();
|
|
69
|
+
const { id, path: capsulePath } = await this.recorder.save(this.options.basePath);
|
|
70
|
+
|
|
71
|
+
debug(`Recording saved as capsule: ${id} (${capsulePath})`);
|
|
72
|
+
|
|
73
|
+
this.recorder = null;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
id,
|
|
77
|
+
path: capsulePath,
|
|
78
|
+
metadata: result.metadata
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* List available replay capsules
|
|
84
|
+
*/
|
|
85
|
+
async listCapsules() {
|
|
86
|
+
return this.manager.listCapsules();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get a replay capsule by ID or path
|
|
91
|
+
*/
|
|
92
|
+
async getCapsule(idOrPath) {
|
|
93
|
+
return this.manager.loadCapsule(idOrPath);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Delete a replay capsule
|
|
98
|
+
*/
|
|
99
|
+
async deleteCapsule(idOrPath) {
|
|
100
|
+
return this.manager.deleteCapsule(idOrPath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Export a capsule to a portable format
|
|
105
|
+
*/
|
|
106
|
+
async exportCapsule(idOrPath, outputPath) {
|
|
107
|
+
return this.manager.exportCapsule(idOrPath, outputPath);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Import a capsule from a file
|
|
112
|
+
*/
|
|
113
|
+
async importCapsule(filePath) {
|
|
114
|
+
return this.manager.importCapsule(filePath);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a new replay player for a capsule
|
|
119
|
+
*/
|
|
120
|
+
createPlayer(options = {}) {
|
|
121
|
+
if (this.player) {
|
|
122
|
+
throw new Error('A player is already active');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.player = new ReplayPlayer({
|
|
126
|
+
...this.options,
|
|
127
|
+
...options
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return this.player;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Play a replay capsule
|
|
135
|
+
*/
|
|
136
|
+
async playCapsule(capsule, options = {}) {
|
|
137
|
+
const player = this.createPlayer(options);
|
|
138
|
+
await player.loadCapsule(capsule);
|
|
139
|
+
|
|
140
|
+
if (options.browser) {
|
|
141
|
+
await player.setupPlaywright(options.browser);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return player.play();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a Playwright recorder
|
|
149
|
+
*/
|
|
150
|
+
createPlaywrightRecorder(page, options = {}) {
|
|
151
|
+
return ReplayRecorder.fromPlaywright(page, {
|
|
152
|
+
...this.options,
|
|
153
|
+
...options
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Clean up resources
|
|
159
|
+
*/
|
|
160
|
+
async cleanup() {
|
|
161
|
+
if (this.player) {
|
|
162
|
+
await this.player.close().catch(console.error);
|
|
163
|
+
this.player = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (this.recorder) {
|
|
167
|
+
this.recorder.stop();
|
|
168
|
+
this.recorder = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Export everything
|
|
174
|
+
module.exports = {
|
|
175
|
+
// Main API
|
|
176
|
+
ReplayEngine,
|
|
177
|
+
|
|
178
|
+
// Core components
|
|
179
|
+
CapsuleManager,
|
|
180
|
+
ReplayRecorder,
|
|
181
|
+
ReplayPlayer,
|
|
182
|
+
|
|
183
|
+
// Factory function for quick setup
|
|
184
|
+
createReplayEngine: async (options = {}) => {
|
|
185
|
+
const engine = new ReplayEngine(options);
|
|
186
|
+
await engine.init();
|
|
187
|
+
return engine;
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// Utility functions
|
|
191
|
+
utils: {
|
|
192
|
+
/**
|
|
193
|
+
* Generate a human-readable report from replay results
|
|
194
|
+
*/
|
|
195
|
+
generateReport: (results) => {
|
|
196
|
+
const { metadata, stats } = results;
|
|
197
|
+
const { name, description, duration, steps } = metadata;
|
|
198
|
+
const { passed, failed, skipped } = stats;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
title: `Replay Report: ${name}`,
|
|
202
|
+
summary: {
|
|
203
|
+
description,
|
|
204
|
+
duration: `${(duration / 1000).toFixed(2)}s`,
|
|
205
|
+
totalSteps: steps,
|
|
206
|
+
passed,
|
|
207
|
+
failed,
|
|
208
|
+
skipped,
|
|
209
|
+
success: failed === 0
|
|
210
|
+
},
|
|
211
|
+
timestamp: new Date().toISOString(),
|
|
212
|
+
details: results.steps ? results.steps.map(step => ({
|
|
213
|
+
id: step.id,
|
|
214
|
+
type: step.type,
|
|
215
|
+
status: step.error ? 'failed' : 'passed',
|
|
216
|
+
duration: step.duration ? `${step.duration.toFixed(2)}ms` : 'N/A',
|
|
217
|
+
error: step.error ? step.error.message : null
|
|
218
|
+
})) : []
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Compare two replay results to detect regressions
|
|
224
|
+
*/
|
|
225
|
+
compareResults: (baseline, current) => {
|
|
226
|
+
const regressions = [];
|
|
227
|
+
|
|
228
|
+
// Check step counts
|
|
229
|
+
if (baseline.stats.totalSteps !== current.stats.totalSteps) {
|
|
230
|
+
regressions.push({
|
|
231
|
+
type: 'step_count_mismatch',
|
|
232
|
+
message: `Different number of steps: ${baseline.stats.totalSteps} vs ${current.stats.totalSteps}`
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check for new failures
|
|
237
|
+
if (current.stats.failed > baseline.stats.failed) {
|
|
238
|
+
regressions.push({
|
|
239
|
+
type: 'new_failures',
|
|
240
|
+
message: `New failures: ${current.stats.failed - baseline.stats.failed} more than baseline`
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check for performance regressions (if timing data is available)
|
|
245
|
+
if (baseline.metadata.duration && current.metadata.duration) {
|
|
246
|
+
const threshold = baseline.metadata.duration * 1.5; // 50% slower
|
|
247
|
+
if (current.metadata.duration > threshold) {
|
|
248
|
+
regressions.push({
|
|
249
|
+
type: 'performance_regression',
|
|
250
|
+
message: `Performance regression: ${(current.metadata.duration / 1000).toFixed(2)}s vs baseline ${(baseline.metadata.duration / 1000).toFixed(2)}s`
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
hasRegressions: regressions.length > 0,
|
|
257
|
+
regressions,
|
|
258
|
+
baseline: baseline.stats,
|
|
259
|
+
current: current.stats
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|