@vibecheckai/cli 3.1.6 → 3.2.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 +27 -32
- package/bin/registry.js +208 -343
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +2 -3
- package/bin/runners/runDoctor.js +11 -4
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +37 -20
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +861 -107
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +25 -5
- package/bin/vibecheck.js +35 -47
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/package.json +1 -1
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runBadge.js +0 -916
- package/bin/runners/runContracts.js +0 -105
- package/bin/runners/runCtx.js +0 -680
- package/bin/runners/runCtxDiff.js +0 -301
- package/bin/runners/runCtxGuard.js +0 -176
- package/bin/runners/runCtxSync.js +0 -116
- package/bin/runners/runExport.js +0 -93
- package/bin/runners/runGraph.js +0 -454
- package/bin/runners/runInstall.js +0 -273
- package/bin/runners/runLabs.js +0 -341
- package/bin/runners/runLaunch.js +0 -181
- package/bin/runners/runPR.js +0 -255
- package/bin/runners/runPermissions.js +0 -310
- package/bin/runners/runPreflight.js +0 -580
- package/bin/runners/runReplay.js +0 -499
- package/bin/runners/runSecurity.js +0 -92
- package/bin/runners/runShare.js +0 -212
- package/bin/runners/runStatus.js +0 -102
- package/bin/runners/runVerify.js +0 -272
package/bin/runners/runReplay.js
DELETED
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vibecheck Replay CLI
|
|
3
|
-
*
|
|
4
|
-
* Commands:
|
|
5
|
-
* vibecheck replay record <url> [options] Record a user session
|
|
6
|
-
* vibecheck replay play <capsule> Replay a recorded session
|
|
7
|
-
* vibecheck replay list List available replay capsules
|
|
8
|
-
* vibecheck replay show <id> Show details of a replay capsule
|
|
9
|
-
* vibecheck replay delete <id> Delete a replay capsule
|
|
10
|
-
* vibecheck replay export <id> <file> Export a replay capsule
|
|
11
|
-
* vibecheck replay import <file> Import a replay capsule
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { Command } = require('commander');
|
|
15
|
-
const path = require('path');
|
|
16
|
-
|
|
17
|
-
// Lazy-load heavy dependencies
|
|
18
|
-
let chalk, fs, chromium, createReplayEngine, version;
|
|
19
|
-
|
|
20
|
-
// Global options
|
|
21
|
-
let verbose = false;
|
|
22
|
-
let outputDir = path.join(process.cwd(), '.vibecheck', 'replays');
|
|
23
|
-
|
|
24
|
-
// Initialize replay engine
|
|
25
|
-
let replayEngine;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Load heavy dependencies on demand
|
|
29
|
-
*/
|
|
30
|
-
function loadDeps() {
|
|
31
|
-
if (!chalk) {
|
|
32
|
-
chalk = require('chalk');
|
|
33
|
-
fs = require('fs').promises;
|
|
34
|
-
chromium = require('playwright').chromium;
|
|
35
|
-
createReplayEngine = require('./lib/replay').createReplayEngine;
|
|
36
|
-
try {
|
|
37
|
-
version = require('../../package.json').version;
|
|
38
|
-
} catch {
|
|
39
|
-
version = '0.0.0';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Initialize the replay engine
|
|
46
|
-
*/
|
|
47
|
-
async function initEngine() {
|
|
48
|
-
loadDeps();
|
|
49
|
-
if (!replayEngine) {
|
|
50
|
-
replayEngine = await createReplayEngine({
|
|
51
|
-
basePath: process.cwd(),
|
|
52
|
-
verbose
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return replayEngine;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Record a user session
|
|
60
|
-
*/
|
|
61
|
-
async function recordSession(url, options) {
|
|
62
|
-
loadDeps();
|
|
63
|
-
console.log(chalk.blue(`\n🚀 Starting recording session for ${url}\n`));
|
|
64
|
-
|
|
65
|
-
const browser = await chromium.launch({
|
|
66
|
-
headless: !options.headed,
|
|
67
|
-
devtools: options.devtools
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const context = await browser.newContext({
|
|
72
|
-
viewport: options.viewport || { width: 1280, height: 800 },
|
|
73
|
-
recordVideo: options.video ? { dir: path.join(outputDir, 'videos') } : undefined
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const page = await context.newPage();
|
|
77
|
-
|
|
78
|
-
// Initialize recorder
|
|
79
|
-
const engine = await initEngine();
|
|
80
|
-
const recorder = engine.createPlaywrightRecorder(page, {
|
|
81
|
-
name: options.name || `Session at ${new Date().toLocaleString()}`,
|
|
82
|
-
description: options.description || `Recorded from ${url}`,
|
|
83
|
-
tags: options.tags ? options.tags.split(',') : []
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Set up event listeners
|
|
87
|
-
recorder.on('start', () => {
|
|
88
|
-
console.log(chalk.green('\n🔴 Recording started. Press Ctrl+C to stop.\n'));
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
recorder.on('step', (step) => {
|
|
92
|
-
if (verbose) {
|
|
93
|
-
console.log(chalk.gray(` [${step.type}] ${JSON.stringify(step).slice(0, 100)}...`));
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Navigate to the URL
|
|
98
|
-
await page.goto(url, { waitUntil: 'networkidle' });
|
|
99
|
-
|
|
100
|
-
// Wait for user to stop recording
|
|
101
|
-
console.log(chalk.blue('\n🛑 Press Ctrl+C when done recording...'));
|
|
102
|
-
|
|
103
|
-
// Handle process termination
|
|
104
|
-
const stopRecording = async () => {
|
|
105
|
-
console.log('\n\nStopping recording...');
|
|
106
|
-
const result = await recorder.save(outputDir);
|
|
107
|
-
console.log(chalk.green(`\n✅ Recording saved as capsule: ${result.id}`));
|
|
108
|
-
console.log(chalk.gray(`Path: ${result.path}`));
|
|
109
|
-
|
|
110
|
-
await browser.close();
|
|
111
|
-
process.exit(0);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
process.on('SIGINT', stopRecording);
|
|
115
|
-
|
|
116
|
-
// If auto-stop is enabled, wait for the specified duration
|
|
117
|
-
if (options.duration) {
|
|
118
|
-
console.log(`Will auto-stop after ${options.duration} seconds...`);
|
|
119
|
-
setTimeout(stopRecording, options.duration * 1000);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
} catch (error) {
|
|
123
|
-
console.error(chalk.red('\n❌ Error during recording:'), error);
|
|
124
|
-
await browser.close();
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Replay a recorded session
|
|
131
|
-
*/
|
|
132
|
-
async function replaySession(capsuleId, options) {
|
|
133
|
-
loadDeps();
|
|
134
|
-
console.log(chalk.blue(`\n▶️ Replaying capsule: ${capsuleId}\n`));
|
|
135
|
-
|
|
136
|
-
const browser = await chromium.launch({
|
|
137
|
-
headless: !options.headed,
|
|
138
|
-
devtools: options.devtools
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
const engine = await initEngine();
|
|
143
|
-
const capsule = await engine.getCapsule(capsuleId);
|
|
144
|
-
|
|
145
|
-
if (!capsule) {
|
|
146
|
-
console.error(chalk.red(`\n❌ Capsule not found: ${capsuleId}`));
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
console.log(chalk.blue(`📼 ${capsule.metadata.name || 'Untitled Session'}`));
|
|
151
|
-
if (capsule.metadata.description) {
|
|
152
|
-
console.log(chalk.gray(` ${capsule.metadata.description}`));
|
|
153
|
-
}
|
|
154
|
-
console.log(chalk.gray(` ${capsule.metadata.steps} steps, recorded ${new Date(capsule.metadata.createdAt).toLocaleString()}\n`));
|
|
155
|
-
|
|
156
|
-
const player = engine.createPlayer({
|
|
157
|
-
speed: options.speed,
|
|
158
|
-
headless: !options.headed,
|
|
159
|
-
waitForNetworkIdle: true,
|
|
160
|
-
stopOnFailure: options.stopOnFailure
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
await player.loadCapsule(capsule);
|
|
164
|
-
await player.setupPlaywright(browser);
|
|
165
|
-
|
|
166
|
-
// Set up event listeners
|
|
167
|
-
player.on('stepStart', ({ step, total, type }) => {
|
|
168
|
-
process.stdout.write(chalk.blue(`\r[${step}/${total}] ${type}...`));
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
player.on('stepPass', ({ step, type, duration }) => {
|
|
172
|
-
process.stdout.write(chalk.green(`\r[${step}] ${type} ✓ (${duration.toFixed(0)}ms)\n`));
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
player.on('stepFail', ({ step, type, error, duration }) => {
|
|
176
|
-
process.stdout.write(chalk.red(`\r[${step}] ${type} ✗ (${duration.toFixed(0)}ms)\n`));
|
|
177
|
-
if (verbose) {
|
|
178
|
-
console.error(chalk.red(` Error: ${error.message}`));
|
|
179
|
-
if (error.stack) {
|
|
180
|
-
console.error(chalk.gray(error.stack.split('\n').slice(1).join('\n')));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
player.on('complete', (result) => {
|
|
186
|
-
console.log('\n');
|
|
187
|
-
console.log(chalk.bold('🏁 Replay Complete'));
|
|
188
|
-
console.log(chalk.gray('━'.repeat(40)));
|
|
189
|
-
console.log(` Steps: ${result.passed + result.failed + result.skipped} total`);
|
|
190
|
-
console.log(` ${chalk.green(`✓ ${result.passed} passed`)}`);
|
|
191
|
-
if (result.failed > 0) {
|
|
192
|
-
console.log(` ${chalk.red(`✗ ${result.failed} failed`)}`);
|
|
193
|
-
}
|
|
194
|
-
if (result.skipped > 0) {
|
|
195
|
-
console.log(` ${chalk.yellow(`↷ ${result.skipped} skipped`)}`);
|
|
196
|
-
}
|
|
197
|
-
console.log(`\n Duration: ${(result.duration / 1000).toFixed(2)}s`);
|
|
198
|
-
console.log(chalk.gray('━'.repeat(40) + '\n'));
|
|
199
|
-
|
|
200
|
-
if (result.failed > 0) {
|
|
201
|
-
console.log(chalk.red('❌ Some steps failed during replay'));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
} else {
|
|
204
|
-
console.log(chalk.green('✅ Replay completed successfully'));
|
|
205
|
-
process.exit(0);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Start playback
|
|
210
|
-
await player.play();
|
|
211
|
-
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error(chalk.red('\n❌ Error during replay:'), error);
|
|
214
|
-
await browser.close();
|
|
215
|
-
process.exit(1);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* List available replay capsules
|
|
221
|
-
*/
|
|
222
|
-
async function listCapsules() {
|
|
223
|
-
try {
|
|
224
|
-
const engine = await initEngine();
|
|
225
|
-
const capsules = await engine.listCapsules();
|
|
226
|
-
|
|
227
|
-
if (capsules.length === 0) {
|
|
228
|
-
console.log(chalk.yellow('\nNo replay capsules found.\n'));
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
console.log(chalk.blue('\n📼 Available Replay Capsules\n'));
|
|
233
|
-
|
|
234
|
-
capsules.forEach((capsule, index) => {
|
|
235
|
-
const { id, name, timestamp, metadata } = capsule;
|
|
236
|
-
const date = new Date(timestamp).toLocaleString();
|
|
237
|
-
const steps = metadata.steps || '?';
|
|
238
|
-
|
|
239
|
-
console.log(chalk.bold(`${index + 1}. ${name || 'Untitled Session'}`));
|
|
240
|
-
console.log(` ID: ${chalk.gray(id)}`);
|
|
241
|
-
console.log(` Steps: ${chalk.cyan(steps)}`);
|
|
242
|
-
console.log(` Recorded: ${chalk.gray(date)}`);
|
|
243
|
-
|
|
244
|
-
if (metadata.tags && metadata.tags.length > 0) {
|
|
245
|
-
console.log(` Tags: ${metadata.tags.map(t => chalk.cyan(`#${t}`)).join(' ')}`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (metadata.description) {
|
|
249
|
-
console.log(` ${chalk.gray(metadata.description)}`);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
console.log();
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
} catch (error) {
|
|
256
|
-
console.error(chalk.red('\n❌ Error listing capsules:'), error);
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Show details of a specific capsule
|
|
263
|
-
*/
|
|
264
|
-
async function showCapsule(capsuleId) {
|
|
265
|
-
try {
|
|
266
|
-
const engine = await initEngine();
|
|
267
|
-
const capsule = await engine.getCapsule(capsuleId);
|
|
268
|
-
|
|
269
|
-
if (!capsule) {
|
|
270
|
-
console.error(chalk.red(`\n❌ Capsule not found: ${capsuleId}`));
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const { metadata, steps } = capsule;
|
|
275
|
-
|
|
276
|
-
console.log(chalk.blue('\n📼 Replay Capsule Details\n'));
|
|
277
|
-
console.log(chalk.bold(metadata.name || 'Untitled Session'));
|
|
278
|
-
console.log(chalk.gray('━'.repeat(60)));
|
|
279
|
-
|
|
280
|
-
// Basic info
|
|
281
|
-
console.log(chalk.bold('\n📋 Metadata'));
|
|
282
|
-
console.log(` ID: ${chalk.gray(metadata.id)}`);
|
|
283
|
-
console.log(` Recorded: ${chalk.gray(new Date(metadata.createdAt).toLocaleString())}`);
|
|
284
|
-
console.log(` Duration: ${chalk.cyan((metadata.duration / 1000).toFixed(2))}s`);
|
|
285
|
-
console.log(` Steps: ${chalk.cyan(steps.length)}`);
|
|
286
|
-
|
|
287
|
-
if (metadata.tags && metadata.tags.length > 0) {
|
|
288
|
-
console.log(` Tags: ${metadata.tags.map(t => chalk.cyan(`#${t}`)).join(' ')}`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (metadata.url) {
|
|
292
|
-
console.log(` URL: ${chalk.cyan(metadata.url)}`);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (metadata.userAgent) {
|
|
296
|
-
console.log(` User Agent: ${chalk.gray(metadata.userAgent)}`);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (metadata.viewport) {
|
|
300
|
-
console.log(` Viewport: ${metadata.viewport.width}x${metadata.viewport.height}`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Description
|
|
304
|
-
if (metadata.description) {
|
|
305
|
-
console.log(`\n${chalk.bold('📝 Description')}\n${metadata.description}`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Steps summary
|
|
309
|
-
console.log(`\n${chalk.bold('🔍 Steps')}`);
|
|
310
|
-
|
|
311
|
-
const stepTypes = {};
|
|
312
|
-
steps.forEach(step => {
|
|
313
|
-
stepTypes[step.type] = (stepTypes[step.type] || 0) + 1;
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
console.log(' ' + Object.entries(stepTypes)
|
|
317
|
-
.map(([type, count]) => `${chalk.cyan(type)}: ${count}`)
|
|
318
|
-
.join(', '));
|
|
319
|
-
|
|
320
|
-
// Sample of steps
|
|
321
|
-
console.log(`\n${chalk.bold('🔎 Sample Steps')}`);
|
|
322
|
-
const sampleSize = Math.min(5, steps.length);
|
|
323
|
-
steps.slice(0, sampleSize).forEach((step, i) => {
|
|
324
|
-
const time = (step.timestamp / 1000).toFixed(2).padStart(6, ' ');
|
|
325
|
-
const type = step.type.padEnd(15, ' ');
|
|
326
|
-
let details = '';
|
|
327
|
-
|
|
328
|
-
switch (step.type) {
|
|
329
|
-
case 'navigation':
|
|
330
|
-
details = step.url;
|
|
331
|
-
break;
|
|
332
|
-
case 'click':
|
|
333
|
-
details = step.selector || `x:${step.x}, y:${step.y}`;
|
|
334
|
-
break;
|
|
335
|
-
case 'input':
|
|
336
|
-
details = `${step.selector} = "${step.value}"`;
|
|
337
|
-
break;
|
|
338
|
-
default:
|
|
339
|
-
details = JSON.stringify(step).slice(0, 50) + '...';
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
console.log(` ${chalk.gray(time + 's')} ${chalk.cyan(type)} ${details}`);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
if (steps.length > sampleSize) {
|
|
346
|
-
console.log(` ... and ${steps.length - sampleSize} more steps`);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
console.log();
|
|
350
|
-
|
|
351
|
-
} catch (error) {
|
|
352
|
-
console.error(chalk.red('\n❌ Error showing capsule:'), error);
|
|
353
|
-
process.exit(1);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Delete a replay capsule
|
|
359
|
-
*/
|
|
360
|
-
async function deleteCapsule(capsuleId) {
|
|
361
|
-
try {
|
|
362
|
-
const engine = await initEngine();
|
|
363
|
-
await engine.deleteCapsule(capsuleId);
|
|
364
|
-
console.log(chalk.green(`\n✅ Deleted capsule: ${capsuleId}\n`));
|
|
365
|
-
} catch (error) {
|
|
366
|
-
console.error(chalk.red('\n❌ Error deleting capsule:'), error);
|
|
367
|
-
process.exit(1);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Export a replay capsule to a file
|
|
373
|
-
*/
|
|
374
|
-
async function exportCapsule(capsuleId, outputFile) {
|
|
375
|
-
try {
|
|
376
|
-
const engine = await initEngine();
|
|
377
|
-
const outputPath = await engine.exportCapsule(capsuleId, outputFile);
|
|
378
|
-
console.log(chalk.green(`\n✅ Exported capsule to: ${outputPath}\n`));
|
|
379
|
-
} catch (error) {
|
|
380
|
-
console.error(chalk.red('\n❌ Error exporting capsule:'), error);
|
|
381
|
-
process.exit(1);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Import a replay capsule from a file
|
|
387
|
-
*/
|
|
388
|
-
async function importCapsule(filePath) {
|
|
389
|
-
try {
|
|
390
|
-
const engine = await initEngine();
|
|
391
|
-
const result = await engine.importCapsule(filePath);
|
|
392
|
-
console.log(chalk.green(`\n✅ Imported capsule: ${result.id}`));
|
|
393
|
-
console.log(chalk.gray(`Path: ${result.path}\n`));
|
|
394
|
-
} catch (error) {
|
|
395
|
-
console.error(chalk.red('\n❌ Error importing capsule:'), error);
|
|
396
|
-
process.exit(1);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Main entry point for replay command
|
|
402
|
-
* Only parses args when explicitly called
|
|
403
|
-
*/
|
|
404
|
-
async function runReplay(args = []) {
|
|
405
|
-
loadDeps();
|
|
406
|
-
|
|
407
|
-
const program = new Command();
|
|
408
|
-
|
|
409
|
-
program
|
|
410
|
-
.name('vibecheck replay')
|
|
411
|
-
.description('Record and replay user interactions for testing and debugging')
|
|
412
|
-
.version(version || '0.0.0')
|
|
413
|
-
.option('-v, --verbose', 'Enable verbose output', false)
|
|
414
|
-
.option('--output-dir <dir>', 'Directory to save replay files', outputDir)
|
|
415
|
-
.hook('preAction', (thisCommand) => {
|
|
416
|
-
verbose = thisCommand.opts().verbose;
|
|
417
|
-
outputDir = thisCommand.opts().outputDir || outputDir;
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
// Record command
|
|
421
|
-
program
|
|
422
|
-
.command('record <url>')
|
|
423
|
-
.description('Record a user session')
|
|
424
|
-
.option('-n, --name <name>', 'Name for this recording')
|
|
425
|
-
.option('-d, --description <description>', 'Description of the recording')
|
|
426
|
-
.option('--tags <tags>', 'Comma-separated list of tags')
|
|
427
|
-
.option('--duration <seconds>', 'Auto-stop after specified seconds')
|
|
428
|
-
.option('--headed', 'Run browser in headed mode', false)
|
|
429
|
-
.option('--devtools', 'Open devtools', false)
|
|
430
|
-
.option('--video', 'Record video', false)
|
|
431
|
-
.action((url, options) => {
|
|
432
|
-
recordSession(url, options).catch(console.error);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
// Play command
|
|
436
|
-
program
|
|
437
|
-
.command('play <capsule>')
|
|
438
|
-
.description('Replay a recorded session')
|
|
439
|
-
.option('--speed <speed>', 'Playback speed (1.0 = normal, 2.0 = 2x, etc.)', parseFloat, 1.0)
|
|
440
|
-
.option('--headed', 'Run browser in headed mode', false)
|
|
441
|
-
.option('--devtools', 'Open devtools', false)
|
|
442
|
-
.option('--stop-on-failure', 'Stop on first failure', false)
|
|
443
|
-
.action((capsule, options) => {
|
|
444
|
-
replaySession(capsule, options).catch(console.error);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// List command
|
|
448
|
-
program
|
|
449
|
-
.command('list')
|
|
450
|
-
.description('List available replay capsules')
|
|
451
|
-
.action(() => {
|
|
452
|
-
listCapsules().catch(console.error);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
// Show command
|
|
456
|
-
program
|
|
457
|
-
.command('show <id>')
|
|
458
|
-
.description('Show details of a replay capsule')
|
|
459
|
-
.action((id) => {
|
|
460
|
-
showCapsule(id).catch(console.error);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
// Delete command
|
|
464
|
-
program
|
|
465
|
-
.command('delete <id>')
|
|
466
|
-
.description('Delete a replay capsule')
|
|
467
|
-
.action((id) => {
|
|
468
|
-
deleteCapsule(id).catch(console.error);
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// Export command
|
|
472
|
-
program
|
|
473
|
-
.command('export <id> <file>')
|
|
474
|
-
.description('Export a replay capsule to a file')
|
|
475
|
-
.action((id, file) => {
|
|
476
|
-
exportCapsule(id, file).catch(console.error);
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
// Import command
|
|
480
|
-
program
|
|
481
|
-
.command('import <file>')
|
|
482
|
-
.description('Import a replay capsule from a file')
|
|
483
|
-
.action((file) => {
|
|
484
|
-
importCapsule(file).catch(console.error);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
// Parse the provided args (prepend fake node/script path for commander)
|
|
488
|
-
const argv = ['node', 'vibecheck-replay', ...args];
|
|
489
|
-
|
|
490
|
-
if (args.length === 0) {
|
|
491
|
-
program.help();
|
|
492
|
-
return 0;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
await program.parseAsync(argv);
|
|
496
|
-
return 0;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
module.exports = { runReplay };
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck security - AuthZ + IDOR + Sensitive Security Proofs
|
|
3
|
-
*
|
|
4
|
-
* Subcommands:
|
|
5
|
-
* security model = learn (extract auth model)
|
|
6
|
-
* security matrix = build AuthZ matrix
|
|
7
|
-
* security idor = detect IDOR candidates
|
|
8
|
-
* security prove --url ... = runtime verification
|
|
9
|
-
*
|
|
10
|
-
* Replaces: permissions
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
"use strict";
|
|
14
|
-
|
|
15
|
-
const c = {
|
|
16
|
-
reset: '\x1b[0m',
|
|
17
|
-
bold: '\x1b[1m',
|
|
18
|
-
dim: '\x1b[2m',
|
|
19
|
-
cyan: '\x1b[36m',
|
|
20
|
-
yellow: '\x1b[33m',
|
|
21
|
-
red: '\x1b[31m',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function printHelp() {
|
|
25
|
-
console.log(`
|
|
26
|
-
${c.cyan}${c.bold}🔒 vibecheck security${c.reset} - Authorization & Security Verification
|
|
27
|
-
|
|
28
|
-
AuthZ matrix & IDOR detection for sensitive security proofs.
|
|
29
|
-
|
|
30
|
-
${c.bold}SUBCOMMANDS${c.reset}
|
|
31
|
-
${c.cyan}model${c.reset} ${c.dim}Extract auth model from codebase (replaces 'permissions --learn')${c.reset}
|
|
32
|
-
${c.cyan}matrix${c.reset} ${c.dim}Build AuthZ matrix (replaces 'permissions --matrix')${c.reset}
|
|
33
|
-
${c.cyan}idor${c.reset} ${c.dim}Detect IDOR candidates (replaces 'permissions --idor')${c.reset}
|
|
34
|
-
${c.cyan}prove${c.reset} --url <url> ${c.dim}Runtime verification (replaces 'permissions --prove')${c.reset}
|
|
35
|
-
|
|
36
|
-
${c.bold}EXAMPLES${c.reset}
|
|
37
|
-
vibecheck security model
|
|
38
|
-
vibecheck security matrix
|
|
39
|
-
vibecheck security idor
|
|
40
|
-
vibecheck security prove --url http://localhost:3000
|
|
41
|
-
|
|
42
|
-
${c.dim}Note: Old command still works as alias:
|
|
43
|
-
vibecheck permissions → vibecheck security model${c.reset}
|
|
44
|
-
`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function runSecurity(args) {
|
|
48
|
-
if (!args || args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
49
|
-
printHelp();
|
|
50
|
-
return 0;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const subcommand = args[0];
|
|
54
|
-
const subArgs = args.slice(1);
|
|
55
|
-
|
|
56
|
-
// Map subcommands to permissions flags
|
|
57
|
-
let permissionsArgs = [];
|
|
58
|
-
|
|
59
|
-
switch (subcommand) {
|
|
60
|
-
case "model":
|
|
61
|
-
permissionsArgs = ["--learn", ...subArgs];
|
|
62
|
-
break;
|
|
63
|
-
|
|
64
|
-
case "matrix":
|
|
65
|
-
permissionsArgs = ["--matrix", ...subArgs];
|
|
66
|
-
break;
|
|
67
|
-
|
|
68
|
-
case "idor":
|
|
69
|
-
permissionsArgs = ["--idor", ...subArgs];
|
|
70
|
-
break;
|
|
71
|
-
|
|
72
|
-
case "prove":
|
|
73
|
-
permissionsArgs = ["--prove", ...subArgs];
|
|
74
|
-
break;
|
|
75
|
-
|
|
76
|
-
default:
|
|
77
|
-
console.error(`${c.red}Unknown subcommand:${c.reset} ${subcommand}`);
|
|
78
|
-
console.log(`\n${c.dim}Run 'vibecheck security --help' for available subcommands.${c.reset}\n`);
|
|
79
|
-
return 1;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Delegate to runPermissions
|
|
83
|
-
try {
|
|
84
|
-
const { runPermissions } = require("./runPermissions");
|
|
85
|
-
return await runPermissions(permissionsArgs);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
console.error(`${c.red}Error:${c.reset} Security command unavailable: ${e.message}`);
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
module.exports = { runSecurity };
|