@codacy/verity-cli 0.34.0-experimental.5602fd5 → 0.34.0-experimental.675f6d2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/verity.js +69 -100
  2. package/package.json +1 -1
package/bin/verity.js CHANGED
@@ -17221,7 +17221,7 @@ function readConfigFile(path) {
17221
17221
  return {
17222
17222
  git_moments: [...new Set(moments)],
17223
17223
  ...source2 ? { git_moments_source: source2 } : {},
17224
- ...raw.formal === "push" || raw.formal === "off" ? { formal: raw.formal } : {}
17224
+ ...raw.formal === "push" ? { formal: "push" } : {}
17225
17225
  };
17226
17226
  } catch {
17227
17227
  return DEFAULTS;
@@ -24152,7 +24152,7 @@ function registerConfigCommands(program2) {
24152
24152
  config.command("formal [state]").description('Get or set formal verification at push: "push" or "off" (needs Lean \u2014 verity lean status)').action((state) => {
24153
24153
  if (state === void 0) {
24154
24154
  const cfg = readProjectConfig();
24155
- process.stdout.write((cfg.formal === "push" ? "push" : "off") + "\n");
24155
+ process.stdout.write((cfg.formal ?? "off") + "\n");
24156
24156
  if (cfg.formal_refused) printWarn(formalRefusalMessage(cfg.formal_refused));
24157
24157
  return;
24158
24158
  }
@@ -24160,7 +24160,7 @@ function registerConfigCommands(program2) {
24160
24160
  printError(`Unrecognised state: ${state}. Use "push" or "off".`);
24161
24161
  process.exit(1);
24162
24162
  }
24163
- writeProjectConfig({ formal: state === "push" ? "push" : "off" });
24163
+ writeProjectConfig({ formal: state === "push" ? "push" : void 0 });
24164
24164
  const refused2 = state === "push" ? readProjectConfig().formal_refused : void 0;
24165
24165
  if (refused2) {
24166
24166
  printWarn(`Formal verification at push: still off \u2014 ${formalRefusalMessage(refused2)}`);
@@ -27073,7 +27073,7 @@ function channelSilence(input) {
27073
27073
  // src/lib/cli-version.ts
27074
27074
  function cliVersion() {
27075
27075
  try {
27076
- return true ? "0.34.0-experimental.5602fd5" : "dev";
27076
+ return true ? "0.34.0-experimental.675f6d2" : "dev";
27077
27077
  } catch {
27078
27078
  return "dev";
27079
27079
  }
@@ -33869,11 +33869,6 @@ async function rankViaServer(candidates2, ctx, auth2) {
33869
33869
  cmd: "prove-rank"
33870
33870
  });
33871
33871
  if (!res.ok) return { ok: false, ranker: "luna", error: res.error, ranked: fallback };
33872
- const off = res.data;
33873
- if (off?.enabled === false) {
33874
- const why = typeof off.reason === "string" ? off.reason.slice(0, 40) : "disabled";
33875
- return { ok: false, ranker: "luna", error: `formal is off for this project (${why})`, ranked: fallback, disabled: why };
33876
- }
33877
33872
  const items = Array.isArray(res.data?.items) ? res.data.items : [];
33878
33873
  return { ok: true, ranker: "luna", ranked: applyAnnotations(candidates2, items), costUsd: typeof res.data?.cost_usd === "number" ? res.data.cost_usd : null };
33879
33874
  }
@@ -34598,8 +34593,6 @@ async function generateModel(target, opts) {
34598
34593
  });
34599
34594
  if (!res.ok) return { ok: false, error: res.error };
34600
34595
  const d = res.data ?? {};
34601
- const off = d;
34602
- if (off.enabled === false) return { ok: false, error: `formal is off for this project (${typeof off.reason === "string" ? off.reason.slice(0, 40) : "disabled"})` };
34603
34596
  if (d.error && typeof d.error === "object") return { ok: false, error: `${d.error.code ?? "SERVICE_ERROR"}: ${d.error.message ?? "the service failed"}` };
34604
34597
  const usage2 = d.usage && typeof d.usage === "object" ? { inputTokens: Number(d.usage.inputTokens ?? 0), outputTokens: Number(d.usage.outputTokens ?? 0) } : null;
34605
34598
  return acceptAnswer(d, usage2, typeof d.cost_usd === "number" ? d.cost_usd : null);
@@ -34779,7 +34772,8 @@ Rules:
34779
34772
  5. Write the harness as ONE file and run it in the SAME shell call that writes it (a heredoc followed by the run). TypeScript: node --experimental-strip-types harness.ts (detect runtimes only if that fails: node --version, python3 --version); if strip-types is unavailable, remove type annotations ONLY on lines that are not part of the function under test's logic, and say so. Python: python3 harness.py.
34780
34773
  6. Nothing you run can reach the network. Do not try.
34781
34774
  7. Keep it under about eight shell calls. Output tokens are time.
34782
- 8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters).
34775
+ 8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters), impact (one word, below).
34776
+ 9. impact \u2014 AFTER the verdict, and never a reason to change it: what the violation you ran costs a user of this code, the most serious one the run showed. money: a balance, charge, refund, price or stock is wrong. access: someone gets access they should not have, or an auth check is bypassed. data: a user's records are lost, corrupted or wrongly duplicated. external: an effect outside the system happens twice or never (a charge, an email, a shipment, a webhook). count: a counter, metric or statistic is off. cache: a cached or derived value is stale until it refreshes. internal: the program's own bookkeeping is off, with no effect a user would see. none: the verdict is not reproduced.
34783
34777
 
34784
34778
  "reproduced" means the real code, driven by the trace, reached the property violation the model describes. "diverged" means it did not \u2014 the model is wrong about the code, which is a useful answer. "unreplayable" means you could not run the real function faithfully; say why.`;
34785
34779
  var REPLAY_SCHEMA = {
@@ -34797,9 +34791,10 @@ var REPLAY_SCHEMA = {
34797
34791
  required: ["path", "lines"]
34798
34792
  }
34799
34793
  },
34800
- executed_output: { type: "string" }
34794
+ executed_output: { type: "string" },
34795
+ impact: { type: "string", enum: ["money", "access", "data", "external", "count", "cache", "internal", "none"] }
34801
34796
  },
34802
- required: ["verdict", "reason", "harness_files", "executed_output"]
34797
+ required: ["verdict", "reason", "harness_files", "executed_output", "impact"]
34803
34798
  };
34804
34799
  function renderBriefInput(brief) {
34805
34800
  const p = brief.property;
@@ -34826,6 +34821,8 @@ function renderBriefInput(brief) {
34826
34821
  }
34827
34822
  return parts2.join("\n");
34828
34823
  }
34824
+ var IMPACTS = ["money", "access", "data", "external", "count", "cache", "internal", "none"];
34825
+ var isImpact = (v) => typeof v === "string" && IMPACTS.includes(v);
34829
34826
  function stringsIn(value, out2 = [], depth = 0) {
34830
34827
  if (depth > 8) return out2;
34831
34828
  if (typeof value === "string") out2.push(value);
@@ -34867,7 +34864,8 @@ function parseReplayOutput(output) {
34867
34864
  verdict,
34868
34865
  reason: typeof o.reason === "string" ? o.reason.slice(0, 2e3) : "",
34869
34866
  harness_files: Array.isArray(o.harness_files) ? o.harness_files.filter((f) => typeof f.path === "string").map((f) => ({ path: String(f.path).slice(0, 200), lines: Number.isInteger(f.lines) ? f.lines : 0 })) : [],
34870
- executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : ""
34867
+ executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : "",
34868
+ impact: isImpact(o.impact) ? o.impact : "none"
34871
34869
  };
34872
34870
  }
34873
34871
  } catch {
@@ -34875,10 +34873,10 @@ function parseReplayOutput(output) {
34875
34873
  }
34876
34874
  return { claim, shellCalls, executedCalls, shellTexts, outputTexts };
34877
34875
  }
34878
- var RESULT_LINE = /REPLAY_RESULT=(reproduced|diverged)\b/g;
34876
+ var RESULT_LINE = /REPLAY_RESULT=(reproduced|diverged)\b/;
34879
34877
  function printedResult(outputTexts) {
34880
34878
  let last = null;
34881
- for (const t of outputTexts) for (const m of t.matchAll(RESULT_LINE)) last = m[1];
34879
+ for (const t of outputTexts) for (const m of t.matchAll(new RegExp(RESULT_LINE.source, "g"))) last = m[1];
34882
34880
  return last;
34883
34881
  }
34884
34882
  var RESULT_MISSING = "the harness never printed REPLAY_RESULT=reproduced on its own stdout \u2014 the verdict was the model's word, not the run's";
@@ -35068,6 +35066,7 @@ async function runReplayInContainer(brief, opts) {
35068
35066
  ...judged,
35069
35067
  executedCalls,
35070
35068
  shellCalls,
35069
+ ...judged.verdict === "reproduced" && parsed.claim ? { impact: parsed.claim.impact } : {},
35071
35070
  executedOutput: parsed.claim?.executed_output ?? "",
35072
35071
  harnessFiles: parsed.claim?.harness_files ?? [],
35073
35072
  usage,
@@ -35114,11 +35113,6 @@ async function runReplayViaServer(brief, auth2, budgetMs = 24e4) {
35114
35113
  return { claimed: null, verdict: "unverified", reason: "", downgrade: res.error, executedCalls: 0, shellCalls: 0, verbatim: none, executedOutput: "", harnessFiles: [], usage: null, durationMs: Date.now() - started, containerId: null, error: res.error };
35115
35114
  }
35116
35115
  const d = res.data ?? {};
35117
- if (d.enabled === false) {
35118
- const why = d.reason;
35119
- const msg = `formal is off for this project (${typeof why === "string" ? why.slice(0, 40) : "disabled"})`;
35120
- return { claimed: null, verdict: "unverified", reason: "", downgrade: msg, executedCalls: 0, shellCalls: 0, verbatim: none, executedOutput: "", harnessFiles: [], usage: null, durationMs: Date.now() - started, containerId: null, error: msg };
35121
- }
35122
35116
  if (d.error && typeof d.error === "object") {
35123
35117
  const e = d.error;
35124
35118
  const msg = `${e.code ?? "SERVICE_ERROR"}: ${e.message ?? "the service failed"}`;
@@ -35131,6 +35125,7 @@ async function runReplayViaServer(brief, auth2, budgetMs = 24e4) {
35131
35125
  verdict: verdict === "reproduced" || verdict === "diverged" || verdict === "unreplayable" ? verdict : "unverified",
35132
35126
  reason: typeof d.reason === "string" ? d.reason : "",
35133
35127
  downgrade: typeof d.downgrade === "string" ? d.downgrade : null,
35128
+ ...isImpact(d.impact) ? { impact: d.impact } : {},
35134
35129
  executedCalls: Number(d.executed_calls ?? 0),
35135
35130
  shellCalls: Number(d.shell_calls ?? 0),
35136
35131
  verbatim: { covered: Number(v.covered ?? 0), total: Number(v.total ?? 0), ratio: Number(v.ratio ?? 0), basis: v.basis === "symbol" || v.basis === "file" ? v.basis : "none" },
@@ -35915,10 +35910,9 @@ function verityLeanHome(env = process.env) {
35915
35910
  function findToolchain(env = process.env) {
35916
35911
  const managedBin = (0, import_node_path43.join)(verityLeanHome(env), LEAN_TOOLCHAIN.replace(/^leanprover\/lean4:/, ""), "bin");
35917
35912
  const elanBin = (0, import_node_path43.join)(env.HOME ?? (0, import_node_os9.homedir)(), ".elan", "bin");
35918
- const elanHas = (0, import_node_fs58.existsSync)((0, import_node_path43.join)(env.HOME ?? (0, import_node_os9.homedir)(), ".elan", "toolchains", LEAN_TOOLCHAIN.replace("/", "--").replace(":", "---")));
35919
- const userPath = (env.PATH ?? "").split(import_node_path43.delimiter).filter((d) => d && (elanHas || (0, import_node_path43.resolve)(d) !== (0, import_node_path43.resolve)(elanBin)));
35920
- const path = [managedBin, ...elanHas ? [elanBin] : [], ...userPath].join(import_node_path43.delimiter);
35913
+ const path = [managedBin, elanBin, env.PATH ?? ""].filter(Boolean).join(import_node_path43.delimiter);
35921
35914
  const merged = { ...env, PATH: path, ELAN_TOOLCHAIN: env.ELAN_TOOLCHAIN ?? LEAN_TOOLCHAIN };
35915
+ const elanHas = (0, import_node_fs58.existsSync)((0, import_node_path43.join)(env.HOME ?? (0, import_node_os9.homedir)(), ".elan", "toolchains", LEAN_TOOLCHAIN.replace("/", "--").replace(":", "---")));
35922
35916
  const exe = process.platform === "win32" ? "lake.exe" : "lake";
35923
35917
  const candidates2 = [(0, import_node_path43.join)(managedBin, exe), ...elanHas ? [(0, import_node_path43.join)(elanBin, exe)] : [], exe];
35924
35918
  for (const candidate of candidates2) {
@@ -36113,18 +36107,33 @@ function writeResult(root, report) {
36113
36107
  function isCrashTrace(trace) {
36114
36108
  return trace.some((s) => /\b(crash\w*|kill\w*|restart\w*|interrupt\w*|power|abort\w*|process (dies|exit\w*)|lost (reply|response))\b/i.test(s));
36115
36109
  }
36110
+ var SEVERITY_BY_IMPACT = {
36111
+ money: "high",
36112
+ access: "high",
36113
+ data: "high",
36114
+ external: "high",
36115
+ count: "medium",
36116
+ cache: "medium",
36117
+ internal: "medium",
36118
+ // A reproduced finding always has a real code; 'none' here is a malformed answer, so it blocks.
36119
+ none: "high"
36120
+ };
36121
+ function formalSeverity(f) {
36122
+ return f.impact && SEVERITY_BY_IMPACT[f.impact] || "high";
36123
+ }
36116
36124
  function mergeFormalFindings(response, outcome, pushedFiles) {
36117
- if (!outcome) return { blocking: 0 };
36125
+ if (!outcome) return { blocking: 0, warning: 0 };
36118
36126
  const norm2 = (p) => p.replace(/\\/g, "/");
36119
36127
  const pushed = new Set(pushedFiles.map(norm2));
36120
- const blocking = outcome.findings.filter((f) => f.verdict === "reproduced" && !f.waived && pushed.has(norm2(f.file)));
36121
- if (blocking.length === 0) return { blocking: 0 };
36128
+ const reproduced = outcome.findings.filter((f) => f.verdict === "reproduced" && !f.waived && pushed.has(norm2(f.file)));
36129
+ if (reproduced.length === 0) return { blocking: 0, warning: 0 };
36130
+ const blocking = reproduced.filter((f) => formalSeverity(f) === "high");
36122
36131
  const existing = Array.isArray(response.findings) ? response.findings : [];
36123
36132
  response.findings = [
36124
36133
  ...existing,
36125
- ...blocking.map((f) => ({
36134
+ ...reproduced.map((f) => ({
36126
36135
  pattern_id: "formal-counterexample",
36127
- severity: "high",
36136
+ severity: formalSeverity(f),
36128
36137
  scope: "change",
36129
36138
  file: f.file,
36130
36139
  line: outcome.target?.lines?.from ?? null,
@@ -36132,14 +36141,15 @@ function mergeFormalFindings(response, outcome, pushedFiles) {
36132
36141
  // as a title it read backwards (e2e, 2026-09-26). A trace that needs a
36133
36142
  // crash between two writes is labelled: reproducing it means injecting
36134
36143
  // the crash, so it is a crash-consistency finding, not a race.
36135
- title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code`,
36144
+ title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code${f.impact && f.impact !== "none" ? ` (impact: ${f.impact})` : ""}`,
36136
36145
  description: terminalText(f.reason ?? "", 2e3),
36137
36146
  fix: { description: `Trace: ${terminalText(f.trace.join(" \u2192 ") || "(initial state)", 2e3)}. Replay: ${terminalText(f.reason ?? "reproduced in the container", 2e3)}` },
36138
36147
  source: "formal"
36139
36148
  }))
36140
36149
  ];
36141
- if (response.gate_decision !== "FAIL") response.gate_decision = "FAIL";
36142
- return { blocking: blocking.length };
36150
+ if (blocking.length > 0) response.gate_decision = "FAIL";
36151
+ else if (response.gate_decision === "PASS" || response.gate_decision == null) response.gate_decision = "WARN";
36152
+ return { blocking: blocking.length, warning: reproduced.length - blocking.length };
36143
36153
  }
36144
36154
 
36145
36155
  // src/lib/prove/scalpel.ts
@@ -36458,7 +36468,9 @@ async function runFormalPhase(input) {
36458
36468
  symbol: f.symbol,
36459
36469
  verdict: "reproduced",
36460
36470
  reason: f.waived ? `waived ${f.waived.at.slice(0, 10)}: ${f.waived.reason} (standing since ${f.at.slice(0, 10)}; the file has not changed)` : `${f.reason ?? "reproduced on the real code"} (standing since ${f.at.slice(0, 10)}; the file has not changed)`,
36461
- ...f.waived ? { waived: true } : {}
36471
+ ...f.waived ? { waived: true } : {},
36472
+ // A hand-edited file cannot invent a code: unknown values read as absent (HIGH).
36473
+ ...isImpact(f.impact) ? { impact: f.impact } : {}
36462
36474
  });
36463
36475
  }
36464
36476
  }
@@ -36497,7 +36509,6 @@ async function runFormalPhase(input) {
36497
36509
  return done({ status: "skipped", skip: "not-eligible", detail: `${best.file}: ${isEligible(best, largePush).why}` });
36498
36510
  }
36499
36511
  const ranked = await deps.rank(eligible, root, input.changedLines);
36500
- if (!ranked.ok && ranked.disabled) return done({ status: "skipped", skip: "disabled-by-server", detail: ranked.error });
36501
36512
  if (ranked.ok) spend(ranked);
36502
36513
  candidates2 = ranked.ranked.slice(0, 5).map((c) => ({ file: c.file, symbol: c.ai?.symbol ?? null, score: c.ai?.score ?? null, criteria: c.ai?.criteria ?? null, reasked: !!c.ai?.reasked, reasons: (c.ai?.reasons ?? []).map((r) => r.slice(0, 200)) }));
36503
36514
  if (cancelled()) return done({ status: "skipped", skip: "deadline", detail: "cut off after ranking" });
@@ -36732,7 +36743,7 @@ async function runFormalPhase(input) {
36732
36743
  continue;
36733
36744
  }
36734
36745
  const verdict = r.verdict === "reproduced" ? "reproduced" : r.verdict === "diverged" ? "diverged" : r.verdict === "unreplayable" ? "model-only" : "unverified";
36735
- findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0 });
36746
+ findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0, ...verdict === "reproduced" && r.impact ? { impact: r.impact } : {} });
36736
36747
  }
36737
36748
  const reproduced = findings.filter((f) => f.verdict === "reproduced");
36738
36749
  if (reproduced.length > 0 && !cancelled()) {
@@ -36740,7 +36751,7 @@ async function runFormalPhase(input) {
36740
36751
  const others = readPersistedFindings(root).filter((f) => f.file !== top.file);
36741
36752
  writePersistedFindings(root, [
36742
36753
  ...others,
36743
- ...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...lines ? { lines } : {}, at: now }))
36754
+ ...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...f.impact ? { impact: f.impact } : {}, ...lines ? { lines } : {}, at: now }))
36744
36755
  ]);
36745
36756
  }
36746
36757
  const clean2 = findings.every((f) => f.verdict === "diverged");
@@ -37480,6 +37491,8 @@ async function runGuard(opts, globals) {
37480
37491
  rounds: formalOutcome.model?.rounds ?? null,
37481
37492
  properties: formalOutcome.properties?.map((p) => `${p.id}:${p.result}`) ?? [],
37482
37493
  findings: formalOutcome.findings.map((f) => `${f.property}:${f.verdict}`),
37494
+ // Decision 82: the replay's impact code per reproduced finding; count/cache/internal warn, the rest block.
37495
+ impacts: formalOutcome.findings.filter((f) => f.impact).map((f) => `${f.property}:${f.impact}`),
37483
37496
  // Decision 81: what each property rests on (a rule, or "line"), what was
37484
37497
  // sent back or dropped as ungrounded, and which counterexamples lack their
37485
37498
  // property's trigger (shadow). Ids and rule names only, never code.
@@ -38789,22 +38802,6 @@ var import_promises18 = require("node:stream/promises");
38789
38802
  var import_node_stream = require("node:stream");
38790
38803
  var RELEASES = "https://releases.lean-lang.org/lean4";
38791
38804
  var LEAN_DOWNLOAD_SIZE = "\u2248800 MB download, 2.7 GB on disk";
38792
- var DOWNLOAD_STALL_MS = 6e4;
38793
- var LEAN_DISK_NEEDED_BYTES = 3.75 * 1024 ** 3;
38794
- function freeBytes(dir) {
38795
- let d = dir;
38796
- for (let i2 = 0; i2 < 64; i2++) {
38797
- try {
38798
- const st = (0, import_node_fs63.statfsSync)(d);
38799
- return Number(st.bavail) * Number(st.bsize);
38800
- } catch {
38801
- const up = (0, import_node_path47.join)(d, "..");
38802
- if (up === d) return null;
38803
- d = up;
38804
- }
38805
- }
38806
- return null;
38807
- }
38808
38805
  var LEAN_ASSET_SHA256 = {
38809
38806
  "v4.34.0": {
38810
38807
  "lean-4.34.0-darwin.zip": "75a2a4b8e3d35680e8f51fae5a42c79702f463dd73ee1520fb61e5a9143e336c",
@@ -38863,32 +38860,17 @@ function unzip(zip, dest) {
38863
38860
  }
38864
38861
  return { ok: false, error: errors.join("; ") };
38865
38862
  }
38866
- async function download(url, dest, onProgress, stallMs = DOWNLOAD_STALL_MS) {
38867
- const ctrl = new AbortController();
38868
- let timer;
38869
- const arm = () => {
38870
- clearTimeout(timer);
38871
- timer = setTimeout(() => ctrl.abort(), stallMs);
38872
- };
38873
- arm();
38874
- try {
38875
- const res = await fetch(url, { redirect: "follow", signal: ctrl.signal });
38876
- if (!res.ok || !res.body) throw new Error(`download failed: HTTP ${res.status}`);
38877
- const total = Number(res.headers.get("content-length")) || null;
38878
- let seen = 0;
38879
- const stream = import_node_stream.Readable.fromWeb(res.body);
38880
- stream.on("data", (chunk) => {
38881
- arm();
38882
- seen += chunk.length;
38883
- onProgress(seen, total);
38884
- });
38885
- await (0, import_promises18.pipeline)(stream, (0, import_node_fs63.createWriteStream)(dest), { signal: ctrl.signal });
38886
- } catch (err2) {
38887
- if (ctrl.signal.aborted) throw new Error(`download stalled: nothing received for ${Math.round(stallMs / 1e3)} s`);
38888
- throw err2;
38889
- } finally {
38890
- clearTimeout(timer);
38891
- }
38863
+ async function download(url, dest, onProgress) {
38864
+ const res = await fetch(url, { redirect: "follow" });
38865
+ if (!res.ok || !res.body) throw new Error(`download failed: HTTP ${res.status}`);
38866
+ const total = Number(res.headers.get("content-length")) || null;
38867
+ let seen = 0;
38868
+ const stream = import_node_stream.Readable.fromWeb(res.body);
38869
+ stream.on("data", (chunk) => {
38870
+ seen += chunk.length;
38871
+ onProgress(seen, total);
38872
+ });
38873
+ await (0, import_promises18.pipeline)(stream, (0, import_node_fs63.createWriteStream)(dest));
38892
38874
  }
38893
38875
  function inPlaceProgress(label2, write = (s) => process.stderr.write(s)) {
38894
38876
  let active = false;
@@ -38926,12 +38908,6 @@ async function installLean(opts = {}) {
38926
38908
  const dir = pinnedVersionDir(env);
38927
38909
  const url = `${RELEASES}/${pinnedVersion()}/${asset}`;
38928
38910
  const home = verityLeanHome(env);
38929
- const free = freeBytes(home);
38930
- const needed = opts.minFreeBytes ?? LEAN_DISK_NEEDED_BYTES;
38931
- if (free !== null && free < needed) {
38932
- const gb = (n) => (n / 1024 ** 3).toFixed(1);
38933
- return { ok: false, error: `not enough disk space for Lean: ${gb(free)} GB free where ${home} lives, needs about ${gb(needed)} GB` };
38934
- }
38935
38911
  const tmp = (0, import_node_path47.join)(home, ".tmp");
38936
38912
  (0, import_node_fs63.rmSync)(tmp, { recursive: true, force: true });
38937
38913
  (0, import_node_fs63.mkdirSync)(tmp, { recursive: true });
@@ -39240,10 +39216,8 @@ var INTENSITY_CHOICES = [
39240
39216
  { id: "thorough", label: "thorough", hint: "all tools, all rules (~15s)" }
39241
39217
  ];
39242
39218
  var FORMAL_CHOICES = [
39243
- { id: "yes", label: "yes", hint: "a Lean model of the riskiest changed function; blocks only on a REPRODUCED counterexample" },
39244
- // No is the default (review of #255): "yes" downloads ≈800 MB and unpacks
39245
- // 2.7 GB, and a bare Enter must never do that.
39246
- { id: "no", label: "no", hint: "enable later with: verity config formal push", recommended: true }
39219
+ { id: "yes", label: "yes", hint: "a Lean model of the riskiest changed function; blocks only on a REPRODUCED counterexample", recommended: true },
39220
+ { id: "no", label: "no", hint: "enable later with: verity config formal push" }
39247
39221
  ];
39248
39222
  var MOMENT_CHOICES = [
39249
39223
  { id: "stop", label: "On stop", hint: "after every agent turn \u2014 fast feedback while you work", recommended: true },
@@ -39649,20 +39623,16 @@ async function offerFormalVerification(defaultsOnly, pluginMode) {
39649
39623
  printWarn(` Formal verification: off \u2014 ${formalRefusalMessage(cfg.formal_refused)}`);
39650
39624
  return;
39651
39625
  }
39626
+ const toolchain = findToolchain();
39652
39627
  if (cfg.formal === "push") {
39653
- if (findToolchain()) printInfo(" Formal verification at push \u2713");
39628
+ if (toolchain) printInfo(" Formal verification at push \u2713");
39654
39629
  else printWarn(" Formal verification is on but Lean is missing \u2014 run: verity lean install");
39655
39630
  return;
39656
39631
  }
39657
- if (cfg.formal === "off") {
39658
- printInfo(" Formal verification: off (enable with: verity config formal push)");
39659
- return;
39660
- }
39661
39632
  if (defaultsOnly || !interactive()) {
39662
39633
  printInfo(" Formal verification (a model-based bug finder at push): off \u2014 enable with: verity config formal push");
39663
39634
  return;
39664
39635
  }
39665
- const toolchain = findToolchain();
39666
39636
  console.log("");
39667
39637
  console.log(" Formal verification review \u2014 a model-based bug finder for your riskiest code.");
39668
39638
  console.log(" At push, Verity picks the riskiest changed function, checks a small model of");
@@ -39670,10 +39640,9 @@ async function offerFormalVerification(defaultsOnly, pluginMode) {
39670
39640
  console.log(" Verity for that). Only a reproduced bug can block; most pushes get no model.");
39671
39641
  console.log(` Adds /verity-prove <worry> to check any code on demand.${toolchain ? "" : ` Needs to install Lean 4 (${LEAN_DOWNLOAD_SIZE}) on "yes".`}`);
39672
39642
  console.log("");
39673
- const wants = await promptChoice("Formal verification at push?", FORMAL_CHOICES, "no");
39643
+ const wants = await promptChoice("Formal verification at push?", FORMAL_CHOICES, "yes");
39674
39644
  if (wants === "yes" && pluginMode) printInfo(" Plugin: the push check works now; /verity:verity-prove arrives with the plugin's next release.");
39675
39645
  if (wants !== "yes") {
39676
- writeProjectConfig({ formal: "off" });
39677
39646
  printInfo(" Formal verification: off (enable later with: verity config formal push)");
39678
39647
  return;
39679
39648
  }
@@ -39932,7 +39901,7 @@ function registerInitCommand(program2) {
39932
39901
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
39933
39902
  init: {
39934
39903
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
39935
- cli_version: true ? "0.34.0-experimental.5602fd5" : "dev"
39904
+ cli_version: true ? "0.34.0-experimental.675f6d2" : "dev"
39936
39905
  }
39937
39906
  });
39938
39907
  } catch (err2) {
@@ -41405,8 +41374,8 @@ function registerLeanCommand(program2) {
41405
41374
  }
41406
41375
 
41407
41376
  // src/cli.ts
41408
- program.name("verity").description("CLI for Verity quality gate service").version("0.34.0-experimental.5602fd5").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
41409
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.34.0-experimental.5602fd5");
41377
+ program.name("verity").description("CLI for Verity quality gate service").version("0.34.0-experimental.675f6d2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
41378
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.34.0-experimental.675f6d2");
41410
41379
  setUserNamedServiceUrl(program.opts().serviceUrl);
41411
41380
  try {
41412
41381
  await foldLegacyLocalCredential();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.34.0-experimental.5602fd5",
3
+ "version": "0.34.0-experimental.675f6d2",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",