@vibecheckai/cli 3.5.0 → 3.5.2
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/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* VibeCheck Entitlements
|
|
3
3
|
*
|
|
4
4
|
* Simple 2-tier model:
|
|
5
|
-
* - FREE ($0): Inspect & Observe
|
|
6
|
-
* - PRO ($69/mo): Fix, Prove & Enforce
|
|
5
|
+
* - FREE ($0): Inspect & Observe
|
|
6
|
+
* - PRO ($69/mo): Fix, Prove & Enforce
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
"use strict";
|
|
@@ -17,7 +17,6 @@ const os = require("os");
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
const EXIT_SUCCESS = 0;
|
|
19
19
|
const EXIT_FEATURE_NOT_ALLOWED = 3;
|
|
20
|
-
const EXIT_AUTH_REQUIRED = 4;
|
|
21
20
|
|
|
22
21
|
// ============================================================================
|
|
23
22
|
// TIERS
|
|
@@ -34,7 +33,7 @@ const FREE_FEATURES = [
|
|
|
34
33
|
// Setup & environment
|
|
35
34
|
"init", "doctor", "install", "status", "watch", "preflight",
|
|
36
35
|
// Scan & analysis
|
|
37
|
-
"scan", "runtime",
|
|
36
|
+
"scan", "runtime",
|
|
38
37
|
// AI verification
|
|
39
38
|
"ctx", "contracts", "verify",
|
|
40
39
|
// Reports
|
|
@@ -44,37 +43,53 @@ const FREE_FEATURES = [
|
|
|
44
43
|
// Preview modes
|
|
45
44
|
"reality.preview", "firewall.observe",
|
|
46
45
|
// Misc
|
|
47
|
-
"labs", "mdc",
|
|
46
|
+
"labs", "mdc",
|
|
48
47
|
];
|
|
49
48
|
|
|
50
49
|
const PRO_FEATURES = [
|
|
51
50
|
// CI/CD & PR
|
|
52
51
|
"gate", "pr", "badge", "ship",
|
|
53
52
|
// Fixes
|
|
54
|
-
"fix", "fix.apply", "scan.autofix",
|
|
53
|
+
"fix", "fix.apply", "fix.analyze", "fix.diff", "fix.rules", "scan.autofix",
|
|
55
54
|
// Prove & verify
|
|
56
55
|
"prove", "replay", "permissions", "graph", "ai-test", "share",
|
|
57
56
|
// Advanced
|
|
58
57
|
"checkpoint", "polish", "guard", "context",
|
|
59
58
|
// Full modes
|
|
60
59
|
"firewall.enforce", "reality.full", "mcp.full",
|
|
61
|
-
// All FREE features
|
|
60
|
+
// All FREE features
|
|
62
61
|
...FREE_FEATURES,
|
|
63
62
|
];
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Check if developer mode bypass is allowed.
|
|
66
|
+
*
|
|
67
|
+
* SECURITY: VIBECHECK_DEV_PRO is ONLY allowed in non-production environments.
|
|
68
|
+
* This prevents environment variable injection from granting PRO access in production.
|
|
69
|
+
*
|
|
70
|
+
* @returns {boolean} True only if in development AND VIBECHECK_DEV_PRO=1
|
|
71
|
+
*/
|
|
72
|
+
function isDevProBypassAllowed() {
|
|
73
|
+
// SECURITY: Never allow dev bypass in production
|
|
74
|
+
if (process.env.NODE_ENV === "production") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Also block in CI environments to prevent pipeline exploitation
|
|
78
|
+
if (process.env.CI === "true" || process.env.CI === "1") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return process.env.VIBECHECK_DEV_PRO === "1";
|
|
71
82
|
}
|
|
72
83
|
|
|
73
|
-
function
|
|
74
|
-
|
|
84
|
+
function isPro(tier) {
|
|
85
|
+
// Developer mode bypass (blocked in production)
|
|
86
|
+
if (isDevProBypassAllowed()) return true;
|
|
87
|
+
return tier === "pro";
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
function tierHasFeature(tier, feature) {
|
|
91
|
+
// Developer mode bypass - grant all features (blocked in production)
|
|
92
|
+
if (isDevProBypassAllowed()) return true;
|
|
78
93
|
if (tier === "pro") return true; // PRO has everything
|
|
79
94
|
return FREE_FEATURES.includes(feature);
|
|
80
95
|
}
|
|
@@ -90,6 +105,14 @@ let _cachedTierExpiry = 0;
|
|
|
90
105
|
async function getTier(options = {}) {
|
|
91
106
|
const { apiKey, forceRefresh = false } = options;
|
|
92
107
|
|
|
108
|
+
// Developer mode: VIBECHECK_DEV_PRO=1 grants pro tier for local development
|
|
109
|
+
// SECURITY: This bypass is blocked in production environments
|
|
110
|
+
if (isDevProBypassAllowed()) {
|
|
111
|
+
_cachedTier = "pro";
|
|
112
|
+
_cachedTierExpiry = Date.now() + 86400000; // 24 hours
|
|
113
|
+
return "pro";
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
if (!forceRefresh && _cachedTier && Date.now() < _cachedTierExpiry) {
|
|
94
117
|
return _cachedTier;
|
|
95
118
|
}
|
|
@@ -127,36 +150,14 @@ async function getTier(options = {}) {
|
|
|
127
150
|
// ============================================================================
|
|
128
151
|
async function enforce(feature, options = {}) {
|
|
129
152
|
const { apiKey, silent = false } = options;
|
|
130
|
-
|
|
131
|
-
// FREE features work without any API key
|
|
132
|
-
if (isFreeFeature(feature)) {
|
|
133
|
-
return { allowed: true, tier: apiKey ? await getTier({ apiKey }) : "free" };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// PRO features require an API key
|
|
137
|
-
if (!apiKey) {
|
|
138
|
-
const message = formatApiKeyRequiredMessage(feature);
|
|
139
|
-
if (!silent) {
|
|
140
|
-
console.error(message);
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
allowed: false,
|
|
144
|
-
tier: "free",
|
|
145
|
-
exitCode: EXIT_AUTH_REQUIRED,
|
|
146
|
-
message,
|
|
147
|
-
reason: "api_key_required",
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Validate API key and check tier
|
|
152
153
|
const tier = await getTier({ apiKey });
|
|
154
|
+
|
|
153
155
|
const hasAccess = tierHasFeature(tier, feature);
|
|
154
156
|
|
|
155
157
|
if (hasAccess) {
|
|
156
158
|
return { allowed: true, tier };
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
// User has API key but not on Pro plan
|
|
160
161
|
const message = formatUpgradeMessage(feature);
|
|
161
162
|
if (!silent) {
|
|
162
163
|
console.error(message);
|
|
@@ -167,7 +168,6 @@ async function enforce(feature, options = {}) {
|
|
|
167
168
|
tier,
|
|
168
169
|
exitCode: EXIT_FEATURE_NOT_ALLOWED,
|
|
169
170
|
message,
|
|
170
|
-
reason: "upgrade_required",
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -189,72 +189,54 @@ async function checkCommand(command, options = {}) {
|
|
|
189
189
|
const c = {
|
|
190
190
|
reset: "\x1b[0m",
|
|
191
191
|
bold: "\x1b[1m",
|
|
192
|
-
dim: "\x1b[2m",
|
|
193
192
|
cyan: "\x1b[36m",
|
|
194
193
|
yellow: "\x1b[33m",
|
|
195
|
-
magenta: "\x1b[35m",
|
|
196
|
-
green: "\x1b[32m",
|
|
197
194
|
};
|
|
198
195
|
|
|
199
|
-
function formatApiKeyRequiredMessage(feature) {
|
|
200
|
-
return `
|
|
201
|
-
${c.bold}${c.magenta}✦ PRO Feature${c.reset}
|
|
202
|
-
|
|
203
|
-
${c.yellow}${feature}${c.reset} requires an API key.
|
|
204
|
-
|
|
205
|
-
${c.dim}To use PRO features:${c.reset}
|
|
206
|
-
|
|
207
|
-
${c.cyan}1.${c.reset} Sign up at ${c.cyan}https://vibecheckai.dev${c.reset}
|
|
208
|
-
${c.cyan}2.${c.reset} Run ${c.green}vibecheck login${c.reset}
|
|
209
|
-
${c.cyan}3.${c.reset} Or set ${c.green}VIBECHECK_API_KEY${c.reset} environment variable
|
|
210
|
-
|
|
211
|
-
${c.dim}FREE features (scan, init, doctor, etc.) work without an API key.${c.reset}
|
|
212
|
-
`;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
196
|
function formatUpgradeMessage(feature) {
|
|
216
197
|
return `
|
|
217
|
-
${c.bold}
|
|
218
|
-
|
|
219
|
-
${c.yellow}${feature}${c.reset} requires a Pro subscription.
|
|
198
|
+
${c.bold}This feature requires Pro.${c.reset}
|
|
220
199
|
|
|
221
|
-
|
|
200
|
+
${c.yellow}${feature}${c.reset} is a Pro feature.
|
|
222
201
|
|
|
223
|
-
|
|
224
|
-
${c.green}✓${c.reset} AI-powered fixes
|
|
225
|
-
${c.green}✓${c.reset} Ship verdicts & badges
|
|
226
|
-
${c.green}✓${c.reset} Runtime proof generation
|
|
227
|
-
${c.green}✓${c.reset} CI/CD enforcement
|
|
228
|
-
${c.green}✓${c.reset} Full MCP integration
|
|
202
|
+
Upgrade to Pro ($69/mo) to unlock Fix, Prove & Enforce capabilities.
|
|
229
203
|
|
|
230
|
-
|
|
231
|
-
|
|
204
|
+
vibecheck upgrade
|
|
205
|
+
https://vibecheckai.dev/pricing
|
|
232
206
|
`;
|
|
233
207
|
}
|
|
234
208
|
|
|
235
209
|
// ============================================================================
|
|
236
|
-
//
|
|
210
|
+
// TIER LIMITS
|
|
237
211
|
// ============================================================================
|
|
212
|
+
const TIER_LIMITS = {
|
|
213
|
+
free: {
|
|
214
|
+
reportFormats: ["html", "md", "json"],
|
|
215
|
+
maxScansPerMonth: 100,
|
|
216
|
+
maxFilesPerScan: 1000,
|
|
217
|
+
},
|
|
218
|
+
pro: {
|
|
219
|
+
reportFormats: ["html", "md", "json", "sarif", "csv", "pdf"],
|
|
220
|
+
maxScansPerMonth: -1, // unlimited
|
|
221
|
+
maxFilesPerScan: -1, // unlimited
|
|
222
|
+
},
|
|
223
|
+
};
|
|
238
224
|
|
|
239
|
-
/**
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
/** @deprecated Use enforce() instead */
|
|
245
|
-
async function enforceFeature(feature, options = {}) {
|
|
246
|
-
return enforce(feature, options);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** @deprecated Use enforce() instead */
|
|
250
|
-
async function enforceLimit(feature, options = {}) {
|
|
251
|
-
return enforce(feature, options);
|
|
225
|
+
/**
|
|
226
|
+
* Get limits for a tier
|
|
227
|
+
*/
|
|
228
|
+
function getLimits(tier) {
|
|
229
|
+
return TIER_LIMITS[tier] || TIER_LIMITS.free;
|
|
252
230
|
}
|
|
253
231
|
|
|
254
|
-
/**
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Check if current tier meets minimum required tier
|
|
234
|
+
*/
|
|
235
|
+
function tierMeetsMinimum(current, required) {
|
|
236
|
+
const tierOrder = ['free', 'pro'];
|
|
237
|
+
const currentIndex = tierOrder.indexOf(current);
|
|
238
|
+
const requiredIndex = tierOrder.indexOf(required);
|
|
239
|
+
return currentIndex >= requiredIndex;
|
|
258
240
|
}
|
|
259
241
|
|
|
260
242
|
// ============================================================================
|
|
@@ -269,21 +251,15 @@ module.exports = {
|
|
|
269
251
|
|
|
270
252
|
// Helpers
|
|
271
253
|
isPro,
|
|
272
|
-
isFreeFeature,
|
|
273
|
-
isProFeature,
|
|
274
254
|
tierHasFeature,
|
|
255
|
+
getLimits,
|
|
256
|
+
tierMeetsMinimum,
|
|
275
257
|
|
|
276
258
|
// Constants
|
|
277
259
|
TIERS,
|
|
260
|
+
TIER_LIMITS,
|
|
278
261
|
FREE_FEATURES,
|
|
279
262
|
PRO_FEATURES,
|
|
280
263
|
EXIT_SUCCESS,
|
|
281
264
|
EXIT_FEATURE_NOT_ALLOWED,
|
|
282
|
-
EXIT_AUTH_REQUIRED,
|
|
283
|
-
|
|
284
|
-
// Backward compatibility (deprecated)
|
|
285
|
-
getCurrentTier,
|
|
286
|
-
enforceFeature,
|
|
287
|
-
enforceLimit,
|
|
288
|
-
trackUsage,
|
|
289
265
|
};
|
|
@@ -160,8 +160,26 @@ function handleError(error, context = "", metadata = {}) {
|
|
|
160
160
|
// Get specific guidance
|
|
161
161
|
const guidance = getErrorGuidance(err);
|
|
162
162
|
|
|
163
|
+
// Check for JSON mode (via NO_COLOR env var or explicit check)
|
|
164
|
+
const isJsonMode = process.env.NO_COLOR === '1' || process.env.VIBECHECK_JSON === '1';
|
|
165
|
+
|
|
163
166
|
// Print error header
|
|
164
167
|
if (guidance) {
|
|
168
|
+
if (isJsonMode) {
|
|
169
|
+
// JSON error output
|
|
170
|
+
const errorOutput = {
|
|
171
|
+
success: false,
|
|
172
|
+
error: {
|
|
173
|
+
code: err.code || err.name || 'ERROR',
|
|
174
|
+
message: message,
|
|
175
|
+
type: guidance.title,
|
|
176
|
+
nextSteps: guidance.nextSteps,
|
|
177
|
+
},
|
|
178
|
+
exitCode: err.exitCode || 1
|
|
179
|
+
};
|
|
180
|
+
console.error(JSON.stringify(errorOutput, null, 2));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
165
183
|
console.error(`\n${c.error("✗")} ${c.error(guidance.title)}`);
|
|
166
184
|
console.error(` ${message}`);
|
|
167
185
|
|
|
@@ -171,8 +189,20 @@ function handleError(error, context = "", metadata = {}) {
|
|
|
171
189
|
console.error(` ${c.dim("•")} ${step}`);
|
|
172
190
|
}
|
|
173
191
|
} else {
|
|
192
|
+
// Check for JSON mode
|
|
193
|
+
const isJsonMode = process.env.NO_COLOR === '1' || process.env.VIBECHECK_JSON === '1';
|
|
194
|
+
|
|
174
195
|
// Generic error handling with specific type detection
|
|
175
196
|
if (err.code === "ENOENT") {
|
|
197
|
+
if (isJsonMode) {
|
|
198
|
+
const errorOutput = {
|
|
199
|
+
success: false,
|
|
200
|
+
error: { code: 'ENOENT', message: err.path || message, receipt },
|
|
201
|
+
exitCode: 4
|
|
202
|
+
};
|
|
203
|
+
console.error(JSON.stringify(errorOutput, null, 2));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
176
206
|
console.error(`\n${c.error("✗")} File or directory not found`);
|
|
177
207
|
console.error(` ${err.path || message}`);
|
|
178
208
|
// Print receipt if available
|
|
@@ -213,6 +243,15 @@ function handleError(error, context = "", metadata = {}) {
|
|
|
213
243
|
console.error(` ${c.dim("•")} Verify VIBECHECK_API_URL is correct`);
|
|
214
244
|
} else {
|
|
215
245
|
// Generic error
|
|
246
|
+
if (isJsonMode) {
|
|
247
|
+
const errorOutput = {
|
|
248
|
+
success: false,
|
|
249
|
+
error: { code: err.code || err.name || 'ERROR', message, receipt },
|
|
250
|
+
exitCode: err.exitCode || 1
|
|
251
|
+
};
|
|
252
|
+
console.error(JSON.stringify(errorOutput, null, 2));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
216
255
|
console.error(`\n${c.error("✗")} Error`);
|
|
217
256
|
console.error(` ${message}`);
|
|
218
257
|
// Print receipt if available
|
|
@@ -229,13 +268,15 @@ function handleError(error, context = "", metadata = {}) {
|
|
|
229
268
|
}
|
|
230
269
|
}
|
|
231
270
|
|
|
232
|
-
// Show stack trace in debug mode
|
|
233
|
-
if (process.env.DEBUG || process.env.VIBECHECK_DEBUG) {
|
|
271
|
+
// Show stack trace in debug mode (skip in JSON mode)
|
|
272
|
+
if (!isJsonMode && (process.env.DEBUG || process.env.VIBECHECK_DEBUG)) {
|
|
234
273
|
console.error(`\n${c.dim("Stack trace:")}`);
|
|
235
274
|
console.error(c.dim(err.stack));
|
|
236
275
|
}
|
|
237
276
|
|
|
238
|
-
|
|
277
|
+
if (!isJsonMode) {
|
|
278
|
+
console.error(""); // Empty line for readability
|
|
279
|
+
}
|
|
239
280
|
}
|
|
240
281
|
|
|
241
282
|
/**
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actionable Error Messages
|
|
3
|
+
*
|
|
4
|
+
* Provides standardized error messages with actionable next steps,
|
|
5
|
+
* documentation links, and clear guidance for users.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { EXIT } = require('./exit-codes');
|
|
9
|
+
|
|
10
|
+
const DOCS_BASE_URL = 'https://docs.vibecheckai.dev';
|
|
11
|
+
const DASHBOARD_URL = 'https://app.vibecheckai.dev';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format an actionable error message
|
|
15
|
+
*/
|
|
16
|
+
function formatError(error, context = {}) {
|
|
17
|
+
const {
|
|
18
|
+
command = '',
|
|
19
|
+
suggestion = null,
|
|
20
|
+
docsLink = null,
|
|
21
|
+
nextSteps = [],
|
|
22
|
+
code = null,
|
|
23
|
+
} = context;
|
|
24
|
+
|
|
25
|
+
const lines = [];
|
|
26
|
+
|
|
27
|
+
// Main error message
|
|
28
|
+
lines.push(`\n ❌ ${error.message || error}`);
|
|
29
|
+
|
|
30
|
+
// Error code if provided
|
|
31
|
+
if (code) {
|
|
32
|
+
lines.push(`\n Code: ${code}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Next steps
|
|
36
|
+
if (nextSteps.length > 0) {
|
|
37
|
+
lines.push(`\n Next steps:`);
|
|
38
|
+
nextSteps.forEach((step, i) => {
|
|
39
|
+
lines.push(` ${i + 1}. ${step}`);
|
|
40
|
+
});
|
|
41
|
+
} else if (suggestion) {
|
|
42
|
+
lines.push(`\n 💡 ${suggestion}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Documentation link
|
|
46
|
+
if (docsLink) {
|
|
47
|
+
lines.push(`\n 📖 Docs: ${docsLink}`);
|
|
48
|
+
} else if (command) {
|
|
49
|
+
lines.push(`\n 📖 Docs: ${DOCS_BASE_URL}/commands/${command}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
lines.push('');
|
|
53
|
+
|
|
54
|
+
return lines.join('\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Common error templates with actionable guidance
|
|
59
|
+
*/
|
|
60
|
+
const ERROR_TEMPLATES = {
|
|
61
|
+
PROJECT_NOT_INITIALIZED: {
|
|
62
|
+
message: 'Project not initialized',
|
|
63
|
+
suggestion: 'Run `vibecheck init` to set up your project',
|
|
64
|
+
nextSteps: [
|
|
65
|
+
'Run: vibecheck init',
|
|
66
|
+
'This creates .vibecheckrc config file',
|
|
67
|
+
'Then run: vibecheck scan',
|
|
68
|
+
],
|
|
69
|
+
docsLink: `${DOCS_BASE_URL}/getting-started/init`,
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
NO_SCAN_RESULTS: {
|
|
73
|
+
message: 'No scan results found',
|
|
74
|
+
suggestion: 'Run `vibecheck scan` first to generate results',
|
|
75
|
+
nextSteps: [
|
|
76
|
+
'Run: vibecheck scan',
|
|
77
|
+
'This analyzes your codebase',
|
|
78
|
+
'Then re-run this command',
|
|
79
|
+
],
|
|
80
|
+
docsLink: `${DOCS_BASE_URL}/commands/scan`,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
NO_TRUTHPACK: {
|
|
84
|
+
message: 'No truthpack found',
|
|
85
|
+
suggestion: 'Run `vibecheck context` to generate truthpack',
|
|
86
|
+
nextSteps: [
|
|
87
|
+
'Run: vibecheck context',
|
|
88
|
+
'This generates route/auth/env mapping',
|
|
89
|
+
'Then re-run this command',
|
|
90
|
+
],
|
|
91
|
+
docsLink: `${DOCS_BASE_URL}/commands/context`,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
AUTH_REQUIRED: {
|
|
95
|
+
message: 'Authentication required',
|
|
96
|
+
suggestion: 'Run `vibecheck login` to authenticate',
|
|
97
|
+
nextSteps: [
|
|
98
|
+
'Run: vibecheck login',
|
|
99
|
+
'Enter your API key',
|
|
100
|
+
'Get your key from: https://app.vibecheckai.dev/settings/api-keys',
|
|
101
|
+
],
|
|
102
|
+
docsLink: `${DOCS_BASE_URL}/getting-started/authentication`,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
INVALID_API_KEY: {
|
|
106
|
+
message: 'Invalid API key format',
|
|
107
|
+
suggestion: 'Check your API key format',
|
|
108
|
+
nextSteps: [
|
|
109
|
+
'API keys should start with vc_',
|
|
110
|
+
'Get a new key: https://app.vibecheckai.dev/settings/api-keys',
|
|
111
|
+
'Run: vibecheck login --key YOUR_KEY',
|
|
112
|
+
],
|
|
113
|
+
docsLink: `${DOCS_BASE_URL}/getting-started/authentication`,
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
TIER_REQUIRED: {
|
|
117
|
+
message: 'This feature requires PRO tier',
|
|
118
|
+
suggestion: 'Upgrade to PRO to access this feature',
|
|
119
|
+
nextSteps: [
|
|
120
|
+
'Visit: https://app.vibecheckai.dev/pricing',
|
|
121
|
+
'Upgrade your account',
|
|
122
|
+
'Re-run this command',
|
|
123
|
+
],
|
|
124
|
+
docsLink: `${DOCS_BASE_URL}/pricing`,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
PROJECT_PATH_NOT_FOUND: {
|
|
128
|
+
message: 'Project path does not exist',
|
|
129
|
+
suggestion: 'Check the path and try again',
|
|
130
|
+
nextSteps: [
|
|
131
|
+
'Verify the path exists: ls <path>',
|
|
132
|
+
'Use absolute path if relative path fails',
|
|
133
|
+
'Run: vibecheck init --path <path>',
|
|
134
|
+
],
|
|
135
|
+
docsLink: `${DOCS_BASE_URL}/commands/init`,
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
CONFIG_INVALID: {
|
|
139
|
+
message: 'Invalid configuration file',
|
|
140
|
+
suggestion: 'Run `vibecheck doctor --fix` to repair config',
|
|
141
|
+
nextSteps: [
|
|
142
|
+
'Run: vibecheck doctor --fix',
|
|
143
|
+
'This auto-fixes common config issues',
|
|
144
|
+
'Or manually edit .vibecheckrc',
|
|
145
|
+
],
|
|
146
|
+
docsLink: `${DOCS_BASE_URL}/configuration`,
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
NETWORK_ERROR: {
|
|
150
|
+
message: 'Network connection failed',
|
|
151
|
+
suggestion: 'Check your internet connection or use --offline mode',
|
|
152
|
+
nextSteps: [
|
|
153
|
+
'Check internet connection',
|
|
154
|
+
'Or run with --offline flag for local-only mode',
|
|
155
|
+
'Example: vibecheck scan --offline',
|
|
156
|
+
],
|
|
157
|
+
docsLink: `${DOCS_BASE_URL}/commands/scan#offline-mode`,
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
MISSING_DEPENDENCY: {
|
|
161
|
+
message: 'Missing required dependency',
|
|
162
|
+
suggestion: 'Install missing dependencies',
|
|
163
|
+
nextSteps: [
|
|
164
|
+
'Run: npm install',
|
|
165
|
+
'Or: pnpm install',
|
|
166
|
+
'Check package.json for required packages',
|
|
167
|
+
],
|
|
168
|
+
docsLink: `${DOCS_BASE_URL}/troubleshooting/dependencies`,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get error template by key
|
|
174
|
+
*/
|
|
175
|
+
function getErrorTemplate(key, overrides = {}) {
|
|
176
|
+
const template = ERROR_TEMPLATES[key];
|
|
177
|
+
if (!template) {
|
|
178
|
+
return {
|
|
179
|
+
message: key,
|
|
180
|
+
suggestion: 'Check the documentation for help',
|
|
181
|
+
docsLink: DOCS_BASE_URL,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return { ...template, ...overrides };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Print actionable error and return exit code
|
|
190
|
+
*/
|
|
191
|
+
function printActionableError(errorKey, context = {}) {
|
|
192
|
+
const template = getErrorTemplate(errorKey, context);
|
|
193
|
+
const error = {
|
|
194
|
+
message: template.message,
|
|
195
|
+
code: context.code || errorKey,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const formatted = formatError(error, {
|
|
199
|
+
...context,
|
|
200
|
+
suggestion: template.suggestion,
|
|
201
|
+
nextSteps: template.nextSteps || [],
|
|
202
|
+
docsLink: template.docsLink,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
console.error(formatted);
|
|
206
|
+
|
|
207
|
+
// Map error keys to exit codes
|
|
208
|
+
const exitCodeMap = {
|
|
209
|
+
AUTH_REQUIRED: EXIT.AUTH_REQUIRED,
|
|
210
|
+
INVALID_API_KEY: EXIT.AUTH_FAILED,
|
|
211
|
+
TIER_REQUIRED: EXIT.TIER_REQUIRED,
|
|
212
|
+
PROJECT_PATH_NOT_FOUND: EXIT.USER_ERROR,
|
|
213
|
+
CONFIG_INVALID: EXIT.USER_ERROR,
|
|
214
|
+
NETWORK_ERROR: EXIT.NETWORK_ERROR || 1,
|
|
215
|
+
MISSING_DEPENDENCY: EXIT.USER_ERROR,
|
|
216
|
+
PROJECT_NOT_INITIALIZED: EXIT.USER_ERROR,
|
|
217
|
+
NO_SCAN_RESULTS: EXIT.NOT_FOUND || 1,
|
|
218
|
+
NO_TRUTHPACK: EXIT.NOT_FOUND || 1,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return exitCodeMap[errorKey] || EXIT.USER_ERROR;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Enhance existing error with actionable guidance
|
|
226
|
+
*/
|
|
227
|
+
function enhanceError(error, command = '', additionalContext = {}) {
|
|
228
|
+
const errorMessage = error.message || String(error);
|
|
229
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
230
|
+
|
|
231
|
+
// Detect error type from message
|
|
232
|
+
if (lowerMessage.includes('not initialized') || lowerMessage.includes('no config')) {
|
|
233
|
+
return printActionableError('PROJECT_NOT_INITIALIZED', { command, ...additionalContext });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (lowerMessage.includes('scan result') || lowerMessage.includes('no results')) {
|
|
237
|
+
return printActionableError('NO_SCAN_RESULTS', { command, ...additionalContext });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (lowerMessage.includes('truthpack') || lowerMessage.includes('context')) {
|
|
241
|
+
return printActionableError('NO_TRUTHPACK', { command, ...additionalContext });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (lowerMessage.includes('auth') && (lowerMessage.includes('required') || lowerMessage.includes('unauthorized'))) {
|
|
245
|
+
return printActionableError('AUTH_REQUIRED', { command, ...additionalContext });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (lowerMessage.includes('api key') && (lowerMessage.includes('invalid') || lowerMessage.includes('format'))) {
|
|
249
|
+
return printActionableError('INVALID_API_KEY', { command, ...additionalContext });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (lowerMessage.includes('tier') || lowerMessage.includes('pro') || lowerMessage.includes('upgrade')) {
|
|
253
|
+
return printActionableError('TIER_REQUIRED', { command, ...additionalContext });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (lowerMessage.includes('path') && (lowerMessage.includes('not found') || lowerMessage.includes('does not exist'))) {
|
|
257
|
+
return printActionableError('PROJECT_PATH_NOT_FOUND', { command, path: additionalContext.path });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (lowerMessage.includes('config') && (lowerMessage.includes('invalid') || lowerMessage.includes('error'))) {
|
|
261
|
+
return printActionableError('CONFIG_INVALID', { command, ...additionalContext });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (lowerMessage.includes('network') || lowerMessage.includes('connection') || lowerMessage.includes('fetch')) {
|
|
265
|
+
return printActionableError('NETWORK_ERROR', { command, ...additionalContext });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (lowerMessage.includes('cannot find module') || lowerMessage.includes('missing')) {
|
|
269
|
+
return printActionableError('MISSING_DEPENDENCY', { command, dependency: additionalContext.dependency });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Default: print enhanced error
|
|
273
|
+
console.error(formatError(error, {
|
|
274
|
+
command,
|
|
275
|
+
suggestion: additionalContext.suggestion || 'Check the documentation for help',
|
|
276
|
+
docsLink: additionalContext.docsLink || `${DOCS_BASE_URL}/commands/${command}`,
|
|
277
|
+
nextSteps: additionalContext.nextSteps || [],
|
|
278
|
+
}));
|
|
279
|
+
|
|
280
|
+
return EXIT.USER_ERROR;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = {
|
|
284
|
+
formatError,
|
|
285
|
+
getErrorTemplate,
|
|
286
|
+
printActionableError,
|
|
287
|
+
enhanceError,
|
|
288
|
+
ERROR_TEMPLATES,
|
|
289
|
+
};
|
|
@@ -14,7 +14,13 @@
|
|
|
14
14
|
const fs = require("fs");
|
|
15
15
|
const path = require("path");
|
|
16
16
|
const crypto = require("crypto");
|
|
17
|
-
|
|
17
|
+
// Make archiver optional - only required for zip creation
|
|
18
|
+
let archiver;
|
|
19
|
+
try {
|
|
20
|
+
archiver = require("archiver");
|
|
21
|
+
} catch {
|
|
22
|
+
archiver = null;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
20
26
|
// EVIDENCE PACK SCHEMA
|