@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,84 +1,84 @@
|
|
|
1
|
-
// bin/runners/lib/reality-findings.js
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
|
|
5
|
-
function loadLastReality(repoRoot, maxAgeHours = 12) {
|
|
6
|
-
const p = path.join(repoRoot, ".vibecheck", "reality", "last_reality.json");
|
|
7
|
-
if (!fs.existsSync(p)) return null;
|
|
8
|
-
|
|
9
|
-
const obj = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
10
|
-
const finishedAt = obj?.meta?.finishedAt ? new Date(obj.meta.finishedAt).getTime() : 0;
|
|
11
|
-
if (!finishedAt) return null;
|
|
12
|
-
|
|
13
|
-
const ageMs = Date.now() - finishedAt;
|
|
14
|
-
if (ageMs > maxAgeHours * 3600 * 1000) return null; // stale
|
|
15
|
-
|
|
16
|
-
return obj;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function findingsFromReality(repoRoot) {
|
|
20
|
-
const r = loadLastReality(repoRoot);
|
|
21
|
-
if (!r) return [];
|
|
22
|
-
|
|
23
|
-
const out = [];
|
|
24
|
-
for (const f of r.findings || []) {
|
|
25
|
-
// Preserve original category (DeadUI, AuthCoverage, etc.)
|
|
26
|
-
const category = f.category || "DeadUI";
|
|
27
|
-
|
|
28
|
-
// Generate appropriate fix hints based on category
|
|
29
|
-
let fixHints;
|
|
30
|
-
if (category === "AuthCoverage") {
|
|
31
|
-
fixHints = [
|
|
32
|
-
"Verify your Next.js matcher config protects this route.",
|
|
33
|
-
"Ensure middleware redirects unauthenticated users.",
|
|
34
|
-
"Check that session/auth state is being validated server-side."
|
|
35
|
-
];
|
|
36
|
-
} else {
|
|
37
|
-
fixHints = [
|
|
38
|
-
"Ensure the element has a real onClick/handler and it returns a real result.",
|
|
39
|
-
"If it calls /api/*, ensure the route exists and returns success only on res.ok.",
|
|
40
|
-
"If it's intentionally disabled, reflect that visually and remove the click affordance."
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
out.push({
|
|
45
|
-
id: `F_REALITY_${f.id}`,
|
|
46
|
-
severity: f.severity === "BLOCK" ? "BLOCK" : "WARN",
|
|
47
|
-
category,
|
|
48
|
-
title: f.title,
|
|
49
|
-
why: category === "AuthCoverage"
|
|
50
|
-
? "Reality Runner detected an auth boundary violation."
|
|
51
|
-
: "Reality Runner found a UI interaction that fails in the running app.",
|
|
52
|
-
confidence: "high",
|
|
53
|
-
evidence: [
|
|
54
|
-
{
|
|
55
|
-
id: `ev_${f.id}`,
|
|
56
|
-
file: f.page ? String(f.page).replace(/^https?:\/\//, "") : "runtime",
|
|
57
|
-
lines: "0-0",
|
|
58
|
-
snippetHash: (f.screenshot || "runtime").slice(0, 80),
|
|
59
|
-
reason: f.reason
|
|
60
|
-
}
|
|
61
|
-
],
|
|
62
|
-
fixHints
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// escalate raw runtime console errors into WARN
|
|
67
|
-
const ce = (r.consoleErrors || []).slice(0, 5);
|
|
68
|
-
if (ce.length) {
|
|
69
|
-
out.push({
|
|
70
|
-
id: "F_REALITY_CONSOLE_ERRORS",
|
|
71
|
-
severity: "WARN",
|
|
72
|
-
category: "DeadUI",
|
|
73
|
-
title: `Console errors observed during Reality run (${ce.length})`,
|
|
74
|
-
why: "Console errors often correlate with broken UI handlers or missing data paths.",
|
|
75
|
-
confidence: "med",
|
|
76
|
-
evidence: [],
|
|
77
|
-
fixHints: ["Open the Reality report and fix the first error in the consoleErrors list."]
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
module.exports = { findingsFromReality };
|
|
1
|
+
// bin/runners/lib/reality-findings.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
function loadLastReality(repoRoot, maxAgeHours = 12) {
|
|
6
|
+
const p = path.join(repoRoot, ".vibecheck", "reality", "last_reality.json");
|
|
7
|
+
if (!fs.existsSync(p)) return null;
|
|
8
|
+
|
|
9
|
+
const obj = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
10
|
+
const finishedAt = obj?.meta?.finishedAt ? new Date(obj.meta.finishedAt).getTime() : 0;
|
|
11
|
+
if (!finishedAt) return null;
|
|
12
|
+
|
|
13
|
+
const ageMs = Date.now() - finishedAt;
|
|
14
|
+
if (ageMs > maxAgeHours * 3600 * 1000) return null; // stale
|
|
15
|
+
|
|
16
|
+
return obj;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function findingsFromReality(repoRoot) {
|
|
20
|
+
const r = loadLastReality(repoRoot);
|
|
21
|
+
if (!r) return [];
|
|
22
|
+
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const f of r.findings || []) {
|
|
25
|
+
// Preserve original category (DeadUI, AuthCoverage, etc.)
|
|
26
|
+
const category = f.category || "DeadUI";
|
|
27
|
+
|
|
28
|
+
// Generate appropriate fix hints based on category
|
|
29
|
+
let fixHints;
|
|
30
|
+
if (category === "AuthCoverage") {
|
|
31
|
+
fixHints = [
|
|
32
|
+
"Verify your Next.js matcher config protects this route.",
|
|
33
|
+
"Ensure middleware redirects unauthenticated users.",
|
|
34
|
+
"Check that session/auth state is being validated server-side."
|
|
35
|
+
];
|
|
36
|
+
} else {
|
|
37
|
+
fixHints = [
|
|
38
|
+
"Ensure the element has a real onClick/handler and it returns a real result.",
|
|
39
|
+
"If it calls /api/*, ensure the route exists and returns success only on res.ok.",
|
|
40
|
+
"If it's intentionally disabled, reflect that visually and remove the click affordance."
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
out.push({
|
|
45
|
+
id: `F_REALITY_${f.id}`,
|
|
46
|
+
severity: f.severity === "BLOCK" ? "BLOCK" : "WARN",
|
|
47
|
+
category,
|
|
48
|
+
title: f.title,
|
|
49
|
+
why: category === "AuthCoverage"
|
|
50
|
+
? "Reality Runner detected an auth boundary violation."
|
|
51
|
+
: "Reality Runner found a UI interaction that fails in the running app.",
|
|
52
|
+
confidence: "high",
|
|
53
|
+
evidence: [
|
|
54
|
+
{
|
|
55
|
+
id: `ev_${f.id}`,
|
|
56
|
+
file: f.page ? String(f.page).replace(/^https?:\/\//, "") : "runtime",
|
|
57
|
+
lines: "0-0",
|
|
58
|
+
snippetHash: (f.screenshot || "runtime").slice(0, 80),
|
|
59
|
+
reason: f.reason
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
fixHints
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// escalate raw runtime console errors into WARN
|
|
67
|
+
const ce = (r.consoleErrors || []).slice(0, 5);
|
|
68
|
+
if (ce.length) {
|
|
69
|
+
out.push({
|
|
70
|
+
id: "F_REALITY_CONSOLE_ERRORS",
|
|
71
|
+
severity: "WARN",
|
|
72
|
+
category: "DeadUI",
|
|
73
|
+
title: `Console errors observed during Reality run (${ce.length})`,
|
|
74
|
+
why: "Console errors often correlate with broken UI handlers or missing data paths.",
|
|
75
|
+
confidence: "med",
|
|
76
|
+
evidence: [],
|
|
77
|
+
fixHints: ["Open the Reality report and fix the first error in the consoleErrors list."]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { findingsFromReality };
|
|
@@ -1,179 +1,179 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receipts and Manifests Generation
|
|
3
|
-
*
|
|
4
|
-
* Creates manifest.json and receipt.json for paid command runs.
|
|
5
|
-
* Required for audit trail and billing verification.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
"use strict";
|
|
9
|
-
|
|
10
|
-
const fs = require("fs");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
const crypto = require("crypto");
|
|
13
|
-
const { logUsage } = require("./usage");
|
|
14
|
-
|
|
15
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
16
|
-
// RECEIPT GENERATION
|
|
17
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
-
|
|
19
|
-
function getRunDir(runId) {
|
|
20
|
-
const runDir = path.join(process.cwd(), ".vibecheck", "runs", runId);
|
|
21
|
-
const receiptsDir = path.join(process.cwd(), ".vibecheck", "receipts");
|
|
22
|
-
|
|
23
|
-
// Ensure directories exist
|
|
24
|
-
if (!fs.existsSync(runDir)) fs.mkdirSync(runDir, { recursive: true });
|
|
25
|
-
if (!fs.existsSync(receiptsDir)) fs.mkdirSync(receiptsDir, { recursive: true });
|
|
26
|
-
|
|
27
|
-
return { runDir, receiptsDir };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Get git commit hash if available
|
|
32
|
-
*/
|
|
33
|
-
function getGitInfo() {
|
|
34
|
-
try {
|
|
35
|
-
const gitDir = path.join(process.cwd(), ".git");
|
|
36
|
-
if (!fs.existsSync(gitDir)) return null;
|
|
37
|
-
|
|
38
|
-
const headFile = path.join(gitDir, "HEAD");
|
|
39
|
-
if (!fs.existsSync(headFile)) return null;
|
|
40
|
-
|
|
41
|
-
const headRef = fs.readFileSync(headFile, "utf8").trim();
|
|
42
|
-
const refMatch = headRef.match(/^ref: (.+)$/);
|
|
43
|
-
|
|
44
|
-
if (refMatch) {
|
|
45
|
-
const refFile = path.join(gitDir, refMatch[1]);
|
|
46
|
-
if (fs.existsSync(refFile)) {
|
|
47
|
-
return fs.readFileSync(refFile, "utf8").trim();
|
|
48
|
-
}
|
|
49
|
-
} else {
|
|
50
|
-
// Detached HEAD
|
|
51
|
-
return headRef;
|
|
52
|
-
}
|
|
53
|
-
} catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Calculate hash of manifest for receipt integrity
|
|
60
|
-
*/
|
|
61
|
-
function calculateManifestHash(manifest) {
|
|
62
|
-
const manifestString = JSON.stringify(manifest, Object.keys(manifest).sort());
|
|
63
|
-
return crypto.createHash("sha256").update(manifestString).digest("hex");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Create manifest and receipt for a command run
|
|
68
|
-
*/
|
|
69
|
-
async function createManifestAndReceipt(options) {
|
|
70
|
-
const { runId, command, args, tier, exitCode, startTime, endTime, projectPath } = options;
|
|
71
|
-
const { runDir, receiptsDir } = getRunDir(runId);
|
|
72
|
-
|
|
73
|
-
// Create manifest
|
|
74
|
-
const manifest = {
|
|
75
|
-
runId,
|
|
76
|
-
command,
|
|
77
|
-
args,
|
|
78
|
-
timestamp: startTime,
|
|
79
|
-
duration: endTime ? new Date(endTime).getTime() - new Date(startTime).getTime() : null,
|
|
80
|
-
project: {
|
|
81
|
-
path: projectPath,
|
|
82
|
-
git: getGitInfo(),
|
|
83
|
-
branch: process.env.GIT_BRANCH || null,
|
|
84
|
-
},
|
|
85
|
-
environment: {
|
|
86
|
-
nodeVersion: process.version,
|
|
87
|
-
platform: process.platform,
|
|
88
|
-
arch: process.arch,
|
|
89
|
-
},
|
|
90
|
-
execution: {
|
|
91
|
-
exitCode,
|
|
92
|
-
endTime,
|
|
93
|
-
},
|
|
94
|
-
outputs: [], // To be filled by runners if needed
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Save manifest
|
|
98
|
-
const manifestPath = path.join(runDir, "manifest.json");
|
|
99
|
-
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
100
|
-
|
|
101
|
-
// Create receipt
|
|
102
|
-
const manifestHash = calculateManifestHash(manifest);
|
|
103
|
-
const receipt = {
|
|
104
|
-
runId,
|
|
105
|
-
command,
|
|
106
|
-
tier,
|
|
107
|
-
timestamp: new Date().toISOString(),
|
|
108
|
-
manifestHash,
|
|
109
|
-
verification: {
|
|
110
|
-
algorithm: "SHA-256",
|
|
111
|
-
signature: "TODO: Implement server-side signing",
|
|
112
|
-
},
|
|
113
|
-
billing: {
|
|
114
|
-
tier,
|
|
115
|
-
charged: false, // TODO: Check with backend
|
|
116
|
-
usage: {
|
|
117
|
-
// Units will be populated by individual runners
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// Save receipt
|
|
123
|
-
const receiptPath = path.join(receiptsDir, `${runId}.json`);
|
|
124
|
-
fs.writeFileSync(receiptPath, JSON.stringify(receipt, null, 2));
|
|
125
|
-
|
|
126
|
-
// Log usage (if units are provided)
|
|
127
|
-
if (options.units) {
|
|
128
|
-
logUsage({
|
|
129
|
-
runId,
|
|
130
|
-
command,
|
|
131
|
-
tier,
|
|
132
|
-
units: options.units,
|
|
133
|
-
timestamp: startTime,
|
|
134
|
-
projectPath,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return { manifest, receipt, manifestPath, receiptPath };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Verify a receipt against its manifest
|
|
143
|
-
*/
|
|
144
|
-
function verifyReceipt(receiptPath) {
|
|
145
|
-
try {
|
|
146
|
-
const receipt = JSON.parse(fs.readFileSync(receiptPath, "utf8"));
|
|
147
|
-
const manifestPath = path.join(
|
|
148
|
-
process.cwd(),
|
|
149
|
-
".vibecheck",
|
|
150
|
-
"runs",
|
|
151
|
-
receipt.runId,
|
|
152
|
-
"manifest.json"
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
if (!fs.existsSync(manifestPath)) {
|
|
156
|
-
return { valid: false, error: "Manifest not found" };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
160
|
-
const calculatedHash = calculateManifestHash(manifest);
|
|
161
|
-
|
|
162
|
-
if (calculatedHash !== receipt.manifestHash) {
|
|
163
|
-
return { valid: false, error: "Manifest hash mismatch" };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return { valid: true, receipt, manifest };
|
|
167
|
-
} catch (error) {
|
|
168
|
-
return { valid: false, error: error.message };
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
173
|
-
// EXPORTS
|
|
174
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
175
|
-
module.exports = {
|
|
176
|
-
createManifestAndReceipt,
|
|
177
|
-
verifyReceipt,
|
|
178
|
-
calculateManifestHash,
|
|
179
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Receipts and Manifests Generation
|
|
3
|
+
*
|
|
4
|
+
* Creates manifest.json and receipt.json for paid command runs.
|
|
5
|
+
* Required for audit trail and billing verification.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const crypto = require("crypto");
|
|
13
|
+
const { logUsage } = require("./usage");
|
|
14
|
+
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
16
|
+
// RECEIPT GENERATION
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
+
|
|
19
|
+
function getRunDir(runId) {
|
|
20
|
+
const runDir = path.join(process.cwd(), ".vibecheck", "runs", runId);
|
|
21
|
+
const receiptsDir = path.join(process.cwd(), ".vibecheck", "receipts");
|
|
22
|
+
|
|
23
|
+
// Ensure directories exist
|
|
24
|
+
if (!fs.existsSync(runDir)) fs.mkdirSync(runDir, { recursive: true });
|
|
25
|
+
if (!fs.existsSync(receiptsDir)) fs.mkdirSync(receiptsDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
return { runDir, receiptsDir };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get git commit hash if available
|
|
32
|
+
*/
|
|
33
|
+
function getGitInfo() {
|
|
34
|
+
try {
|
|
35
|
+
const gitDir = path.join(process.cwd(), ".git");
|
|
36
|
+
if (!fs.existsSync(gitDir)) return null;
|
|
37
|
+
|
|
38
|
+
const headFile = path.join(gitDir, "HEAD");
|
|
39
|
+
if (!fs.existsSync(headFile)) return null;
|
|
40
|
+
|
|
41
|
+
const headRef = fs.readFileSync(headFile, "utf8").trim();
|
|
42
|
+
const refMatch = headRef.match(/^ref: (.+)$/);
|
|
43
|
+
|
|
44
|
+
if (refMatch) {
|
|
45
|
+
const refFile = path.join(gitDir, refMatch[1]);
|
|
46
|
+
if (fs.existsSync(refFile)) {
|
|
47
|
+
return fs.readFileSync(refFile, "utf8").trim();
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
// Detached HEAD
|
|
51
|
+
return headRef;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Calculate hash of manifest for receipt integrity
|
|
60
|
+
*/
|
|
61
|
+
function calculateManifestHash(manifest) {
|
|
62
|
+
const manifestString = JSON.stringify(manifest, Object.keys(manifest).sort());
|
|
63
|
+
return crypto.createHash("sha256").update(manifestString).digest("hex");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create manifest and receipt for a command run
|
|
68
|
+
*/
|
|
69
|
+
async function createManifestAndReceipt(options) {
|
|
70
|
+
const { runId, command, args, tier, exitCode, startTime, endTime, projectPath } = options;
|
|
71
|
+
const { runDir, receiptsDir } = getRunDir(runId);
|
|
72
|
+
|
|
73
|
+
// Create manifest
|
|
74
|
+
const manifest = {
|
|
75
|
+
runId,
|
|
76
|
+
command,
|
|
77
|
+
args,
|
|
78
|
+
timestamp: startTime,
|
|
79
|
+
duration: endTime ? new Date(endTime).getTime() - new Date(startTime).getTime() : null,
|
|
80
|
+
project: {
|
|
81
|
+
path: projectPath,
|
|
82
|
+
git: getGitInfo(),
|
|
83
|
+
branch: process.env.GIT_BRANCH || null,
|
|
84
|
+
},
|
|
85
|
+
environment: {
|
|
86
|
+
nodeVersion: process.version,
|
|
87
|
+
platform: process.platform,
|
|
88
|
+
arch: process.arch,
|
|
89
|
+
},
|
|
90
|
+
execution: {
|
|
91
|
+
exitCode,
|
|
92
|
+
endTime,
|
|
93
|
+
},
|
|
94
|
+
outputs: [], // To be filled by runners if needed
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Save manifest
|
|
98
|
+
const manifestPath = path.join(runDir, "manifest.json");
|
|
99
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
100
|
+
|
|
101
|
+
// Create receipt
|
|
102
|
+
const manifestHash = calculateManifestHash(manifest);
|
|
103
|
+
const receipt = {
|
|
104
|
+
runId,
|
|
105
|
+
command,
|
|
106
|
+
tier,
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
manifestHash,
|
|
109
|
+
verification: {
|
|
110
|
+
algorithm: "SHA-256",
|
|
111
|
+
signature: "TODO: Implement server-side signing",
|
|
112
|
+
},
|
|
113
|
+
billing: {
|
|
114
|
+
tier,
|
|
115
|
+
charged: false, // TODO: Check with backend
|
|
116
|
+
usage: {
|
|
117
|
+
// Units will be populated by individual runners
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Save receipt
|
|
123
|
+
const receiptPath = path.join(receiptsDir, `${runId}.json`);
|
|
124
|
+
fs.writeFileSync(receiptPath, JSON.stringify(receipt, null, 2));
|
|
125
|
+
|
|
126
|
+
// Log usage (if units are provided)
|
|
127
|
+
if (options.units) {
|
|
128
|
+
logUsage({
|
|
129
|
+
runId,
|
|
130
|
+
command,
|
|
131
|
+
tier,
|
|
132
|
+
units: options.units,
|
|
133
|
+
timestamp: startTime,
|
|
134
|
+
projectPath,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { manifest, receipt, manifestPath, receiptPath };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Verify a receipt against its manifest
|
|
143
|
+
*/
|
|
144
|
+
function verifyReceipt(receiptPath) {
|
|
145
|
+
try {
|
|
146
|
+
const receipt = JSON.parse(fs.readFileSync(receiptPath, "utf8"));
|
|
147
|
+
const manifestPath = path.join(
|
|
148
|
+
process.cwd(),
|
|
149
|
+
".vibecheck",
|
|
150
|
+
"runs",
|
|
151
|
+
receipt.runId,
|
|
152
|
+
"manifest.json"
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (!fs.existsSync(manifestPath)) {
|
|
156
|
+
return { valid: false, error: "Manifest not found" };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
160
|
+
const calculatedHash = calculateManifestHash(manifest);
|
|
161
|
+
|
|
162
|
+
if (calculatedHash !== receipt.manifestHash) {
|
|
163
|
+
return { valid: false, error: "Manifest hash mismatch" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { valid: true, receipt, manifest };
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return { valid: false, error: error.message };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
173
|
+
// EXPORTS
|
|
174
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
175
|
+
module.exports = {
|
|
176
|
+
createManifestAndReceipt,
|
|
177
|
+
verifyReceipt,
|
|
178
|
+
calculateManifestHash,
|
|
179
|
+
};
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
// bin/runners/lib/redact.js
|
|
2
|
-
function redactString(s) {
|
|
3
|
-
if (typeof s !== "string") return s;
|
|
4
|
-
|
|
5
|
-
return s
|
|
6
|
-
.replace(/(api[_-]?key|secret|token|password)\s*[:=]\s*["']?([^"'\s]{6,})["']?/gi, "$1: [REDACTED]")
|
|
7
|
-
.replace(/(bearer)\s+([a-z0-9_\-\.=]{12,})/gi, "$1 [REDACTED]")
|
|
8
|
-
.replace(/sk-[a-z0-9]{16,}/gi, "[REDACTED]")
|
|
9
|
-
.replace(/[a-z0-9+\/=]{80,}/gi, "[REDACTED_BLOB]");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function redactObject(obj) {
|
|
13
|
-
if (obj == null) return obj;
|
|
14
|
-
if (typeof obj === "string") return redactString(obj);
|
|
15
|
-
if (typeof obj !== "object") return obj;
|
|
16
|
-
if (Array.isArray(obj)) return obj.map(redactObject);
|
|
17
|
-
|
|
18
|
-
const out = {};
|
|
19
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
20
|
-
if (/api[_-]?key|secret|token|password|private/i.test(k)) {
|
|
21
|
-
out[k] = "[REDACTED]";
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
out[k] = redactObject(v);
|
|
25
|
-
}
|
|
26
|
-
return out;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = { redactObject };
|
|
1
|
+
// bin/runners/lib/redact.js
|
|
2
|
+
function redactString(s) {
|
|
3
|
+
if (typeof s !== "string") return s;
|
|
4
|
+
|
|
5
|
+
return s
|
|
6
|
+
.replace(/(api[_-]?key|secret|token|password)\s*[:=]\s*["']?([^"'\s]{6,})["']?/gi, "$1: [REDACTED]")
|
|
7
|
+
.replace(/(bearer)\s+([a-z0-9_\-\.=]{12,})/gi, "$1 [REDACTED]")
|
|
8
|
+
.replace(/sk-[a-z0-9]{16,}/gi, "[REDACTED]")
|
|
9
|
+
.replace(/[a-z0-9+\/=]{80,}/gi, "[REDACTED_BLOB]");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function redactObject(obj) {
|
|
13
|
+
if (obj == null) return obj;
|
|
14
|
+
if (typeof obj === "string") return redactString(obj);
|
|
15
|
+
if (typeof obj !== "object") return obj;
|
|
16
|
+
if (Array.isArray(obj)) return obj.map(redactObject);
|
|
17
|
+
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20
|
+
if (/api[_-]?key|secret|token|password|private/i.test(k)) {
|
|
21
|
+
out[k] = "[REDACTED]";
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
out[k] = redactObject(v);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { redactObject };
|