@vibecheckai/cli 3.2.5 → 3.3.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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/registry.js +192 -5
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
- package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
- package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
- package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
- package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
- package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
- package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
- package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
- package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
- package/bin/runners/lib/agent-firewall/logger.js +141 -0
- package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
- package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
- package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
- package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
- package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
- package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
- package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
- package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
- package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
- package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
- package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
- package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
- package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
- package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
- package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
- package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
- package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
- package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
- package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
- package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
- package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
- package/bin/runners/lib/analyzers.js +81 -18
- package/bin/runners/lib/api-client.js +269 -0
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/authority-badge.js +425 -0
- 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-output.js +7 -1
- 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/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/error-handler.js +16 -9
- package/bin/runners/lib/exit-codes.js +275 -0
- 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 +37 -0
- 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/help-formatter.js +413 -0
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/logger.js +38 -0
- 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.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/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/unified-cli-output.js +604 -0
- package/bin/runners/lib/upsell.js +658 -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/runAgent.d.ts +5 -0
- package/bin/runners/runApprove.js +1200 -0
- package/bin/runners/runAuth.js +324 -95
- package/bin/runners/runCheckpoint.js +39 -21
- package/bin/runners/runClassify.js +859 -0
- package/bin/runners/runContext.js +136 -24
- package/bin/runners/runDoctor.js +108 -68
- package/bin/runners/runFirewall.d.ts +5 -0
- package/bin/runners/runFirewallHook.d.ts +5 -0
- package/bin/runners/runFix.js +6 -5
- package/bin/runners/runGuard.js +262 -168
- package/bin/runners/runInit.js +3 -2
- package/bin/runners/runMcp.js +130 -52
- package/bin/runners/runPolish.js +43 -20
- package/bin/runners/runProve.js +1 -2
- package/bin/runners/runReport.js +3 -2
- package/bin/runners/runScan.js +145 -44
- package/bin/runners/runShip.js +3 -4
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/runners/runValidate.js +19 -2
- package/bin/runners/runWatch.js +104 -53
- package/bin/vibecheck.js +106 -19
- package/mcp-server/HARDENING_SUMMARY.md +299 -0
- package/mcp-server/agent-firewall-interceptor.js +367 -31
- package/mcp-server/authority-tools.js +569 -0
- package/mcp-server/conductor/conflict-resolver.js +588 -0
- package/mcp-server/conductor/execution-planner.js +544 -0
- package/mcp-server/conductor/index.js +377 -0
- package/mcp-server/conductor/lock-manager.js +615 -0
- package/mcp-server/conductor/request-queue.js +550 -0
- package/mcp-server/conductor/session-manager.js +500 -0
- package/mcp-server/conductor/tools.js +510 -0
- package/mcp-server/index.js +1199 -208
- package/mcp-server/lib/api-client.cjs +305 -0
- package/mcp-server/lib/logger.cjs +30 -0
- package/mcp-server/logger.js +173 -0
- package/mcp-server/package.json +2 -2
- package/mcp-server/premium-tools.js +2 -2
- package/mcp-server/tier-auth.js +351 -136
- package/mcp-server/tools/index.js +72 -72
- package/mcp-server/truth-firewall-tools.js +145 -15
- package/mcp-server/vibecheck-tools.js +2 -2
- package/package.json +2 -3
- package/mcp-server/index.old.js +0 -4137
- package/mcp-server/package-lock.json +0 -165
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
// bin/runners/lib/env-template.js
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
|
|
5
|
-
function ensureDir(p) {
|
|
6
|
-
fs.mkdirSync(p, { recursive: true });
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function pickEnvTemplatePath(root) {
|
|
10
|
-
const candidates = [".env.template", ".env.example", ".env.sample"];
|
|
11
|
-
for (const c of candidates) {
|
|
12
|
-
const abs = path.join(root, c);
|
|
13
|
-
if (fs.existsSync(abs)) return c;
|
|
14
|
-
}
|
|
15
|
-
return ".env.template";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function normalizeEnvName(n) {
|
|
19
|
-
return String(n || "").trim();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function parseDeclaredEnvFromText(text) {
|
|
23
|
-
const declared = new Set();
|
|
24
|
-
const lines = String(text || "").split(/\r?\n/);
|
|
25
|
-
for (const l of lines) {
|
|
26
|
-
if (!l || l.trim().startsWith("#")) continue;
|
|
27
|
-
const m = l.match(/^([A-Z0-9_]+)\s*=/);
|
|
28
|
-
if (m) declared.add(m[1]);
|
|
29
|
-
}
|
|
30
|
-
return declared;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function writeEnvTemplateFromTruthpack(root, truthpack) {
|
|
34
|
-
const outRel = pickEnvTemplatePath(root);
|
|
35
|
-
const outAbs = path.join(root, outRel);
|
|
36
|
-
|
|
37
|
-
const used = new Set((truthpack?.env?.vars || []).map(normalizeEnvName).filter(Boolean));
|
|
38
|
-
|
|
39
|
-
let existingText = "";
|
|
40
|
-
if (fs.existsSync(outAbs)) existingText = fs.readFileSync(outAbs, "utf8");
|
|
41
|
-
|
|
42
|
-
const declared = parseDeclaredEnvFromText(existingText);
|
|
43
|
-
const missing = Array.from(used).filter((x) => !declared.has(x)).sort();
|
|
44
|
-
|
|
45
|
-
if (!missing.length && fs.existsSync(outAbs)) {
|
|
46
|
-
return { outRel, wrote: false, added: [] };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const header =
|
|
50
|
-
`# vibecheck env template
|
|
51
|
-
# This file is generated/extended from REAL env usage found in your code.
|
|
52
|
-
# Fill values in your real .env (never commit secrets).
|
|
53
|
-
|
|
54
|
-
`;
|
|
55
|
-
|
|
56
|
-
const additions = missing.map((k) => `${k}=\n`).join("");
|
|
57
|
-
|
|
58
|
-
const nextText = (existingText.trim().length ? existingText.trimEnd() + "\n\n" : header) + additions;
|
|
59
|
-
|
|
60
|
-
ensureDir(path.dirname(outAbs));
|
|
61
|
-
fs.writeFileSync(outAbs, nextText, "utf8");
|
|
62
|
-
|
|
63
|
-
return { outRel, wrote: true, added: missing };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = { writeEnvTemplateFromTruthpack };
|
|
1
|
+
// bin/runners/lib/env-template.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
function ensureDir(p) {
|
|
6
|
+
fs.mkdirSync(p, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function pickEnvTemplatePath(root) {
|
|
10
|
+
const candidates = [".env.template", ".env.example", ".env.sample"];
|
|
11
|
+
for (const c of candidates) {
|
|
12
|
+
const abs = path.join(root, c);
|
|
13
|
+
if (fs.existsSync(abs)) return c;
|
|
14
|
+
}
|
|
15
|
+
return ".env.template";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeEnvName(n) {
|
|
19
|
+
return String(n || "").trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseDeclaredEnvFromText(text) {
|
|
23
|
+
const declared = new Set();
|
|
24
|
+
const lines = String(text || "").split(/\r?\n/);
|
|
25
|
+
for (const l of lines) {
|
|
26
|
+
if (!l || l.trim().startsWith("#")) continue;
|
|
27
|
+
const m = l.match(/^([A-Z0-9_]+)\s*=/);
|
|
28
|
+
if (m) declared.add(m[1]);
|
|
29
|
+
}
|
|
30
|
+
return declared;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeEnvTemplateFromTruthpack(root, truthpack) {
|
|
34
|
+
const outRel = pickEnvTemplatePath(root);
|
|
35
|
+
const outAbs = path.join(root, outRel);
|
|
36
|
+
|
|
37
|
+
const used = new Set((truthpack?.env?.vars || []).map(normalizeEnvName).filter(Boolean));
|
|
38
|
+
|
|
39
|
+
let existingText = "";
|
|
40
|
+
if (fs.existsSync(outAbs)) existingText = fs.readFileSync(outAbs, "utf8");
|
|
41
|
+
|
|
42
|
+
const declared = parseDeclaredEnvFromText(existingText);
|
|
43
|
+
const missing = Array.from(used).filter((x) => !declared.has(x)).sort();
|
|
44
|
+
|
|
45
|
+
if (!missing.length && fs.existsSync(outAbs)) {
|
|
46
|
+
return { outRel, wrote: false, added: [] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const header =
|
|
50
|
+
`# vibecheck env template
|
|
51
|
+
# This file is generated/extended from REAL env usage found in your code.
|
|
52
|
+
# Fill values in your real .env (never commit secrets).
|
|
53
|
+
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const additions = missing.map((k) => `${k}=\n`).join("");
|
|
57
|
+
|
|
58
|
+
const nextText = (existingText.trim().length ? existingText.trimEnd() + "\n\n" : header) + additions;
|
|
59
|
+
|
|
60
|
+
ensureDir(path.dirname(outAbs));
|
|
61
|
+
fs.writeFileSync(outAbs, nextText, "utf8");
|
|
62
|
+
|
|
63
|
+
return { outRel, wrote: true, added: missing };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { writeEnvTemplateFromTruthpack };
|
package/bin/runners/lib/env.js
CHANGED
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
// bin/runners/lib/env.js
|
|
2
|
-
const fg = require("fast-glob");
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const parser = require("@babel/parser");
|
|
6
|
-
const traverse = require("@babel/traverse").default;
|
|
7
|
-
const t = require("@babel/types");
|
|
8
|
-
const crypto = require("crypto");
|
|
9
|
-
|
|
10
|
-
function sha256(text) {
|
|
11
|
-
return "sha256:" + crypto.createHash("sha256").update(text).digest("hex");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function parseFile(code) {
|
|
15
|
-
return parser.parse(code, { sourceType: "unambiguous", plugins: ["typescript", "jsx"] });
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function safeRead(fileAbs) {
|
|
19
|
-
return fs.readFileSync(fileAbs, "utf8");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function evidenceFromLoc({ fileAbs, fileRel, loc, reason }) {
|
|
23
|
-
if (!loc) return null;
|
|
24
|
-
const lines = safeRead(fileAbs).split(/\r?\n/);
|
|
25
|
-
const start = Math.max(1, loc.start?.line || 1);
|
|
26
|
-
const end = Math.max(start, loc.end?.line || start);
|
|
27
|
-
const snippet = lines.slice(start - 1, end).join("\n");
|
|
28
|
-
return {
|
|
29
|
-
id: `ev_${crypto.randomBytes(4).toString("hex")}`,
|
|
30
|
-
file: fileRel,
|
|
31
|
-
lines: `${start}-${end}`,
|
|
32
|
-
snippetHash: sha256(snippet),
|
|
33
|
-
reason
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function mergeEvidenceMap(map, name, ev) {
|
|
38
|
-
map[name] = map[name] || { name, references: [], signals: { hasDefault: false } };
|
|
39
|
-
if (ev) map[name].references.push(ev);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isEnvName(s) {
|
|
43
|
-
return typeof s === "string" && /^[A-Z0-9_]+$/.test(s);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function extractEnvFromMemberExpr(node) {
|
|
47
|
-
if (!t.isMemberExpression(node)) return null;
|
|
48
|
-
|
|
49
|
-
// process.env.NAME
|
|
50
|
-
if (t.isMemberExpression(node.object) &&
|
|
51
|
-
t.isIdentifier(node.object.object, { name: "process" }) &&
|
|
52
|
-
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
53
|
-
t.isIdentifier(node.property)) {
|
|
54
|
-
return node.property.name;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// process.env["NAME"]
|
|
58
|
-
if (t.isMemberExpression(node.object) &&
|
|
59
|
-
t.isIdentifier(node.object.object, { name: "process" }) &&
|
|
60
|
-
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
61
|
-
t.isStringLiteral(node.property)) {
|
|
62
|
-
return node.property.value;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// import.meta.env.NAME
|
|
66
|
-
if (t.isMemberExpression(node.object) &&
|
|
67
|
-
t.isMemberExpression(node.object.object) &&
|
|
68
|
-
t.isIdentifier(node.object.object.object, { name: "import" }) &&
|
|
69
|
-
t.isIdentifier(node.object.object.property, { name: "meta" }) &&
|
|
70
|
-
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
71
|
-
t.isIdentifier(node.property)) {
|
|
72
|
-
return node.property.name;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function detectDefaultSignals(parentPath) {
|
|
79
|
-
const p = parentPath?.parentPath;
|
|
80
|
-
if (!p) return { hasDefault: false };
|
|
81
|
-
|
|
82
|
-
if (p.isLogicalExpression() && (p.node.operator === "||" || p.node.operator === "??")) {
|
|
83
|
-
return { hasDefault: true };
|
|
84
|
-
}
|
|
85
|
-
if (p.isConditionalExpression()) return { hasDefault: true };
|
|
86
|
-
if (p.isAssignmentExpression() && p.node.operator === "||=") return { hasDefault: true };
|
|
87
|
-
return { hasDefault: false };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function resolveEnvUsage(repoRoot) {
|
|
91
|
-
const files = await fg(["**/*.{ts,tsx,js,jsx}"], {
|
|
92
|
-
cwd: repoRoot,
|
|
93
|
-
absolute: true,
|
|
94
|
-
ignore: ["**/node_modules/**","**/.next/**","**/dist/**","**/build/**"]
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const usageMap = {};
|
|
98
|
-
|
|
99
|
-
for (const fileAbs of files) {
|
|
100
|
-
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
101
|
-
const code = safeRead(fileAbs);
|
|
102
|
-
|
|
103
|
-
let ast;
|
|
104
|
-
try { ast = parseFile(code); } catch { continue; }
|
|
105
|
-
|
|
106
|
-
traverse(ast, {
|
|
107
|
-
MemberExpression(p) {
|
|
108
|
-
const name = extractEnvFromMemberExpr(p.node);
|
|
109
|
-
if (!name || !isEnvName(name)) return;
|
|
110
|
-
|
|
111
|
-
const ev = evidenceFromLoc({
|
|
112
|
-
fileAbs,
|
|
113
|
-
fileRel,
|
|
114
|
-
loc: p.node.loc,
|
|
115
|
-
reason: `Env usage: ${name}`
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
mergeEvidenceMap(usageMap, name, ev);
|
|
119
|
-
|
|
120
|
-
const sig = detectDefaultSignals(p);
|
|
121
|
-
if (sig.hasDefault) usageMap[name].signals.hasDefault = true;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return usageMap;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function parseDotEnvLike(content) {
|
|
130
|
-
const out = new Set();
|
|
131
|
-
const lines = content.split(/\r?\n/);
|
|
132
|
-
for (const raw of lines) {
|
|
133
|
-
const line = raw.trim();
|
|
134
|
-
if (!line || line.startsWith("#")) continue;
|
|
135
|
-
const l = line.startsWith("export ") ? line.slice(7).trim() : line;
|
|
136
|
-
const eq = l.indexOf("=");
|
|
137
|
-
if (eq <= 0) continue;
|
|
138
|
-
const key = l.slice(0, eq).trim();
|
|
139
|
-
if (isEnvName(key)) out.add(key);
|
|
140
|
-
}
|
|
141
|
-
return out;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function resolveEnvDeclared(repoRoot) {
|
|
145
|
-
const candidates = [
|
|
146
|
-
".env.example", ".env.template", ".env.sample",
|
|
147
|
-
".env.local.example", ".env.development.example",
|
|
148
|
-
".env"
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
const declared = new Set();
|
|
152
|
-
const sources = [];
|
|
153
|
-
|
|
154
|
-
for (const rel of candidates) {
|
|
155
|
-
const abs = path.join(repoRoot, rel);
|
|
156
|
-
if (!fs.existsSync(abs)) continue;
|
|
157
|
-
|
|
158
|
-
const content = safeRead(abs);
|
|
159
|
-
const keys = parseDotEnvLike(content);
|
|
160
|
-
if (keys.size) {
|
|
161
|
-
for (const k of keys) declared.add(k);
|
|
162
|
-
sources.push(rel);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return { declared: Array.from(declared).sort(), sources };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function buildEnvTruth(repoRoot) {
|
|
170
|
-
const usageMap = await resolveEnvUsage(repoRoot);
|
|
171
|
-
const declared = await resolveEnvDeclared(repoRoot);
|
|
172
|
-
|
|
173
|
-
const used = Object.values(usageMap).sort((a, b) => a.name.localeCompare(b.name));
|
|
174
|
-
|
|
175
|
-
const vars = used.map(u => ({
|
|
176
|
-
name: u.name,
|
|
177
|
-
required: !u.signals?.hasDefault,
|
|
178
|
-
references: u.references || [],
|
|
179
|
-
notes: u.signals?.hasDefault ? "Has default/fallback usage signal" : ""
|
|
180
|
-
}));
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
vars,
|
|
184
|
-
declared: declared.declared,
|
|
185
|
-
declaredSources: declared.sources
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
module.exports = { buildEnvTruth };
|
|
1
|
+
// bin/runners/lib/env.js
|
|
2
|
+
const fg = require("fast-glob");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const parser = require("@babel/parser");
|
|
6
|
+
const traverse = require("@babel/traverse").default;
|
|
7
|
+
const t = require("@babel/types");
|
|
8
|
+
const crypto = require("crypto");
|
|
9
|
+
|
|
10
|
+
function sha256(text) {
|
|
11
|
+
return "sha256:" + crypto.createHash("sha256").update(text).digest("hex");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseFile(code) {
|
|
15
|
+
return parser.parse(code, { sourceType: "unambiguous", plugins: ["typescript", "jsx"] });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function safeRead(fileAbs) {
|
|
19
|
+
return fs.readFileSync(fileAbs, "utf8");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function evidenceFromLoc({ fileAbs, fileRel, loc, reason }) {
|
|
23
|
+
if (!loc) return null;
|
|
24
|
+
const lines = safeRead(fileAbs).split(/\r?\n/);
|
|
25
|
+
const start = Math.max(1, loc.start?.line || 1);
|
|
26
|
+
const end = Math.max(start, loc.end?.line || start);
|
|
27
|
+
const snippet = lines.slice(start - 1, end).join("\n");
|
|
28
|
+
return {
|
|
29
|
+
id: `ev_${crypto.randomBytes(4).toString("hex")}`,
|
|
30
|
+
file: fileRel,
|
|
31
|
+
lines: `${start}-${end}`,
|
|
32
|
+
snippetHash: sha256(snippet),
|
|
33
|
+
reason
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function mergeEvidenceMap(map, name, ev) {
|
|
38
|
+
map[name] = map[name] || { name, references: [], signals: { hasDefault: false } };
|
|
39
|
+
if (ev) map[name].references.push(ev);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isEnvName(s) {
|
|
43
|
+
return typeof s === "string" && /^[A-Z0-9_]+$/.test(s);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function extractEnvFromMemberExpr(node) {
|
|
47
|
+
if (!t.isMemberExpression(node)) return null;
|
|
48
|
+
|
|
49
|
+
// process.env.NAME
|
|
50
|
+
if (t.isMemberExpression(node.object) &&
|
|
51
|
+
t.isIdentifier(node.object.object, { name: "process" }) &&
|
|
52
|
+
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
53
|
+
t.isIdentifier(node.property)) {
|
|
54
|
+
return node.property.name;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// process.env["NAME"]
|
|
58
|
+
if (t.isMemberExpression(node.object) &&
|
|
59
|
+
t.isIdentifier(node.object.object, { name: "process" }) &&
|
|
60
|
+
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
61
|
+
t.isStringLiteral(node.property)) {
|
|
62
|
+
return node.property.value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// import.meta.env.NAME
|
|
66
|
+
if (t.isMemberExpression(node.object) &&
|
|
67
|
+
t.isMemberExpression(node.object.object) &&
|
|
68
|
+
t.isIdentifier(node.object.object.object, { name: "import" }) &&
|
|
69
|
+
t.isIdentifier(node.object.object.property, { name: "meta" }) &&
|
|
70
|
+
t.isIdentifier(node.object.property, { name: "env" }) &&
|
|
71
|
+
t.isIdentifier(node.property)) {
|
|
72
|
+
return node.property.name;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function detectDefaultSignals(parentPath) {
|
|
79
|
+
const p = parentPath?.parentPath;
|
|
80
|
+
if (!p) return { hasDefault: false };
|
|
81
|
+
|
|
82
|
+
if (p.isLogicalExpression() && (p.node.operator === "||" || p.node.operator === "??")) {
|
|
83
|
+
return { hasDefault: true };
|
|
84
|
+
}
|
|
85
|
+
if (p.isConditionalExpression()) return { hasDefault: true };
|
|
86
|
+
if (p.isAssignmentExpression() && p.node.operator === "||=") return { hasDefault: true };
|
|
87
|
+
return { hasDefault: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function resolveEnvUsage(repoRoot) {
|
|
91
|
+
const files = await fg(["**/*.{ts,tsx,js,jsx}"], {
|
|
92
|
+
cwd: repoRoot,
|
|
93
|
+
absolute: true,
|
|
94
|
+
ignore: ["**/node_modules/**","**/.next/**","**/dist/**","**/build/**"]
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const usageMap = {};
|
|
98
|
+
|
|
99
|
+
for (const fileAbs of files) {
|
|
100
|
+
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
101
|
+
const code = safeRead(fileAbs);
|
|
102
|
+
|
|
103
|
+
let ast;
|
|
104
|
+
try { ast = parseFile(code); } catch { continue; }
|
|
105
|
+
|
|
106
|
+
traverse(ast, {
|
|
107
|
+
MemberExpression(p) {
|
|
108
|
+
const name = extractEnvFromMemberExpr(p.node);
|
|
109
|
+
if (!name || !isEnvName(name)) return;
|
|
110
|
+
|
|
111
|
+
const ev = evidenceFromLoc({
|
|
112
|
+
fileAbs,
|
|
113
|
+
fileRel,
|
|
114
|
+
loc: p.node.loc,
|
|
115
|
+
reason: `Env usage: ${name}`
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
mergeEvidenceMap(usageMap, name, ev);
|
|
119
|
+
|
|
120
|
+
const sig = detectDefaultSignals(p);
|
|
121
|
+
if (sig.hasDefault) usageMap[name].signals.hasDefault = true;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return usageMap;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parseDotEnvLike(content) {
|
|
130
|
+
const out = new Set();
|
|
131
|
+
const lines = content.split(/\r?\n/);
|
|
132
|
+
for (const raw of lines) {
|
|
133
|
+
const line = raw.trim();
|
|
134
|
+
if (!line || line.startsWith("#")) continue;
|
|
135
|
+
const l = line.startsWith("export ") ? line.slice(7).trim() : line;
|
|
136
|
+
const eq = l.indexOf("=");
|
|
137
|
+
if (eq <= 0) continue;
|
|
138
|
+
const key = l.slice(0, eq).trim();
|
|
139
|
+
if (isEnvName(key)) out.add(key);
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function resolveEnvDeclared(repoRoot) {
|
|
145
|
+
const candidates = [
|
|
146
|
+
".env.example", ".env.template", ".env.sample",
|
|
147
|
+
".env.local.example", ".env.development.example",
|
|
148
|
+
".env"
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
const declared = new Set();
|
|
152
|
+
const sources = [];
|
|
153
|
+
|
|
154
|
+
for (const rel of candidates) {
|
|
155
|
+
const abs = path.join(repoRoot, rel);
|
|
156
|
+
if (!fs.existsSync(abs)) continue;
|
|
157
|
+
|
|
158
|
+
const content = safeRead(abs);
|
|
159
|
+
const keys = parseDotEnvLike(content);
|
|
160
|
+
if (keys.size) {
|
|
161
|
+
for (const k of keys) declared.add(k);
|
|
162
|
+
sources.push(rel);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { declared: Array.from(declared).sort(), sources };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function buildEnvTruth(repoRoot) {
|
|
170
|
+
const usageMap = await resolveEnvUsage(repoRoot);
|
|
171
|
+
const declared = await resolveEnvDeclared(repoRoot);
|
|
172
|
+
|
|
173
|
+
const used = Object.values(usageMap).sort((a, b) => a.name.localeCompare(b.name));
|
|
174
|
+
|
|
175
|
+
const vars = used.map(u => ({
|
|
176
|
+
name: u.name,
|
|
177
|
+
required: !u.signals?.hasDefault,
|
|
178
|
+
references: u.references || [],
|
|
179
|
+
notes: u.signals?.hasDefault ? "Has default/fallback usage signal" : ""
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
vars,
|
|
184
|
+
declared: declared.declared,
|
|
185
|
+
declaredSources: declared.sources
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = { buildEnvTruth };
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Standardized error handling for CLI runners
|
|
3
3
|
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* World-Class Error Handling
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
4
8
|
* Design principles:
|
|
5
9
|
* - Every error has a human-readable message
|
|
6
10
|
* - Every error suggests a next step
|
|
7
11
|
* - Exit codes are consistent and documented
|
|
12
|
+
* - Errors include "receipt" (file:line evidence) where possible
|
|
13
|
+
* - Debug mode shows stack traces
|
|
8
14
|
*/
|
|
9
15
|
|
|
16
|
+
const { EXIT, getExitInfo, getHint } = require("./exit-codes");
|
|
17
|
+
|
|
10
18
|
const colors = {
|
|
11
19
|
reset: "\x1b[0m",
|
|
12
20
|
red: "\x1b[31m",
|
|
@@ -24,16 +32,15 @@ const c = {
|
|
|
24
32
|
dim: (text) => `\x1b[2m${text}${colors.reset}`,
|
|
25
33
|
};
|
|
26
34
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// IMPORTANT: These codes are part of the CLI contract - do not change without migration guide
|
|
35
|
+
// Re-export EXIT for backward compatibility
|
|
36
|
+
// NOTE: Prefer importing from exit-codes.js directly
|
|
30
37
|
const EXIT_CODES = {
|
|
31
|
-
SUCCESS:
|
|
32
|
-
POLICY_FAIL:
|
|
33
|
-
USER_ERROR:
|
|
34
|
-
SYSTEM_ERROR:
|
|
35
|
-
AUTH_FAILURE:
|
|
36
|
-
NETWORK_FAILURE:
|
|
38
|
+
SUCCESS: EXIT.SUCCESS,
|
|
39
|
+
POLICY_FAIL: EXIT.WARNINGS, // Findings above threshold (policy fail)
|
|
40
|
+
USER_ERROR: EXIT.USER_ERROR, // User error: invalid args, bad config
|
|
41
|
+
SYSTEM_ERROR: EXIT.INTERNAL_ERROR, // System error: crash, unexpected exceptions
|
|
42
|
+
AUTH_FAILURE: EXIT.AUTH_REQUIRED, // Auth/entitlement failure
|
|
43
|
+
NETWORK_FAILURE: EXIT.NETWORK_ERROR, // Network/backend failure
|
|
37
44
|
};
|
|
38
45
|
|
|
39
46
|
// Error-specific guidance
|