@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.
Files changed (56) hide show
  1. package/README.md +27 -32
  2. package/bin/registry.js +208 -343
  3. package/bin/runners/context/generators/mcp.js +18 -0
  4. package/bin/runners/context/index.js +72 -4
  5. package/bin/runners/context/proof-context.js +293 -1
  6. package/bin/runners/context/security-scanner.js +311 -73
  7. package/bin/runners/lib/analyzers.js +607 -20
  8. package/bin/runners/lib/detectors-v2.js +172 -15
  9. package/bin/runners/lib/entitlements-v2.js +48 -1
  10. package/bin/runners/lib/evidence-pack.js +678 -0
  11. package/bin/runners/lib/html-proof-report.js +913 -0
  12. package/bin/runners/lib/missions/plan.js +231 -41
  13. package/bin/runners/lib/missions/templates.js +125 -0
  14. package/bin/runners/lib/scan-output.js +492 -253
  15. package/bin/runners/lib/ship-output.js +901 -641
  16. package/bin/runners/runCheckpoint.js +44 -3
  17. package/bin/runners/runContext.d.ts +4 -0
  18. package/bin/runners/runContext.js +2 -3
  19. package/bin/runners/runDoctor.js +11 -4
  20. package/bin/runners/runFix.js +51 -341
  21. package/bin/runners/runInit.js +37 -20
  22. package/bin/runners/runPolish.d.ts +4 -0
  23. package/bin/runners/runPolish.js +608 -29
  24. package/bin/runners/runProve.js +210 -25
  25. package/bin/runners/runReality.js +861 -107
  26. package/bin/runners/runScan.js +238 -4
  27. package/bin/runners/runShip.js +19 -3
  28. package/bin/runners/runWatch.js +25 -5
  29. package/bin/vibecheck.js +35 -47
  30. package/mcp-server/consolidated-tools.js +408 -42
  31. package/mcp-server/index.js +152 -15
  32. package/mcp-server/package.json +1 -1
  33. package/mcp-server/proof-tools.js +571 -0
  34. package/mcp-server/tier-auth.js +22 -19
  35. package/mcp-server/tools-v3.js +744 -0
  36. package/mcp-server/truth-firewall-tools.js +190 -4
  37. package/package.json +3 -1
  38. package/bin/runners/runBadge.js +0 -916
  39. package/bin/runners/runContracts.js +0 -105
  40. package/bin/runners/runCtx.js +0 -680
  41. package/bin/runners/runCtxDiff.js +0 -301
  42. package/bin/runners/runCtxGuard.js +0 -176
  43. package/bin/runners/runCtxSync.js +0 -116
  44. package/bin/runners/runExport.js +0 -93
  45. package/bin/runners/runGraph.js +0 -454
  46. package/bin/runners/runInstall.js +0 -273
  47. package/bin/runners/runLabs.js +0 -341
  48. package/bin/runners/runLaunch.js +0 -181
  49. package/bin/runners/runPR.js +0 -255
  50. package/bin/runners/runPermissions.js +0 -310
  51. package/bin/runners/runPreflight.js +0 -580
  52. package/bin/runners/runReplay.js +0 -499
  53. package/bin/runners/runSecurity.js +0 -92
  54. package/bin/runners/runShare.js +0 -212
  55. package/bin/runners/runStatus.js +0 -102
  56. package/bin/runners/runVerify.js +0 -272
@@ -1,580 +0,0 @@
1
- /**
2
- * Preflight Validation Command
3
- *
4
- * Validates environment, migrations, storage, and security
5
- * before deployment. Fails fast on critical issues.
6
- */
7
-
8
- "use strict";
9
-
10
- const { execSync } = require("child_process");
11
- const fs = require("fs");
12
- const path = require("path");
13
- const https = require("https");
14
- const http = require("http");
15
-
16
- const c = {
17
- reset: "\x1b[0m",
18
- bold: "\x1b[1m",
19
- dim: "\x1b[2m",
20
- red: "\x1b[31m",
21
- green: "\x1b[32m",
22
- yellow: "\x1b[33m",
23
- blue: "\x1b[34m",
24
- cyan: "\x1b[36m",
25
- };
26
-
27
- const checks = {
28
- env: validateEnvironment,
29
- secrets: validateSecrets,
30
- database: validateDatabase,
31
- storage: validateStorage,
32
- ssl: validateSSL,
33
- webhooks: validateWebhooks,
34
- tiers: validateTierEnforcement,
35
- services: validateExternalServices,
36
- security: validateSecurityConfig
37
- };
38
-
39
- async function runPreflight(args) {
40
- if (args.includes("--help") || args.includes("-h")) {
41
- console.log(`
42
- ${c.bold}vibecheck preflight${c.reset} - Deployment validation checks
43
-
44
- ${c.bold}Usage:${c.reset}
45
- vibecheck preflight [options]
46
-
47
- ${c.bold}Options:${c.reset}
48
- ${c.cyan}--all${c.reset} Run all checks
49
- ${c.cyan}--env${c.reset} Validate environment variables
50
- ${c.cyan}--secrets${c.reset} Validate secrets configuration
51
- ${c.cyan}--database${c.reset} Validate database connectivity
52
- ${c.cyan}--storage${c.reset} Validate storage configuration
53
- ${c.cyan}--ssl${c.reset} Validate SSL certificates
54
- ${c.cyan}--webhooks${c.reset} Validate webhook endpoints
55
- ${c.cyan}--tiers${c.reset} Validate tier enforcement
56
- ${c.cyan}--services${c.reset} Validate external services
57
- ${c.cyan}--security${c.reset} Validate security configuration
58
- ${c.cyan}--help, -h${c.reset} Show this help
59
-
60
- ${c.bold}Examples:${c.reset}
61
- vibecheck preflight --all
62
- vibecheck preflight --env --secrets --database
63
- `);
64
- return 0;
65
- }
66
-
67
- console.log(`${c.cyan}${c.bold}🚀 Vibecheck Preflight Check${c.reset}\n`);
68
-
69
- const env = process.env.NODE_ENV || "development";
70
- console.log(`${c.dim}Environment: ${env.toUpperCase()}${c.reset}\n`);
71
-
72
- // Determine which checks to run
73
- const checksToRun = args.includes("--all")
74
- ? Object.keys(checks)
75
- : args.filter(arg => arg.startsWith("--")).map(arg => arg.substring(2)).filter(arg => checks[arg]);
76
-
77
- if (checksToRun.length === 0) {
78
- checksToRun.push(...Object.keys(checks));
79
- }
80
-
81
- console.log(`${c.blue}Running checks:${c.reset} ${checksToRun.join(", ")}\n`);
82
-
83
- const results = {
84
- passed: [],
85
- failed: [],
86
- warnings: []
87
- };
88
-
89
- // Run each check
90
- for (const checkName of checksToRun) {
91
- console.log(`${c.yellow}⏳ Running ${checkName} check...${c.reset}`);
92
-
93
- try {
94
- const result = await checks[checkName]();
95
-
96
- if (result.passed) {
97
- console.log(`${c.green}✅ ${checkName}: PASSED${c.reset}`);
98
- results.passed.push(checkName);
99
- if (result.message) {
100
- console.log(` ${c.dim}${result.message}${c.reset}`);
101
- }
102
- } else {
103
- console.log(`${c.red}❌ ${checkName}: FAILED${c.reset}`);
104
- console.log(` ${result.error}`);
105
- results.failed.push({ name: checkName, error: result.error });
106
- }
107
- } catch (err) {
108
- console.log(`${c.red}❌ ${checkName}: ERROR${c.reset}`);
109
- console.log(` ${err.message}`);
110
- results.failed.push({ name: checkName, error: err.message });
111
- }
112
-
113
- console.log();
114
- }
115
-
116
- // Summary
117
- console.log(`${c.bold}${'═'.repeat(50)}${c.reset}`);
118
- console.log(`${c.bold}PREFLIGHT CHECK SUMMARY${c.reset}`);
119
- console.log(`${c.bold}${'═'.repeat(50)}${c.reset}`);
120
- console.log(`${c.green}✅ Passed: ${results.passed.length}${c.reset}`);
121
- console.log(`${c.red}❌ Failed: ${results.failed.length}${c.reset}`);
122
- console.log(`${c.yellow}⚠️ Warnings: ${results.warnings.length}${c.reset}\n`);
123
-
124
- // Fail if any critical checks failed
125
- const criticalFailures = results.failed.filter(f =>
126
- ["env", "secrets", "database", "webhooks", "tiers"].includes(f.name)
127
- );
128
-
129
- if (criticalFailures.length > 0) {
130
- console.log(`${c.red}${c.bold}💥 CRITICAL FAILURES DETECTED${c.reset}`);
131
- console.log("Fix the following issues before deploying:\n");
132
-
133
- for (const failure of criticalFailures) {
134
- console.log(`${c.red}• ${failure.name}: ${failure.error}${c.reset}`);
135
- }
136
-
137
- console.log(`\n${c.yellow}Run 'vibecheck preflight --fix' to attempt auto-fixes${c.reset}`);
138
- return 2; // BLOCK
139
- }
140
-
141
- if (results.failed.length > 0) {
142
- console.log(`${c.yellow}⚠️ Some non-critical checks failed${c.reset}`);
143
- console.log("Review and fix if necessary\n");
144
- return 1; // WARN
145
- }
146
-
147
- console.log(`${c.green}🎉 All checks passed! Ready to deploy.${c.reset}`);
148
- return 0; // SHIP
149
- }
150
-
151
- async function validateEnvironment() {
152
- const { validateEnvironment } = require("../../shared/env-validator");
153
-
154
- try {
155
- const { validated } = validateEnvironment();
156
-
157
- // Check for production-specific requirements
158
- if (process.env.NODE_ENV === "production") {
159
- const prodRequired = [
160
- "VIBECHECK_ENFORCE_HTTPS",
161
- "VIBECHECK_CORS_ORIGIN",
162
- "SENTRY_DSN",
163
- "VIBECHECK_RATE_LIMIT_STRICT"
164
- ];
165
-
166
- for (const key of prodRequired) {
167
- if (!validated[key]) {
168
- return { passed: false, error: `Missing production variable: ${key}` };
169
- }
170
- }
171
-
172
- // Check that dangerous dev flags are NOT set
173
- const forbidden = [
174
- "VIBECHECK_SKIP_AUTH",
175
- "VIBECHECK_FAKE_BILLING",
176
- "VIBECHECK_MOCK_AI"
177
- ];
178
-
179
- for (const key of forbidden) {
180
- if (validated[key] === "true") {
181
- return { passed: false, error: `Dangerous flag set in production: ${key}` };
182
- }
183
- }
184
- }
185
-
186
- return { passed: true, message: "All environment variables valid" };
187
- } catch (err) {
188
- return { passed: false, error: err.message };
189
- }
190
- }
191
-
192
- async function validateSecrets() {
193
- const secrets = [
194
- "VIBECHECK_JWT_SECRET",
195
- "VIBECHECK_REFRESH_SECRET",
196
- "VIBECHECK_API_SECRET"
197
- ];
198
-
199
- for (const secret of secrets) {
200
- const value = process.env[secret];
201
-
202
- if (!value) {
203
- return { passed: false, error: `Missing secret: ${secret}` };
204
- }
205
-
206
- if (value.length < 32) {
207
- return { passed: false, error: `${secret} too short (min 32 chars)` };
208
- }
209
-
210
- // Check for weak patterns
211
- if (value === "secret" || value === "password" || value.startsWith("test")) {
212
- return { passed: false, error: `${secret} appears to be weak` };
213
- }
214
- }
215
-
216
- return { passed: true, message: "All secrets are strong" };
217
- }
218
-
219
- async function validateDatabase() {
220
- const databaseUrl = process.env.DATABASE_URL;
221
-
222
- if (!databaseUrl) {
223
- return { passed: false, error: "DATABASE_URL not set" };
224
- }
225
-
226
- try {
227
- // Parse database URL to check SSL
228
- const url = new URL(databaseUrl);
229
-
230
- if (process.env.NODE_ENV === "production" && url.searchParams.get("sslmode") !== "require") {
231
- return { passed: false, error: "Database must use SSL in production" };
232
- }
233
-
234
- // Test connectivity
235
- const { Client } = require("pg");
236
- const client = new Client({ connectionString: databaseUrl });
237
-
238
- await client.connect();
239
-
240
- // Check migrations
241
- const result = await client.query(`
242
- SELECT COUNT(*) as count FROM schema_migrations
243
- `);
244
-
245
- await client.end();
246
-
247
- const migrationCount = parseInt(result.rows[0].count);
248
- if (migrationCount === 0) {
249
- return { passed: false, error: "No migrations found - run 'npm run db:migrate'" };
250
- }
251
-
252
- return { passed: true, message: `${migrationCount} migrations applied` };
253
- } catch (err) {
254
- return { passed: false, error: `Database connection failed: ${err.message}` };
255
- }
256
- }
257
-
258
- async function validateStorage() {
259
- const storageType = process.env.STORAGE_TYPE || "local";
260
-
261
- if (storageType === "s3") {
262
- const required = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_S3_BUCKET"];
263
-
264
- for (const key of required) {
265
- if (!process.env[key]) {
266
- return { passed: false, error: `Missing S3 config: ${key}` };
267
- }
268
- }
269
-
270
- // Test S3 connectivity
271
- try {
272
- const AWS = require("aws-sdk");
273
- const s3 = new AWS.S3({
274
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
275
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
276
- region: process.env.AWS_REGION || "us-east-1"
277
- });
278
-
279
- await s3.headBucket({ Bucket: process.env.AWS_S3_BUCKET }).promise();
280
-
281
- return { passed: true, message: "S3 bucket accessible" };
282
- } catch (err) {
283
- return { passed: false, error: `S3 access failed: ${err.message}` };
284
- }
285
- }
286
-
287
- // For local storage, check directory exists
288
- if (storageType === "local") {
289
- const uploadDir = process.env.UPLOAD_DIR || "./uploads";
290
-
291
- if (!fs.existsSync(uploadDir)) {
292
- try {
293
- fs.mkdirSync(uploadDir, { recursive: true });
294
- } catch (err) {
295
- return { passed: false, error: `Cannot create upload directory: ${err.message}` };
296
- }
297
- }
298
-
299
- return { passed: true, message: "Local storage ready" };
300
- }
301
-
302
- return { passed: true, message: `${storageType} storage configured` };
303
- }
304
-
305
- async function validateSSL() {
306
- if (process.env.NODE_ENV !== "production") {
307
- return { passed: true, message: "SSL not required in development" };
308
- }
309
-
310
- const url = process.env.VIBECHECK_PUBLIC_URL || "https://api.vibecheck.ai";
311
-
312
- try {
313
- return new Promise((resolve) => {
314
- const req = https.get(url, (res) => {
315
- const cert = res.socket.getPeerCertificate();
316
-
317
- if (!cert) {
318
- resolve({ passed: false, error: "No SSL certificate found" });
319
- return;
320
- }
321
-
322
- // Check certificate expiry
323
- const now = new Date();
324
- const expiry = new Date(cert.valid_to);
325
-
326
- if (expiry < now) {
327
- resolve({ passed: false, error: "SSL certificate expired" });
328
- return;
329
- }
330
-
331
- // Check expiry within 30 days
332
- const thirtyDays = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
333
- if (expiry < thirtyDays) {
334
- resolve({
335
- passed: false,
336
- error: `SSL certificate expires soon: ${cert.valid_to}`
337
- });
338
- return;
339
- }
340
-
341
- resolve({
342
- passed: true,
343
- message: `SSL valid until ${cert.valid_to}`
344
- });
345
- });
346
-
347
- req.on("error", (err) => {
348
- resolve({ passed: false, error: `SSL check failed: ${err.message}` });
349
- });
350
-
351
- req.setTimeout(5000, () => {
352
- req.destroy();
353
- resolve({ passed: false, error: "SSL check timeout" });
354
- });
355
- });
356
- } catch (err) {
357
- return { passed: false, error: err.message };
358
- }
359
- }
360
-
361
- async function validateWebhooks() {
362
- const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
363
-
364
- if (!webhookSecret) {
365
- return { passed: false, error: "STRIPE_WEBHOOK_SECRET not set" };
366
- }
367
-
368
- if (!webhookSecret.startsWith("whsec_")) {
369
- return { passed: false, error: "Invalid webhook secret format" };
370
- }
371
-
372
- // Test webhook endpoint
373
- const testPayload = JSON.stringify({
374
- id: `evt_test_${Date.now()}`,
375
- type: "test",
376
- data: { object: { test: true } }
377
- });
378
-
379
- const timestamp = Math.floor(Date.now() / 1000);
380
- const signedPayload = `${timestamp}.${testPayload}`;
381
-
382
- const crypto = require("crypto");
383
- const signature = crypto
384
- .createHmac("sha256", webhookSecret)
385
- .update(signedPayload)
386
- .digest("hex");
387
-
388
- const stripeSignature = `t=${timestamp},v1=${signature}`;
389
-
390
- try {
391
- const webhookUrl = process.env.VIBECHECK_WEBHOOK_URL || "http://localhost:3000/webhooks/stripe";
392
-
393
- return new Promise((resolve) => {
394
- const postData = testPayload;
395
-
396
- const req = http.request(webhookUrl, {
397
- method: "POST",
398
- headers: {
399
- "Content-Type": "application/json",
400
- "Content-Length": Buffer.byteLength(postData),
401
- "Stripe-Signature": stripeSignature
402
- }
403
- }, (res) => {
404
- let data = "";
405
- res.on("data", chunk => data += chunk);
406
- res.on("end", () => {
407
- if (res.statusCode === 200) {
408
- resolve({ passed: true, message: "Webhook signature verification working" });
409
- } else {
410
- resolve({ passed: false, error: `Webhook returned ${res.statusCode}` });
411
- }
412
- });
413
- });
414
-
415
- req.on("error", (err) => {
416
- resolve({ passed: false, error: `Webhook test failed: ${err.message}` });
417
- });
418
-
419
- req.setTimeout(5000, () => {
420
- req.destroy();
421
- resolve({ passed: false, error: "Webhook test timeout" });
422
- });
423
-
424
- req.write(postData);
425
- req.end();
426
- });
427
- } catch (err) {
428
- return { passed: false, error: err.message };
429
- }
430
- }
431
-
432
- async function validateTierEnforcement() {
433
- // Check CLI tier enforcement
434
- try {
435
- const { enforce } = require("../lib/entitlements-v2");
436
-
437
- // Test free tier
438
- const freeResult = await enforce("scan", { silent: true });
439
- if (!freeResult.allowed) {
440
- return { passed: false, error: "CLI incorrectly blocks free tier features" };
441
- }
442
-
443
- // Test pro tier enforcement
444
- const proResult = await enforce("prove", { silent: true });
445
- if (proResult.allowed && process.env.NODE_ENV === "production") {
446
- return { passed: false, error: "CLI incorrectly allows pro features without auth" };
447
- }
448
- } catch (err) {
449
- return { passed: false, error: `CLI tier check failed: ${err.message}` };
450
- }
451
-
452
- // Check API tier enforcement if server is running
453
- const apiUrl = process.env.VIBECHECK_API_URL || "http://localhost:3000";
454
-
455
- try {
456
- // Test without auth
457
- const response = await fetch(`${apiUrl}/api/v1/ship`, {
458
- method: "POST",
459
- headers: { "Content-Type": "application/json" }
460
- });
461
-
462
- if (response.status !== 401 && response.status !== 403) {
463
- return { passed: false, error: "API not enforcing authentication" };
464
- }
465
- } catch (err) {
466
- // Server might not be running - that's OK for preflight
467
- return { passed: true, message: "API not running - skip check" };
468
- }
469
-
470
- return { passed: true, message: "Tier enforcement consistent" };
471
- }
472
-
473
- async function validateExternalServices() {
474
- const services = [
475
- { name: "License API", url: process.env.VIBECHECK_LICENSE_API },
476
- { name: "AI Endpoint", url: process.env.VIBECHECK_AI_ENDPOINT }
477
- ];
478
-
479
- for (const service of services) {
480
- if (!service.url) {
481
- return { passed: false, error: `${service.name} URL not configured` };
482
- }
483
-
484
- try {
485
- const response = await fetch(`${service.url}/health`, {
486
- method: "GET",
487
- timeout: 5000
488
- });
489
-
490
- if (!response.ok) {
491
- return { passed: false, error: `${service.name} unhealthy: ${response.status}` };
492
- }
493
- } catch (err) {
494
- return { passed: false, error: `${service.name} unreachable: ${err.message}` };
495
- }
496
- }
497
-
498
- return { passed: true, message: "All external services healthy" };
499
- }
500
-
501
- async function validateSecurityConfig() {
502
- const issues = [];
503
-
504
- // Check for secure headers in production
505
- if (process.env.NODE_ENV === "production") {
506
- if (!process.env.VIBECHECK_ENFORCE_HTTPS) {
507
- issues.push("HTTPS enforcement not enabled");
508
- }
509
-
510
- if (!process.env.VIBECHECK_RATE_LIMIT_STRICT) {
511
- issues.push("Strict rate limiting not enabled");
512
- }
513
-
514
- const corsOrigin = process.env.VIBECHECK_CORS_ORIGIN;
515
- if (!corsOrigin || corsOrigin.includes("*") || corsOrigin.includes("localhost")) {
516
- issues.push("CORS origin not properly restricted");
517
- }
518
- }
519
-
520
- // Check for required security headers
521
- const requiredHeaders = [
522
- "helmet",
523
- "cors",
524
- "rateLimit"
525
- ];
526
-
527
- // Check if security middleware is configured
528
- try {
529
- const apiFile = fs.readFileSync("./apps/api/src/app.js", "utf8");
530
-
531
- for (const header of requiredHeaders) {
532
- if (!apiFile.includes(header)) {
533
- issues.push(`Security middleware '${header}' not found`);
534
- }
535
- }
536
- } catch (err) {
537
- // API file might not exist in this context
538
- }
539
-
540
- if (issues.length > 0) {
541
- return { passed: false, error: issues.join("; ") };
542
- }
543
-
544
- return { passed: true, message: "Security configuration valid" };
545
- }
546
-
547
- function printHelp() {
548
- console.log(`
549
- ${c.cyan}${c.bold}vibecheck preflight${c.reset} - Deployment validation
550
-
551
- ${c.bold}USAGE${c.reset}
552
- vibecheck preflight [options]
553
-
554
- ${c.bold}OPTIONS${c.reset}
555
- --all Run all checks
556
- --env Validate environment variables
557
- --secrets Validate secrets strength
558
- --database Validate database connectivity
559
- --storage Validate storage configuration
560
- --ssl Validate SSL certificates
561
- --webhooks Validate webhook signatures
562
- --tiers Validate tier enforcement
563
- --services Validate external services
564
- --security Validate security configuration
565
- --fix Attempt to fix issues (where possible)
566
- --help, -h Show this help
567
-
568
- ${c.bold}EXAMPLES${c.reset}
569
- vibecheck preflight --all # Run all checks
570
- vibecheck preflight --env --secrets # Run specific checks
571
- vibecheck preflight # Run all checks (default)
572
-
573
- ${c.bold}EXIT CODES${c.reset}
574
- 0 = All checks passed
575
- 1 = Non-critical warnings
576
- 2 = Critical failures (block deployment)
577
- `);
578
- }
579
-
580
- module.exports = { runPreflight, printHelp };