@westbayberry/dg 1.2.1 → 1.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.
Files changed (2) hide show
  1. package/dist/index.mjs +225 -278
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -2568,9 +2568,9 @@ function loadDgrc() {
2568
2568
  `);
2569
2569
  }
2570
2570
  }
2571
- if (cwdPath !== homePath && existsSync2(cwdPath) && process.env.DG_QUIET !== "1") {
2571
+ if (cwdPath !== homePath && existsSync2(cwdPath)) {
2572
2572
  process.stderr.write(
2573
- `dg: note: ./.dgrc.json is ignored (repo-controlled config is no longer honored). Use ~/.dg/.dgrc.json, env vars (DG_MODE, DG_API_URL), or CLI flags.
2573
+ `dg: note: ./.dgrc.json is ignored (repo-controlled config is no longer honored). Use ~/.dg/.dgrc.json or CLI flags.
2574
2574
  `
2575
2575
  );
2576
2576
  }
@@ -2668,7 +2668,7 @@ function parseConfig(argv, strictFlags = true) {
2668
2668
  const apiKey = dgrc.apiKey && typeof dgrc.apiKey === "string" && (dgrc.apiKey.startsWith("dg_live_") || dgrc.apiKey.startsWith("dg_test_")) ? dgrc.apiKey : null;
2669
2669
  const isProtectiveWrapper = command === "npm" || command === "pip";
2670
2670
  const defaultMode = isProtectiveWrapper ? "block" : "warn";
2671
- const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? defaultMode;
2671
+ const modeRaw = values.mode ?? dgrc.mode ?? defaultMode;
2672
2672
  if (!["block", "warn", "off"].includes(modeRaw)) {
2673
2673
  process.stderr.write(
2674
2674
  `Error: Invalid mode "${modeRaw}". Must be block, warn, or off.
@@ -2696,7 +2696,7 @@ function parseConfig(argv, strictFlags = true) {
2696
2696
  sarif: values.sarif || false,
2697
2697
  scanAll: !values["changed-only"],
2698
2698
  baseLockfile: values["base-lockfile"] ?? null,
2699
- workspace: values.workspace ?? process.env.DG_WORKSPACE ?? null,
2699
+ workspace: values.workspace ?? null,
2700
2700
  outputFile: values.output ?? null,
2701
2701
  command,
2702
2702
  debug,
@@ -2705,7 +2705,6 @@ function parseConfig(argv, strictFlags = true) {
2705
2705
  quiet: (() => {
2706
2706
  if (values["no-quiet"]) return false;
2707
2707
  if (values.quiet) return true;
2708
- if (process.env.DG_QUIET === "1") return true;
2709
2708
  if (process.env.CI === "1" || process.env.CI === "true") return true;
2710
2709
  return false;
2711
2710
  })(),
@@ -2814,8 +2813,7 @@ var init_config = __esm({
2814
2813
  --api-url <url> API base URL (default: https://api.westbayberry.com)
2815
2814
 
2816
2815
  Install-wrapper flags (work on \`npm install\`, \`pip install\`, etc.):
2817
- --dg-force Bypass a block (audit-visible \u2014 record it)
2818
- --dg-force-reason "..." Reason recorded with the override
2816
+ --dg-force Bypass a block (audit-visible)
2819
2817
  --dg-no-scripts Add \`--ignore-scripts\` to the real install
2820
2818
  --strict Strongest protection: implies --dg-no-scripts and
2821
2819
  refuses any partial-coverage scan
@@ -2824,16 +2822,11 @@ var init_config = __esm({
2824
2822
  DG_API_KEY CI auth token (dg_live_* or dg_test_*).
2825
2823
  Used when ~/.dg/config.json is absent.
2826
2824
  File beats env if both are present.
2827
- DG_API_URL API base URL
2828
- DG_MODE Mode (block | warn | off)
2829
- DG_DEBUG=1 Enable debug output
2830
- DG_QUIET=1 Same as passing --quiet
2831
- DG_WORKSPACE Workspace subdirectory to scan
2832
- DG_TELEMETRY=0 Opt out of anonymous crash reports (on by default; DO_NOT_TRACK=1 also opts out)
2833
- DG_FORCE_POSTINSTALL=1 Force postinstall to run even outside a TTY
2834
- (useful for re-running setup manually)
2825
+ DG_TELEMETRY=0 Opt out of anonymous crash reports (on by default;
2826
+ DO_NOT_TRACK=1 also opts out)
2835
2827
  DG_NO_RC_EDIT=1 Postinstall installs shims but does not edit
2836
2828
  shell rc files; you set PATH yourself
2829
+ NO_COLOR=1 Disable colored output
2837
2830
  CI=1 Auto-enables --ci output mode + --quiet
2838
2831
 
2839
2832
  Exit Codes:
@@ -2850,8 +2843,8 @@ var init_config = __esm({
2850
2843
  if the verdict permits. Top-level versions are pinned to the
2851
2844
  exact version we scanned (TOCTOU defense).
2852
2845
  - Warn verdict in a TTY: shows findings and prompts \`Proceed? (Y/n)\`.
2853
- - Block verdict: refuses install, prints the override command.
2854
- Override always requires \`--dg-force-reason\`.
2846
+ - Block verdict: refuses install, prints the override command
2847
+ (re-run with \`--dg-force\` to install anyway).
2855
2848
  - Read-only subcommands (npm view, pip show, pnpm list, etc.) skip
2856
2849
  the wrapper entirely \u2014 fast pass-through to the real binary.
2857
2850
  - \`dg scan\` is read-only and never blocks. It does not consume
@@ -2864,7 +2857,7 @@ var init_config = __esm({
2864
2857
  bun add some-pkg
2865
2858
  uv pip install numpy
2866
2859
  pipx install black
2867
- npm install risky-pkg --dg-force --dg-force-reason="emergency hotfix"
2860
+ npm install risky-pkg --dg-force
2868
2861
  dg scan --json | jq # CI-friendly
2869
2862
  dg status # see what's protected
2870
2863
  `.trimStart();
@@ -4006,7 +3999,6 @@ dg publish-check \u2014 scan a package about to be published
4006
3999
 
4007
4000
  Usage:
4008
4001
  dg publish-check [--ecosystem npm|pypi] [--json] [--dg-force]
4009
- [--dg-force-reason <text>]
4010
4002
 
4011
4003
  What it does:
4012
4004
  Scans the package artifact you're about to publish for bundled secrets,
@@ -4018,7 +4010,6 @@ Flags:
4018
4010
  --ecosystem <npm|pypi> Force ecosystem detection
4019
4011
  --json JSON output to stdout
4020
4012
  --dg-force Bypass block verdict (audited)
4021
- --dg-force-reason <text> Reason string stored with override
4022
4013
 
4023
4014
  Exit codes:
4024
4015
  0 pass No risks detected
@@ -14039,7 +14030,7 @@ var require_react_reconciler_development = __commonJS({
14039
14030
  var HostPortal = 4;
14040
14031
  var HostComponent = 5;
14041
14032
  var HostText = 6;
14042
- var Fragment6 = 7;
14033
+ var Fragment7 = 7;
14043
14034
  var Mode = 8;
14044
14035
  var ContextConsumer = 9;
14045
14036
  var ContextProvider = 10;
@@ -14179,7 +14170,7 @@ var require_react_reconciler_development = __commonJS({
14179
14170
  return "DehydratedFragment";
14180
14171
  case ForwardRef:
14181
14172
  return getWrappedName$1(type, type.render, "ForwardRef");
14182
- case Fragment6:
14173
+ case Fragment7:
14183
14174
  return "Fragment";
14184
14175
  case HostComponent:
14185
14176
  return type;
@@ -17313,7 +17304,7 @@ var require_react_reconciler_development = __commonJS({
17313
17304
  }
17314
17305
  }
17315
17306
  function updateFragment2(returnFiber, current2, fragment, lanes, key) {
17316
- if (current2 === null || current2.tag !== Fragment6) {
17307
+ if (current2 === null || current2.tag !== Fragment7) {
17317
17308
  var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);
17318
17309
  created.return = returnFiber;
17319
17310
  return created;
@@ -17716,7 +17707,7 @@ var require_react_reconciler_development = __commonJS({
17716
17707
  if (child.key === key) {
17717
17708
  var elementType = element.type;
17718
17709
  if (elementType === REACT_FRAGMENT_TYPE) {
17719
- if (child.tag === Fragment6) {
17710
+ if (child.tag === Fragment7) {
17720
17711
  deleteRemainingChildren(returnFiber, child.sibling);
17721
17712
  var existing = useFiber(child, element.props.children);
17722
17713
  existing.return = returnFiber;
@@ -23207,7 +23198,7 @@ var require_react_reconciler_development = __commonJS({
23207
23198
  var _resolvedProps2 = workInProgress2.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
23208
23199
  return updateForwardRef(current2, workInProgress2, type, _resolvedProps2, renderLanes2);
23209
23200
  }
23210
- case Fragment6:
23201
+ case Fragment7:
23211
23202
  return updateFragment(current2, workInProgress2, renderLanes2);
23212
23203
  case Mode:
23213
23204
  return updateMode(current2, workInProgress2, renderLanes2);
@@ -23644,7 +23635,7 @@ var require_react_reconciler_development = __commonJS({
23644
23635
  case SimpleMemoComponent:
23645
23636
  case FunctionComponent:
23646
23637
  case ForwardRef:
23647
- case Fragment6:
23638
+ case Fragment7:
23648
23639
  case Mode:
23649
23640
  case Profiler:
23650
23641
  case ContextConsumer:
@@ -28412,7 +28403,7 @@ var require_react_reconciler_development = __commonJS({
28412
28403
  return fiber;
28413
28404
  }
28414
28405
  function createFiberFromFragment(elements, mode, lanes, key) {
28415
- var fiber = createFiber(Fragment6, elements, key, mode);
28406
+ var fiber = createFiber(Fragment7, elements, key, mode);
28416
28407
  fiber.lanes = lanes;
28417
28408
  return fiber;
28418
28409
  }
@@ -42587,6 +42578,24 @@ var init_lockfile = __esm({
42587
42578
  });
42588
42579
 
42589
42580
  // src/commands/wrapper-shared.ts
42581
+ import { spawn as spawn2 } from "node:child_process";
42582
+ function spawnTrimmed(bin, args, env3) {
42583
+ return new Promise((resolve3) => {
42584
+ const child = spawn2(bin, args, { stdio: ["inherit", "pipe", "pipe"], shell: false, env: { ...env3, FORCE_COLOR: "1" } });
42585
+ let buf = "";
42586
+ const onData = (c) => {
42587
+ buf += c.toString();
42588
+ };
42589
+ child.stdout?.on("data", onData);
42590
+ child.stderr?.on("data", onData);
42591
+ child.on("error", () => resolve3(1));
42592
+ child.on("close", (code) => {
42593
+ const lines = buf.split("\n").filter((l) => l.replace(ANSI_RE, "").trim() !== "");
42594
+ if (lines.length) process.stdout.write(lines.join("\n") + "\n");
42595
+ resolve3(code ?? 1);
42596
+ });
42597
+ });
42598
+ }
42590
42599
  function injectIgnoreScripts(args, opts) {
42591
42600
  if (!opts.isNpmFamily) return args;
42592
42601
  if (!(opts.strict || opts.dgNoScripts)) return args;
@@ -42595,7 +42604,6 @@ function injectIgnoreScripts(args, opts) {
42595
42604
  }
42596
42605
  function stripDgFlags(args) {
42597
42606
  let dgForce = false;
42598
- let dgForceReason;
42599
42607
  let dgNoScripts = false;
42600
42608
  const filtered = [];
42601
42609
  for (let i = 0; i < args.length; i++) {
@@ -42604,15 +42612,6 @@ function stripDgFlags(args) {
42604
42612
  dgForce = true;
42605
42613
  continue;
42606
42614
  }
42607
- if (arg === "--dg-force-reason") {
42608
- dgForceReason = (args[i + 1] ?? "").slice(0, 500);
42609
- i++;
42610
- continue;
42611
- }
42612
- if (arg.startsWith("--dg-force-reason=")) {
42613
- dgForceReason = arg.slice("--dg-force-reason=".length).slice(0, 500);
42614
- continue;
42615
- }
42616
42615
  if (arg === "--dg-no-scripts") {
42617
42616
  dgNoScripts = true;
42618
42617
  continue;
@@ -42632,12 +42631,13 @@ function stripDgFlags(args) {
42632
42631
  if (consumed) continue;
42633
42632
  filtered.push(arg);
42634
42633
  }
42635
- return { filtered, dgForce, dgForceReason, dgNoScripts };
42634
+ return { filtered, dgForce, dgNoScripts };
42636
42635
  }
42637
- var DG_VALUE_FLAGS, DG_BOOLEAN_FLAGS;
42636
+ var ANSI_RE, DG_VALUE_FLAGS, DG_BOOLEAN_FLAGS;
42638
42637
  var init_wrapper_shared = __esm({
42639
42638
  "src/commands/wrapper-shared.ts"() {
42640
42639
  "use strict";
42640
+ ANSI_RE = /\x1b\[[0-9;]*m/g;
42641
42641
  DG_VALUE_FLAGS = /* @__PURE__ */ new Set([
42642
42642
  "--mode",
42643
42643
  "--api-url",
@@ -42646,6 +42646,8 @@ var init_wrapper_shared = __esm({
42646
42646
  "--workspace",
42647
42647
  "--output",
42648
42648
  "-o",
42649
+ // Removed flag: still consumed (with its value) so a legacy invocation is
42650
+ // silently stripped rather than leaking to the real installer as a package.
42649
42651
  "--dg-force-reason"
42650
42652
  ]);
42651
42653
  DG_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -42663,7 +42665,7 @@ var init_wrapper_shared = __esm({
42663
42665
  });
42664
42666
 
42665
42667
  // src/commands/npm-wrapper.ts
42666
- import { spawn as spawn2 } from "node:child_process";
42668
+ import { spawn as spawn3 } from "node:child_process";
42667
42669
  import { readFileSync as readFileSync9, existsSync as existsSync12, mkdtempSync, writeFileSync as writeFileSync8, rmSync } from "node:fs";
42668
42670
  import { join as join7 } from "node:path";
42669
42671
  import { tmpdir } from "node:os";
@@ -42678,7 +42680,7 @@ function shimSentinelEnv() {
42678
42680
  return childInstallEnv();
42679
42681
  }
42680
42682
  function parseNpmArgs(args) {
42681
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
42683
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
42682
42684
  const command = filtered[0] ?? "";
42683
42685
  const shouldScan = INSTALL_COMMANDS.has(command);
42684
42686
  const packages = [];
@@ -42699,7 +42701,6 @@ function parseNpmArgs(args) {
42699
42701
  packages,
42700
42702
  rawArgs: filtered,
42701
42703
  dgForce,
42702
- dgForceReason,
42703
42704
  dgNoScripts,
42704
42705
  shouldScan
42705
42706
  };
@@ -42755,7 +42756,7 @@ async function resolveVersion(spec) {
42755
42756
  return null;
42756
42757
  }
42757
42758
  return new Promise((resolve3) => {
42758
- const child = spawn2(realNpmBinary(), ["view", spec, "version", "--json"], {
42759
+ const child = spawn3(realNpmBinary(), ["view", spec, "version", "--json"], {
42759
42760
  stdio: ["pipe", "pipe", "pipe"],
42760
42761
  env: shimSentinelEnv()
42761
42762
  });
@@ -42831,9 +42832,12 @@ async function resolvePackages(specs) {
42831
42832
  return { resolved, failed };
42832
42833
  }
42833
42834
  function runNpm(args, opts) {
42835
+ if (opts?.trim) {
42836
+ return spawnTrimmed(realNpmBinary(), args, shimSentinelEnv());
42837
+ }
42834
42838
  return new Promise((resolve3) => {
42835
42839
  const stdio = opts?.stdoutToStderr ? ["inherit", "pipe", "inherit"] : ["inherit", "inherit", "inherit"];
42836
- const child = spawn2(realNpmBinary(), args, { stdio, shell: false, env: shimSentinelEnv() });
42840
+ const child = spawn3(realNpmBinary(), args, { stdio, shell: false, env: shimSentinelEnv() });
42837
42841
  if (opts?.stdoutToStderr && child.stdout) {
42838
42842
  child.stdout.on("data", (chunk) => {
42839
42843
  process.stderr.write(chunk);
@@ -42853,7 +42857,7 @@ async function resolveTreeNpm(specs) {
42853
42857
  try {
42854
42858
  writeFileSync8(join7(dir, "package.json"), '{"name":"_dg_resolve","version":"1.0.0","private":true}');
42855
42859
  const stdout = await new Promise((resolve3) => {
42856
- const child = spawn2(
42860
+ const child = spawn3(
42857
42861
  realNpmBinary(),
42858
42862
  [
42859
42863
  "install",
@@ -43013,10 +43017,10 @@ var init_npm_wrapper = __esm({
43013
43017
  });
43014
43018
 
43015
43019
  // src/commands/pip-wrapper.ts
43016
- import { spawn as spawn3 } from "node:child_process";
43020
+ import { spawn as spawn4 } from "node:child_process";
43017
43021
  import { existsSync as existsSync13 } from "node:fs";
43018
43022
  function parsePipArgs(args) {
43019
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
43023
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
43020
43024
  const command = filtered[0] ?? "";
43021
43025
  const shouldScan = INSTALL_COMMANDS2.has(command);
43022
43026
  const packages = [];
@@ -43043,7 +43047,6 @@ function parsePipArgs(args) {
43043
43047
  packages,
43044
43048
  rawArgs: filtered,
43045
43049
  dgForce,
43046
- dgForceReason,
43047
43050
  dgNoScripts,
43048
43051
  shouldScan,
43049
43052
  requirementsFile
@@ -43215,7 +43218,7 @@ async function detectPipBinary() {
43215
43218
  try {
43216
43219
  const parts = cmd.split(" ");
43217
43220
  const code = await new Promise((resolve3) => {
43218
- const child = spawn3(parts[0], [...parts.slice(1), "--version"], {
43221
+ const child = spawn4(parts[0], [...parts.slice(1), "--version"], {
43219
43222
  stdio: ["pipe", "pipe", "pipe"],
43220
43223
  timeout: 5e3,
43221
43224
  env: shimSentinelEnv2()
@@ -43234,9 +43237,12 @@ async function detectPipBinary() {
43234
43237
  async function runPip(args, opts) {
43235
43238
  const pipCmd = await detectPipBinary();
43236
43239
  const parts = pipCmd.split(" ");
43240
+ if (opts?.trim) {
43241
+ return spawnTrimmed(parts[0], [...parts.slice(1), ...args], shimSentinelEnv2());
43242
+ }
43237
43243
  return new Promise((resolve3) => {
43238
43244
  const stdio = opts?.stdoutToStderr ? ["inherit", "pipe", "inherit"] : ["inherit", "inherit", "inherit"];
43239
- const child = spawn3(parts[0], [...parts.slice(1), ...args], { stdio, shell: false, env: shimSentinelEnv2() });
43245
+ const child = spawn4(parts[0], [...parts.slice(1), ...args], { stdio, shell: false, env: shimSentinelEnv2() });
43240
43246
  if (opts?.stdoutToStderr && child.stdout) {
43241
43247
  child.stdout.on("data", (chunk) => {
43242
43248
  process.stderr.write(chunk);
@@ -43251,7 +43257,7 @@ async function pipVersion() {
43251
43257
  const pipCmd = await detectPipBinary();
43252
43258
  const parts = pipCmd.split(" ");
43253
43259
  return await new Promise((resolve3) => {
43254
- const child = spawn3(parts[0], [...parts.slice(1), "--version"], {
43260
+ const child = spawn4(parts[0], [...parts.slice(1), "--version"], {
43255
43261
  stdio: ["pipe", "pipe", "pipe"],
43256
43262
  timeout: 5e3,
43257
43263
  env: shimSentinelEnv2()
@@ -43309,7 +43315,7 @@ async function resolveTreePip(specs) {
43309
43315
  const pipCmd = await detectPipBinary();
43310
43316
  const parts = pipCmd.split(" ");
43311
43317
  const runDryRun = (extraFlags) => new Promise((resolve3) => {
43312
- const child = spawn3(
43318
+ const child = spawn4(
43313
43319
  parts[0],
43314
43320
  [
43315
43321
  ...parts.slice(1),
@@ -44955,7 +44961,6 @@ async function dispatchPublishCheckAudit(opts) {
44955
44961
  score: opts.score,
44956
44962
  action: opts.decisionAction,
44957
44963
  bypassed: opts.bypassed,
44958
- bypass_reason: opts.bypassReason,
44959
44964
  event_kind: "publish_check"
44960
44965
  },
44961
44966
  { apiUrl: opts.apiUrl }
@@ -44977,7 +44982,6 @@ function recordBypassLocally(record) {
44977
44982
  ecosystem: record.ecosystem,
44978
44983
  verdict: record.verdict,
44979
44984
  trigger: record.trigger,
44980
- reason: record.reason,
44981
44985
  packages: record.packages
44982
44986
  };
44983
44987
  appendFileSync3(path, JSON.stringify(full) + "\n", { mode: 384 });
@@ -45000,7 +45004,6 @@ async function dispatchInstallAudit(opts) {
45000
45004
  score: p.score,
45001
45005
  action: opts.decisionAction,
45002
45006
  bypassed: opts.bypassed,
45003
- bypass_reason: opts.bypassReason,
45004
45007
  project_hash: opts.projectHash
45005
45008
  },
45006
45009
  { apiUrl: opts.apiUrl }
@@ -45815,21 +45818,19 @@ ${installLine}
45815
45818
  const blockedSpecArg = topLevelSpecs.length > 0 ? topLevelSpecs.join(" ") : topLevel.split(", ").map((s) => s.replace(/ /g, "@")).join(" ");
45816
45819
  process.stderr.write(
45817
45820
  ` ${import_chalk5.default.red("\u2717")} ${import_chalk5.default.bold("DG blocked")} ${topLevel}.
45818
- Real install was NOT run. To override:
45819
- ` + import_chalk5.default.dim(` npm install ${blockedSpecArg} --dg-force --dg-force-reason="<reason>"
45821
+ ${import_chalk5.default.dim(`install anyway: npm install ${blockedSpecArg} --dg-force`)}
45820
45822
 
45821
- `)
45823
+ `
45822
45824
  );
45823
45825
  }
45824
45826
  } else if (d.verdict === "analysis_incomplete") {
45825
45827
  if (d.outcome === "refuse") {
45826
45828
  const specArg = topLevelSpecs.length > 0 ? topLevelSpecs.join(" ") : resolved.map((p) => `${p.name}@${p.version}`).join(" ");
45827
45829
  process.stderr.write(
45828
- ` ${import_chalk5.default.cyan("?")} ${import_chalk5.default.bold("DG could not verify")} ${topLevel} \u2014 treating as unverified, not safe.
45829
- Real install was NOT run (--mode block). To override:
45830
- ` + import_chalk5.default.dim(` npm install ${specArg} --dg-force --dg-force-reason="<reason>"
45830
+ ` ${import_chalk5.default.cyan("?")} ${import_chalk5.default.bold("DG couldn't verify")} ${topLevel} \u2014 unverified, not safe.
45831
+ ${import_chalk5.default.dim(`install anyway: npm install ${specArg} --dg-force`)}
45831
45832
 
45832
- `)
45833
+ `
45833
45834
  );
45834
45835
  } else {
45835
45836
  process.stderr.write(import_chalk5.default.dim(` DG could not fully verify ${topLevel}; proceeding (mode is not block).
@@ -45851,8 +45852,7 @@ ${installLine}
45851
45852
  ecosystem: "npm",
45852
45853
  packages: result.packages,
45853
45854
  decisionAction: kind,
45854
- bypassed: kind === "override",
45855
- bypassReason: kind === "override" ? parsed.dgForceReason : void 0
45855
+ bypassed: kind === "override"
45856
45856
  });
45857
45857
  },
45858
45858
  emit(record) {
@@ -45873,8 +45873,7 @@ ${installLine}
45873
45873
  passthroughArgs: parsed.rawArgs,
45874
45874
  result,
45875
45875
  resolved,
45876
- suppressedCount: 0,
45877
- dgForceReason: parsed.dgForceReason
45876
+ suppressedCount: 0
45878
45877
  };
45879
45878
  process.exit(await executeDecision(npmDecision, npmCtx, npmPresenter));
45880
45879
  }
@@ -46072,21 +46071,19 @@ ${installLine}
46072
46071
  const pipBlockedSpecArg = resolved.map((p) => `${p.name}==${p.version}`).join(" ");
46073
46072
  process.stderr.write(
46074
46073
  ` ${import_chalk5.default.red("\u2717")} ${import_chalk5.default.bold("DG blocked")} ${topLevel}.
46075
- Real install was NOT run. To override:
46076
- ` + import_chalk5.default.dim(` pip install ${pipBlockedSpecArg} --dg-force --dg-force-reason="<reason>"
46074
+ ${import_chalk5.default.dim(`install anyway: pip install ${pipBlockedSpecArg} --dg-force`)}
46077
46075
 
46078
- `)
46076
+ `
46079
46077
  );
46080
46078
  }
46081
46079
  } else if (d.verdict === "analysis_incomplete") {
46082
46080
  if (d.outcome === "refuse") {
46083
46081
  const pipSpecArg = resolved.map((p) => `${p.name}==${p.version}`).join(" ");
46084
46082
  process.stderr.write(
46085
- ` ${import_chalk5.default.cyan("?")} ${import_chalk5.default.bold("DG could not verify")} ${topLevel} \u2014 treating as unverified, not safe.
46086
- Real install was NOT run (--mode block). To override:
46087
- ` + import_chalk5.default.dim(` pip install ${pipSpecArg} --dg-force --dg-force-reason="<reason>"
46083
+ ` ${import_chalk5.default.cyan("?")} ${import_chalk5.default.bold("DG couldn't verify")} ${topLevel} \u2014 unverified, not safe.
46084
+ ${import_chalk5.default.dim(`install anyway: pip install ${pipSpecArg} --dg-force`)}
46088
46085
 
46089
- `)
46086
+ `
46090
46087
  );
46091
46088
  } else {
46092
46089
  process.stderr.write(import_chalk5.default.dim(` DG could not fully verify ${topLevel}; proceeding (mode is not block).
@@ -46108,8 +46105,7 @@ ${installLine}
46108
46105
  ecosystem: "pypi",
46109
46106
  packages: result.packages,
46110
46107
  decisionAction: kind,
46111
- bypassed: kind === "override",
46112
- bypassReason: kind === "override" ? parsed.dgForceReason : void 0
46108
+ bypassed: kind === "override"
46113
46109
  });
46114
46110
  },
46115
46111
  emit(record) {
@@ -46130,8 +46126,7 @@ ${installLine}
46130
46126
  passthroughArgs: parsed.rawArgs,
46131
46127
  result,
46132
46128
  resolved,
46133
- suppressedCount: 0,
46134
- dgForceReason: parsed.dgForceReason
46129
+ suppressedCount: 0
46135
46130
  };
46136
46131
  process.exit(await executeDecision(pipDecision, pipCtx, pipPresenter));
46137
46132
  }
@@ -48749,7 +48744,7 @@ __export(publish_check_exports, {
48749
48744
  scanPayload: () => scanPayload,
48750
48745
  summarize: () => summarize
48751
48746
  });
48752
- import { spawn as spawn4 } from "node:child_process";
48747
+ import { spawn as spawn5 } from "node:child_process";
48753
48748
  import { readFileSync as readFileSync13, existsSync as existsSync19, readdirSync as readdirSync3 } from "node:fs";
48754
48749
  import { join as join12, basename as basename2 } from "node:path";
48755
48750
  import { createGunzip } from "node:zlib";
@@ -48761,7 +48756,7 @@ function sanitizeSnippet(s, max = 40) {
48761
48756
  }
48762
48757
  async function npmPackDryRun(cwd2 = process.cwd()) {
48763
48758
  return new Promise((resolve3) => {
48764
- const child = spawn4("npm", ["pack", "--dry-run", "--json"], {
48759
+ const child = spawn5("npm", ["pack", "--dry-run", "--json"], {
48765
48760
  cwd: cwd2,
48766
48761
  stdio: ["ignore", "pipe", "pipe"]
48767
48762
  });
@@ -51805,24 +51800,18 @@ var init_readonly = __esm({
51805
51800
  // src/ui/hooks/useWrapperBase.ts
51806
51801
  function reducer4(_state, action) {
51807
51802
  switch (action.type) {
51808
- case "PASSTHROUGH":
51809
- return { phase: "passthrough" };
51810
51803
  case "RESOLVING":
51811
51804
  return { phase: "resolving", count: action.count };
51812
51805
  case "SCANNING":
51813
51806
  return { phase: "scanning", count: action.count };
51814
- case "PASS":
51815
- return { phase: "pass", count: action.count, result: action.result };
51816
51807
  case "WARN":
51817
- return { phase: "warn", result: action.result, dgForce: action.dgForce };
51818
- case "BLOCKED":
51819
- return { phase: "blocked", result: action.result, dgForce: action.dgForce };
51820
- case "INCOMPLETE":
51821
- return { phase: "incomplete", result: action.result, message: action.message, proceed: action.proceed };
51808
+ return { phase: "warn", result: action.result };
51822
51809
  case "REFUSED":
51823
- return { phase: "refused", result: action.result, message: action.message };
51810
+ return { phase: "refused", result: action.result, verdict: action.verdict, cmd: action.cmd, exitCode: action.exitCode };
51811
+ case "DECLINED":
51812
+ return { phase: "declined", line: action.line, exitCode: action.exitCode };
51824
51813
  case "INSTALLING":
51825
- return { phase: "installing" };
51814
+ return { phase: "installing", line: action.line };
51826
51815
  case "DONE":
51827
51816
  return { phase: "done", exitCode: action.exitCode };
51828
51817
  case "ERROR":
@@ -51831,12 +51820,17 @@ function reducer4(_state, action) {
51831
51820
  return { phase: "free_cap_reached" };
51832
51821
  }
51833
51822
  }
51834
- function useInstallWrapper(rawArgs, config, opts, exit) {
51823
+ function useInstallWrapper(rawArgs, config, opts, handoff) {
51835
51824
  const [state, dispatch] = (0, import_react37.useReducer)(reducer4, { phase: "resolving", count: 0 });
51836
51825
  const started = (0, import_react37.useRef)(false);
51837
51826
  const parsedRef = (0, import_react37.useRef)(opts.parseArgs(rawArgs));
51838
51827
  const pendingInstall = (0, import_react37.useRef)(null);
51839
51828
  const rejectRef = (0, import_react37.useRef)(null);
51829
+ const ho = handoff ?? { install: null };
51830
+ const requestInstall = (args, line) => {
51831
+ ho.install = { run: opts.runInstall, args };
51832
+ dispatch({ type: "INSTALLING", line });
51833
+ };
51840
51834
  const confirmInstall = () => {
51841
51835
  if (pendingInstall.current) {
51842
51836
  pendingInstall.current();
@@ -51859,18 +51853,14 @@ function useInstallWrapper(rawArgs, config, opts, exit) {
51859
51853
  (async () => {
51860
51854
  try {
51861
51855
  if (!parsed.shouldScan) {
51862
- dispatch({ type: "PASSTHROUGH" });
51863
- const code2 = await opts.runInstall(parsed.rawArgs);
51864
- dispatch({ type: "DONE", exitCode: code2 });
51856
+ requestInstall(parsed.rawArgs, null);
51865
51857
  return;
51866
51858
  }
51867
51859
  let specs;
51868
51860
  if (parsed.packages.length === 0) {
51869
51861
  specs = opts.inferSpecsFromContext(parsed);
51870
51862
  if (specs.length === 0) {
51871
- dispatch({ type: "PASSTHROUGH" });
51872
- const code2 = await opts.runInstall(parsed.rawArgs);
51873
- dispatch({ type: "DONE", exitCode: code2 });
51863
+ requestInstall(parsed.rawArgs, null);
51874
51864
  return;
51875
51865
  }
51876
51866
  } else {
@@ -51879,9 +51869,7 @@ function useInstallWrapper(rawArgs, config, opts, exit) {
51879
51869
  dispatch({ type: "RESOLVING", count: specs.length });
51880
51870
  const { resolved } = await opts.resolvePackages(specs);
51881
51871
  if (resolved.length === 0) {
51882
- dispatch({ type: "PASSTHROUGH" });
51883
- const code2 = await opts.runInstall(parsed.rawArgs);
51884
- dispatch({ type: "DONE", exitCode: code2 });
51872
+ requestInstall(parsed.rawArgs, null);
51885
51873
  return;
51886
51874
  }
51887
51875
  const isRequirementsInstall = opts.ecosystem === "pypi" && (parsed.rawArgs.includes("-r") || parsed.rawArgs.includes("--requirement"));
@@ -51891,7 +51879,6 @@ function useInstallWrapper(rawArgs, config, opts, exit) {
51891
51879
  message: `A -r requirements install cannot be pinned to the exact scanned versions; refusing in ${config.strict ? "--strict" : "--mode block"} (resolve\u2192install mismatch risk). Use --mode warn or --dg-force to bypass.`,
51892
51880
  proceed: false
51893
51881
  });
51894
- dispatch({ type: "DONE", exitCode: 2 });
51895
51882
  return;
51896
51883
  }
51897
51884
  const pinnedArgs = isRequirementsInstall ? parsed.rawArgs : opts.ecosystem === "npm" ? pinTopLevelArgs(parsed.rawArgs, parsed.packages, resolved) : pinPipArgs(parsed.rawArgs, parsed.packages, resolved);
@@ -51911,98 +51898,60 @@ function useInstallWrapper(rawArgs, config, opts, exit) {
51911
51898
  message: `Could not enumerate the dependency tree (${tree.errorMessage ?? "dry-run produced no tree"}); transitive deps would install unscanned. Install aborted in ${config.strict ? "--strict" : "--mode block"}. Use --dg-force to bypass.`,
51912
51899
  proceed: false
51913
51900
  });
51914
- dispatch({ type: "DONE", exitCode: 2 });
51915
51901
  return;
51916
51902
  }
51917
51903
  }
51918
51904
  dispatch({ type: "SCANNING", count: scanSet.length });
51919
51905
  const result = await opts.callAnalyze(scanSet, config);
51920
- if (result.action === "pass") {
51921
- if (exit) exit();
51922
- const chalk18 = (await Promise.resolve().then(() => __toESM(require_source(), 1))).default;
51923
- const line = formatPassLine(result, chalk18);
51924
- process.stderr.write(" " + line + "\n\n");
51925
- const code2 = await opts.runInstall(installArgs);
51926
- process.exit(code2);
51927
- return;
51928
- }
51929
- const verdict = verdictFromResult(result);
51930
51906
  const decision = routeVerdict({
51931
- verdict,
51907
+ verdict: verdictFromResult(result),
51932
51908
  mode: config.mode,
51933
51909
  strict: config.strict,
51934
51910
  dgForce: parsed.dgForce
51935
51911
  });
51936
- const bypassVerdict = decision.verdict === "block" ? "block" : "warn";
51912
+ if (decision.outcome === "refuse") {
51913
+ const cmd = `${opts.ecosystem === "npm" ? "npm" : "pip"} ${parsed.rawArgs.join(" ")} --dg-force`;
51914
+ const v = decision.verdict === "analysis_incomplete" ? "incomplete" : decision.verdict === "block" ? "block" : "warn";
51915
+ dispatch({ type: "REFUSED", result, verdict: v, cmd, exitCode: decision.refuseExitCode });
51916
+ return;
51917
+ }
51937
51918
  const recordBypass = async (trigger) => {
51938
51919
  const { recordBypassLocally: recordBypassLocally2 } = await Promise.resolve().then(() => (init_audit_dispatcher(), audit_dispatcher_exports));
51939
51920
  recordBypassLocally2({
51940
51921
  ecosystem: opts.ecosystem,
51941
- verdict: bypassVerdict,
51922
+ verdict: decision.verdict === "block" ? "block" : "warn",
51942
51923
  trigger,
51943
- reason: trigger === "dg-force" ? parsed.dgForceReason : void 0,
51944
51924
  packages: result.packages.map((p) => ({ name: p.name, version: p.version, score: p.score }))
51945
51925
  });
51946
51926
  };
51947
- const refusedMessage = () => {
51948
- const list = resolved.map((p) => `${p.name} ${p.version}`).join(", ");
51949
- if (decision.verdict === "block") {
51950
- return `DG blocked ${list}. Real install was NOT run. Re-run with --dg-force --dg-force-reason="<reason>" to override.`;
51951
- }
51952
- if (decision.verdict === "analysis_incomplete") {
51953
- return `DG could not fully analyze ${list} (analysis incomplete). Refusing in ${config.strict ? "--strict" : "--mode block"} \u2014 unverified is not safe. Use --mode warn or --dg-force to bypass.`;
51954
- }
51955
- return `DG flagged ${list}. Refusing in --strict. Use --mode warn or --dg-force to proceed.`;
51956
- };
51957
- const presenter = {
51958
- announce(d) {
51959
- if (d.outcome === "refuse") {
51960
- dispatch({ type: "REFUSED", result, message: refusedMessage() });
51961
- return;
51962
- }
51963
- switch (d.verdict) {
51964
- case "warn":
51965
- dispatch({ type: "WARN", result, dgForce: d.bypassed });
51966
- return;
51967
- case "block":
51968
- dispatch({ type: "BLOCKED", result, dgForce: d.bypassed });
51969
- return;
51970
- case "analysis_incomplete":
51971
- dispatch({ type: "INCOMPLETE", result, proceed: true, message: "DG could not fully analyze the install set (analysis incomplete)." });
51972
- return;
51973
- default:
51974
- return;
51975
- }
51976
- },
51977
- confirmProceed() {
51978
- return new Promise((resolve3) => {
51979
- pendingInstall.current = () => resolve3(true);
51980
- rejectRef.current = () => resolve3(false);
51981
- });
51982
- },
51983
- async runInstall(args) {
51984
- dispatch({ type: "INSTALLING" });
51985
- return opts.runInstall(args);
51986
- },
51987
- async audit(kind) {
51988
- if (kind === "override") await recordBypass("dg-force");
51989
- },
51990
- onPromptAccepted: () => recordBypass("interactive-confirm"),
51991
- emit() {
51927
+ const chalk18 = (await Promise.resolve().then(() => __toESM(require_source(), 1))).default;
51928
+ const top = result.packages.length === 1 ? result.packages[0] : null;
51929
+ const name = top ? `${top.name}@${top.version}` : `${result.packages.length} packages`;
51930
+ const pinnedLine = (() => {
51931
+ if (decision.bypassed) {
51932
+ const sig = decision.verdict === "block" ? chalk18.red("\u2718") : chalk18.yellow("\u26A0");
51933
+ return ` ${sig} ${name} ${chalk18.dim(`(${decision.verdict}) \u2014 --dg-force override, installing`)}`;
51934
+ }
51935
+ if (decision.verdict === "analysis_incomplete") return ` ${chalk18.cyan("?")} ${name} ${chalk18.dim("\u2014 could not verify; proceeding")}`;
51936
+ if (decision.verdict === "pass") return ` ${chalk18.green("\u2713")} ${name} ${chalk18.dim("\u2014 clean")}`;
51937
+ return null;
51938
+ })();
51939
+ if (decision.outcome === "prompt") {
51940
+ dispatch({ type: "WARN", result });
51941
+ const accepted = await new Promise((resolve3) => {
51942
+ pendingInstall.current = () => resolve3(true);
51943
+ rejectRef.current = () => resolve3(false);
51944
+ });
51945
+ if (!accepted) {
51946
+ dispatch({ type: "DECLINED", line: ` ${chalk18.yellow("\u2717")} ${name} ${chalk18.dim("\u2014 not installed")}`, exitCode: decision.refuseExitCode });
51947
+ return;
51992
51948
  }
51993
- };
51994
- const ctx = {
51995
- ecosystem: opts.ecosystem,
51996
- apiKind: opts.ecosystem,
51997
- installArgs,
51998
- passthroughArgs: parsed.rawArgs,
51999
- result,
52000
- resolved,
52001
- suppressedCount: 0,
52002
- dgForceReason: parsed.dgForceReason
52003
- };
52004
- const code = await executeDecision(decision, ctx, presenter);
52005
- dispatch({ type: "DONE", exitCode: code });
51949
+ await recordBypass("interactive-confirm");
51950
+ requestInstall(installArgs, null);
51951
+ return;
51952
+ }
51953
+ if (decision.bypassed) await recordBypass("dg-force");
51954
+ requestInstall(decision.outcome === "passthrough" ? parsed.rawArgs : installArgs, pinnedLine);
52006
51955
  return;
52007
51956
  } catch (error) {
52008
51957
  if (error instanceof FreeCapReachedError) {
@@ -52016,27 +51965,17 @@ function useInstallWrapper(rawArgs, config, opts, exit) {
52016
51965
  message: `Scan failed (${message}) and --mode is block. Install aborted. Use --dg-force to bypass.`,
52017
51966
  proceed: false
52018
51967
  });
52019
- dispatch({ type: "DONE", exitCode: 2 });
52020
51968
  return;
52021
51969
  }
52022
- dispatch({ type: "ERROR", message, proceed: true });
52023
- dispatch({ type: "INSTALLING" });
52024
- const code = await opts.runInstall(installArgs);
52025
- dispatch({ type: "DONE", exitCode: code });
51970
+ const chalk18 = (await Promise.resolve().then(() => __toESM(require_source(), 1))).default;
51971
+ requestInstall(installArgs, ` ${chalk18.yellow("\u26A0")} ${chalk18.dim(`scan failed (${message}); proceeding`)}`);
52026
51972
  }
52027
51973
  })();
52028
51974
  }, [rawArgs, config]);
52029
51975
  return { state, confirmInstall, rejectInstall };
52030
51976
  }
52031
- function formatPassLine(result, chalk18) {
52032
- if (result.packages.length === 1) {
52033
- const p = result.packages[0];
52034
- return chalk18.green("\u2713 ") + `${p.name}@${p.version}` + chalk18.dim(" \u2014 clean. Installing.");
52035
- }
52036
- return chalk18.green("\u2713 ") + `${result.packages.length} packages` + chalk18.dim(" \u2014 all clean. Installing.");
52037
- }
52038
- function useWrapper(args, config, ecosystem, exit) {
52039
- return useInstallWrapper(args, config, ecosystem === "npm" ? NPM_OPTIONS : PIP_OPTIONS, exit);
51977
+ function useWrapper(args, config, ecosystem, handoff) {
51978
+ return useInstallWrapper(args, config, ecosystem === "npm" ? NPM_OPTIONS : PIP_OPTIONS, handoff);
52040
51979
  }
52041
51980
  var import_react37, NPM_OPTIONS, PIP_OPTIONS;
52042
51981
  var init_useWrapperBase = __esm({
@@ -52054,7 +51993,7 @@ var init_useWrapperBase = __esm({
52054
51993
  resolvePackages,
52055
51994
  resolveTree: resolveTreeNpm,
52056
51995
  callAnalyze: callAnalyzeAPI,
52057
- runInstall: runNpm,
51996
+ runInstall: (args) => runNpm(args, { trim: true }),
52058
51997
  inferSpecsFromContext: () => readBareInstallPackages(process.cwd()),
52059
51998
  ecosystem: "npm"
52060
51999
  };
@@ -52063,7 +52002,7 @@ var init_useWrapperBase = __esm({
52063
52002
  resolvePackages: resolvePackages2,
52064
52003
  resolveTree: resolveTreePip,
52065
52004
  callAnalyze: callPyPIAnalyzeAPI,
52066
- runInstall: runPip,
52005
+ runInstall: (args) => runPip(args, { trim: true }),
52067
52006
  inferSpecsFromContext: (parsed) => {
52068
52007
  const file = parsed.requirementsFile;
52069
52008
  return file ? parseRequirementsFile(file) : [];
@@ -52245,6 +52184,7 @@ var init_ConfirmPrompt = __esm({
52245
52184
  onReject();
52246
52185
  }
52247
52186
  });
52187
+ if (!message) return null;
52248
52188
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Box_default, { flexDirection: "column", children: [
52249
52189
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Text, { children: [
52250
52190
  import_chalk17.default.red.bold("\u26A0"),
@@ -52300,7 +52240,8 @@ var init_WrapperVerdictLine = __esm({
52300
52240
  WrapperVerdictLine = ({
52301
52241
  result,
52302
52242
  verdict,
52303
- dgForce
52243
+ dgForce,
52244
+ refusal
52304
52245
  }) => {
52305
52246
  const total = result.packages.length;
52306
52247
  const counts = countSummary(result);
@@ -52326,12 +52267,13 @@ var init_WrapperVerdictLine = __esm({
52326
52267
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { dimColor: true, children: " \u2014 all clean. Installing." })
52327
52268
  ] });
52328
52269
  }
52329
- const sigil = verdict === "block" ? "\u2718" : "\u26A0";
52330
- const color = verdict === "block" ? "red" : "yellow";
52270
+ const sigil = verdict === "block" ? "\u2718" : verdict === "incomplete" ? "?" : "\u26A0";
52271
+ const color = verdict === "block" ? "red" : verdict === "incomplete" ? "cyan" : "yellow";
52331
52272
  const prompt = verdict === "block" ? "install anyway?" : "install?";
52332
52273
  if (total === 1) {
52333
52274
  const p = topPackage(result);
52334
52275
  const score = p ? p.score : result.score;
52276
+ const meta = verdict === "incomplete" ? `(could not verify)${reasonTag(p)}` : `(${verdict}, score ${score})${reasonTag(p)}`;
52335
52277
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Box_default, { flexDirection: "row", children: [
52336
52278
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { color, children: [
52337
52279
  sigil,
@@ -52342,18 +52284,23 @@ var init_WrapperVerdictLine = __esm({
52342
52284
  "@",
52343
52285
  p?.version
52344
52286
  ] }),
52345
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52346
- " (",
52347
- verdict,
52348
- ", score ",
52349
- score,
52350
- ")",
52351
- reasonTag(p),
52352
- " \u2014 "
52353
- ] }),
52354
- dgForce ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color, bold: true, children: "--dg-force: bypassing. Installing." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { bold: true, children: [
52355
- prompt,
52356
- " [y/N]"
52287
+ refusal ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52288
+ " ",
52289
+ meta
52290
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
52291
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52292
+ " ",
52293
+ meta,
52294
+ " \u2014 "
52295
+ ] }),
52296
+ dgForce ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color, bold: true, children: "--dg-force: bypassing. Installing." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { bold: true, children: [
52297
+ prompt,
52298
+ " [",
52299
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color: "green", children: "y" }),
52300
+ "/",
52301
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color: "red", children: "N" }),
52302
+ "]"
52303
+ ] })
52357
52304
  ] })
52358
52305
  ] });
52359
52306
  }
@@ -52371,14 +52318,23 @@ var init_WrapperVerdictLine = __esm({
52371
52318
  total,
52372
52319
  " packages"
52373
52320
  ] }),
52374
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52321
+ refusal ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52375
52322
  ": ",
52376
- parts.join(", "),
52377
- " \u2014 "
52378
- ] }),
52379
- dgForce ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color, bold: true, children: "--dg-force: bypassing. Installing." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { bold: true, children: [
52380
- prompt,
52381
- " [y/N] [d] details"
52323
+ parts.join(", ")
52324
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
52325
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { dimColor: true, children: [
52326
+ ": ",
52327
+ parts.join(", "),
52328
+ " \u2014 "
52329
+ ] }),
52330
+ dgForce ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color, bold: true, children: "--dg-force: bypassing. Installing." }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text, { bold: true, children: [
52331
+ prompt,
52332
+ " [",
52333
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color: "green", children: "y" }),
52334
+ "/",
52335
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { color: "red", children: "N" }),
52336
+ "] [d] details"
52337
+ ] })
52382
52338
  ] })
52383
52339
  ] });
52384
52340
  };
@@ -52416,21 +52372,30 @@ var init_WrapperApp = __esm({
52416
52372
  installing: "Installing with pip..."
52417
52373
  }
52418
52374
  };
52419
- WrapperApp = ({ args, config, ecosystem }) => {
52375
+ WrapperApp = ({ args, config, ecosystem, handoff }) => {
52420
52376
  const { exit } = use_app_default();
52421
- const { state, confirmInstall, rejectInstall } = useWrapper(args, config, ecosystem, exit);
52377
+ const { state, confirmInstall, rejectInstall } = useWrapper(args, config, ecosystem, handoff);
52422
52378
  const [showDetails, setShowDetails] = (0, import_react38.useState)(false);
52423
52379
  use_input_default((input) => {
52424
- if (input === "d" && (state.phase === "warn" || state.phase === "blocked")) {
52380
+ if (input === "d" && state.phase === "warn") {
52425
52381
  setShowDetails((v) => !v);
52426
52382
  }
52427
52383
  });
52428
52384
  (0, import_react38.useEffect)(() => {
52429
- if (state.phase === "done") {
52385
+ if (state.phase === "done" || state.phase === "refused" || state.phase === "declined") {
52430
52386
  process.exitCode = state.exitCode;
52431
52387
  const timer = setTimeout(() => exit(), 0);
52432
52388
  return () => clearTimeout(timer);
52433
52389
  }
52390
+ if (state.phase === "installing") {
52391
+ const timer = setTimeout(() => exit(), 0);
52392
+ return () => clearTimeout(timer);
52393
+ }
52394
+ if (state.phase === "error") {
52395
+ process.exitCode = 2;
52396
+ const timer = setTimeout(() => exit(), 0);
52397
+ return () => clearTimeout(timer);
52398
+ }
52434
52399
  if (state.phase === "free_cap_reached") {
52435
52400
  process.exitCode = 1;
52436
52401
  const timer = setTimeout(() => exit(), 0);
@@ -52445,44 +52410,30 @@ var init_WrapperApp = __esm({
52445
52410
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner2, { label: labels.resolving(state.count, state.count !== 1 ? "s" : "") });
52446
52411
  case "scanning":
52447
52412
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner2, { label: labels.scanning(state.count, state.count !== 1 ? "s" : "") });
52448
- case "pass":
52449
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(WrapperVerdictLine, { result: state.result, verdict: "pass" });
52450
52413
  case "warn":
52451
52414
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", children: [
52452
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(WrapperVerdictLine, { result: state.result, verdict: "warn", dgForce: state.dgForce }),
52415
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(WrapperVerdictLine, { result: state.result, verdict: "warn" }),
52453
52416
  showDetails && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResultsView, { result: state.result, config, durationMs: state.result.durationMs }) }),
52454
- !state.dgForce && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConfirmPrompt, { message: "", onConfirm: handleConfirm, onReject: handleReject })
52417
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConfirmPrompt, { message: "", onConfirm: handleConfirm, onReject: handleReject })
52455
52418
  ] });
52456
- case "blocked":
52419
+ case "refused":
52457
52420
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Box_default, { flexDirection: "column", children: [
52458
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(WrapperVerdictLine, { result: state.result, verdict: "block", dgForce: state.dgForce }),
52459
- showDetails && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResultsView, { result: state.result, config, durationMs: state.result.durationMs }) })
52421
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(WrapperVerdictLine, { result: state.result, verdict: state.verdict, refusal: true }),
52422
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { dimColor: true, children: [
52423
+ " install anyway: ",
52424
+ state.cmd
52425
+ ] })
52460
52426
  ] });
52461
- case "refused":
52462
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorView, { error: new Error(state.message) });
52463
- case "incomplete":
52464
- if (state.proceed) {
52465
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { color: "yellow", children: [
52466
- state.message,
52467
- " Proceeding with install."
52468
- ] });
52469
- }
52470
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorView, { error: new Error(state.message) });
52427
+ case "declined":
52428
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { children: state.line });
52429
+ // The verdict line is Ink's final frame; it stays on screen after unmount and
52430
+ // the installer's (trimmed) output is printed under it by bin.ts.
52471
52431
  case "installing":
52472
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Spinner2, { label: labels.installing });
52432
+ return state.line ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { children: state.line }) : null;
52473
52433
  case "done":
52474
52434
  return null;
52475
52435
  case "error":
52476
- if (state.proceed) {
52477
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text, { color: "yellow", children: [
52478
- "Warning: Scan failed (",
52479
- state.message,
52480
- "). Proceeding with install."
52481
- ] });
52482
- }
52483
52436
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorView, { error: new Error(state.message) });
52484
- case "passthrough":
52485
- return null;
52486
52437
  case "free_cap_reached": {
52487
52438
  let hasKey = false;
52488
52439
  try {
@@ -52518,9 +52469,9 @@ __export(conda_wrapper_exports, {
52518
52469
  adapter: () => adapter,
52519
52470
  runStaticConda: () => runStaticConda
52520
52471
  });
52521
- import { spawn as spawn5 } from "node:child_process";
52472
+ import { spawn as spawn6 } from "node:child_process";
52522
52473
  function parseCondaArgs(args) {
52523
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52474
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
52524
52475
  const command = filtered[0] ?? "";
52525
52476
  const shouldScan = INSTALL_COMMANDS3.has(command);
52526
52477
  const packages = [];
@@ -52539,7 +52490,6 @@ function parseCondaArgs(args) {
52539
52490
  packages,
52540
52491
  rawArgs: filtered,
52541
52492
  dgForce,
52542
- dgForceReason,
52543
52493
  dgNoScripts,
52544
52494
  shouldScan
52545
52495
  };
@@ -52573,7 +52523,7 @@ async function runStaticConda(args, ecosystem, config) {
52573
52523
  return 127;
52574
52524
  }
52575
52525
  return await new Promise((resolve3) => {
52576
- const child = spawn5(real, parsed.rawArgs, { stdio: "inherit", env: childInstallEnv() });
52526
+ const child = spawn6(real, parsed.rawArgs, { stdio: "inherit", env: childInstallEnv() });
52577
52527
  child.on("close", (code) => resolve3(code ?? 1));
52578
52528
  child.on("error", () => resolve3(1));
52579
52529
  });
@@ -52620,7 +52570,7 @@ function pnpmFlagTakesValue(flag) {
52620
52570
  return valueFlagPrefixes.includes(flag);
52621
52571
  }
52622
52572
  function parsePnpmArgs(args) {
52623
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52573
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
52624
52574
  const command = filtered[0] ?? "";
52625
52575
  const shouldScan = INSTALL_COMMANDS4.has(command);
52626
52576
  const packages = [];
@@ -52639,7 +52589,6 @@ function parsePnpmArgs(args) {
52639
52589
  packages,
52640
52590
  rawArgs: filtered,
52641
52591
  dgForce,
52642
- dgForceReason,
52643
52592
  dgNoScripts,
52644
52593
  shouldScan
52645
52594
  };
@@ -52718,7 +52667,7 @@ function yarnFlagTakesValue(flag) {
52718
52667
  return valueFlagPrefixes.includes(flag);
52719
52668
  }
52720
52669
  function parseYarnArgs(args) {
52721
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52670
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
52722
52671
  const command = filtered[0] ?? "";
52723
52672
  const shouldScan = INSTALL_COMMANDS5.has(command);
52724
52673
  const packages = [];
@@ -52737,7 +52686,6 @@ function parseYarnArgs(args) {
52737
52686
  packages,
52738
52687
  rawArgs: filtered,
52739
52688
  dgForce,
52740
- dgForceReason,
52741
52689
  dgNoScripts,
52742
52690
  shouldScan
52743
52691
  };
@@ -52817,7 +52765,7 @@ function bunFlagTakesValue(flag) {
52817
52765
  return valueFlagPrefixes.includes(flag);
52818
52766
  }
52819
52767
  function parseBunArgs(args) {
52820
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52768
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
52821
52769
  const command = filtered[0] ?? "";
52822
52770
  const shouldScan = INSTALL_COMMANDS6.has(command);
52823
52771
  const packages = [];
@@ -52836,7 +52784,6 @@ function parseBunArgs(args) {
52836
52784
  packages,
52837
52785
  rawArgs: filtered,
52838
52786
  dgForce,
52839
- dgForceReason,
52840
52787
  dgNoScripts,
52841
52788
  shouldScan
52842
52789
  };
@@ -52923,7 +52870,7 @@ function uvFlagTakesValue(flag) {
52923
52870
  return valueFlagPrefixes.includes(flag);
52924
52871
  }
52925
52872
  function parseUvArgs(args) {
52926
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52873
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
52927
52874
  const first = filtered[0] ?? "";
52928
52875
  let command = "";
52929
52876
  let argStart = 1;
@@ -52972,7 +52919,6 @@ function parseUvArgs(args) {
52972
52919
  packages,
52973
52920
  rawArgs: filtered,
52974
52921
  dgForce,
52975
- dgForceReason,
52976
52922
  dgNoScripts,
52977
52923
  shouldScan
52978
52924
  };
@@ -53034,7 +52980,7 @@ function pipxFlagTakesValue(flag) {
53034
52980
  return valueFlagPrefixes.includes(flag);
53035
52981
  }
53036
52982
  function parsePipxArgs(args) {
53037
- const { filtered, dgForce, dgForceReason, dgNoScripts } = stripDgFlags(args);
52983
+ const { filtered, dgForce, dgNoScripts } = stripDgFlags(args);
53038
52984
  const command = filtered[0] ?? "";
53039
52985
  const shouldScan = INSTALL_COMMANDS7.has(command) && command !== "upgrade-all";
53040
52986
  const packages = [];
@@ -53057,7 +53003,6 @@ function parsePipxArgs(args) {
53057
53003
  packages,
53058
53004
  rawArgs: filtered,
53059
53005
  dgForce,
53060
- dgForceReason,
53061
53006
  dgNoScripts,
53062
53007
  shouldScan
53063
53008
  };
@@ -53111,7 +53056,7 @@ var wrapper_factory_exports = {};
53111
53056
  __export(wrapper_factory_exports, {
53112
53057
  runEcosystemWrapper: () => runEcosystemWrapper
53113
53058
  });
53114
- import { spawn as spawn6 } from "node:child_process";
53059
+ import { spawn as spawn7 } from "node:child_process";
53115
53060
  function shimSentinelEnv3() {
53116
53061
  return childInstallEnv();
53117
53062
  }
@@ -53123,7 +53068,7 @@ async function spawnRealBinary(adapter7, args) {
53123
53068
  return 127;
53124
53069
  }
53125
53070
  return await new Promise((resolve3) => {
53126
- const child = spawn6(real, args, { stdio: "inherit", env: shimSentinelEnv3() });
53071
+ const child = spawn7(real, args, { stdio: "inherit", env: shimSentinelEnv3() });
53127
53072
  child.on("close", (code) => resolve3(code ?? 1));
53128
53073
  child.on("error", () => resolve3(1));
53129
53074
  });
@@ -53325,7 +53270,6 @@ async function runEcosystemWrapper(adapter7, args, config) {
53325
53270
  result,
53326
53271
  resolved,
53327
53272
  suppressedCount,
53328
- dgForceReason: parsed.dgForceReason,
53329
53273
  error: verdict === "analysis_incomplete" && decision.outcome === "refuse" ? { code: "analysis_incomplete", message: "scanner could not fully analyze the install set" } : void 0
53330
53274
  };
53331
53275
  const presenter = {
@@ -53377,10 +53321,9 @@ ${installLine}
53377
53321
  const blockedList = resolved.map((p) => `${p.name} ${p.version}`).join(", ");
53378
53322
  process.stderr.write(
53379
53323
  ` ${import_chalk19.default.red("\u2717")} ${import_chalk19.default.bold("DG blocked")} ${blockedList}.
53380
- Real install was NOT run. To override:
53381
- ` + import_chalk19.default.dim(` ${adapter7.realBinaryName} ${parsed.rawArgs.join(" ")} --dg-force --dg-force-reason="<reason>"
53324
+ ${import_chalk19.default.dim(`install anyway: ${adapter7.realBinaryName} ${parsed.rawArgs.join(" ")} --dg-force`)}
53382
53325
 
53383
- `)
53326
+ `
53384
53327
  );
53385
53328
  }
53386
53329
  return;
@@ -53390,10 +53333,10 @@ ${installLine}
53390
53333
  if (d.outcome === "refuse") {
53391
53334
  if (!config.json) {
53392
53335
  process.stderr.write(
53393
- ` ${import_chalk19.default.red("\u2717")} ${import_chalk19.default.bold("DG could not verify")} ${list} \u2014 analysis incomplete.
53394
- ` + import_chalk19.default.dim(` --mode ${config.strict ? "strict" : "block"} \u21D2 refusing install (unverified is not safe). Use --mode warn or --dg-force to bypass.
53336
+ ` ${import_chalk19.default.cyan("?")} ${import_chalk19.default.bold("DG couldn't verify")} ${list} \u2014 unverified, not safe.
53337
+ ${import_chalk19.default.dim(`install anyway: ${adapter7.realBinaryName} ${parsed.rawArgs.join(" ")} --dg-force`)}
53395
53338
 
53396
- `)
53339
+ `
53397
53340
  );
53398
53341
  }
53399
53342
  } else if (!config.json && !config.quiet) {
@@ -53425,8 +53368,7 @@ ${installLine}
53425
53368
  ecosystem: adapter7.apiKind,
53426
53369
  packages: result.packages,
53427
53370
  decisionAction: kind,
53428
- bypassed: kind === "override",
53429
- bypassReason: kind === "override" ? parsed.dgForceReason : void 0
53371
+ bypassed: kind === "override"
53430
53372
  });
53431
53373
  } catch {
53432
53374
  }
@@ -53713,8 +53655,6 @@ async function main() {
53713
53655
  }
53714
53656
  if (result.action === "block") {
53715
53657
  try {
53716
- const reasonIdx = args.indexOf("--dg-force-reason");
53717
- const bypassReason = reasonIdx >= 0 ? args[reasonIdx + 1] : void 0;
53718
53658
  const { dispatchPublishCheckAudit: dispatchPublishCheckAudit2 } = await Promise.resolve().then(() => (init_audit_dispatcher(), audit_dispatcher_exports));
53719
53659
  const at = result.artifact.lastIndexOf("@");
53720
53660
  const pkgName = at > 0 ? result.artifact.slice(0, at) : result.artifact;
@@ -53725,8 +53665,7 @@ async function main() {
53725
53665
  packageVersion: pkgVersion,
53726
53666
  score: result.score,
53727
53667
  decisionAction: force ? "override" : "block",
53728
- bypassed: !!force,
53729
- bypassReason
53668
+ bypassed: !!force
53730
53669
  });
53731
53670
  } catch {
53732
53671
  }
@@ -53898,6 +53837,12 @@ async function main() {
53898
53837
  return;
53899
53838
  }
53900
53839
  }
53840
+ async function runHandoff(handoff) {
53841
+ if (!handoff.install) {
53842
+ return typeof process.exitCode === "number" ? process.exitCode : 0;
53843
+ }
53844
+ return handoff.install.run(handoff.install.args);
53845
+ }
53901
53846
  async function handleWrapInternal(argv) {
53902
53847
  let ecosystem = argv[0];
53903
53848
  if (!ecosystem) {
@@ -53934,9 +53879,9 @@ async function handleWrapInternal(argv) {
53934
53879
  const { isReadonlySubcommand: isReadonlySubcommand2 } = await Promise.resolve().then(() => (init_readonly(), readonly_exports));
53935
53880
  const ecoTyped = ecosystem;
53936
53881
  const spawnReal = async (real, args) => {
53937
- const { spawn: spawn7 } = await import("node:child_process");
53882
+ const { spawn: spawn8 } = await import("node:child_process");
53938
53883
  const { childInstallEnv: childInstallEnv2 } = await Promise.resolve().then(() => (init_install(), install_exports));
53939
- const child = spawn7(real, args, { stdio: "inherit", env: childInstallEnv2() });
53884
+ const child = spawn8(real, args, { stdio: "inherit", env: childInstallEnv2() });
53940
53885
  return await new Promise((resolve3) => {
53941
53886
  child.on("close", (code) => resolve3(code ?? 1));
53942
53887
  child.on("error", () => resolve3(1));
@@ -53963,11 +53908,12 @@ async function handleWrapInternal(argv) {
53963
53908
  const { render: render2 } = await init_build2().then(() => build_exports);
53964
53909
  const React22 = await Promise.resolve().then(() => __toESM(require_react(), 1));
53965
53910
  const { WrapperApp: WrapperApp2 } = await init_WrapperApp().then(() => WrapperApp_exports);
53911
+ const handoff = { install: null };
53966
53912
  const { waitUntilExit } = render2(
53967
- React22.createElement(WrapperApp2, { config, args: wrapperArgs, ecosystem: "npm" })
53913
+ React22.createElement(WrapperApp2, { config, args: wrapperArgs, ecosystem: "npm", handoff })
53968
53914
  );
53969
53915
  await waitUntilExit();
53970
- return 0;
53916
+ return runHandoff(handoff);
53971
53917
  }
53972
53918
  if (ecosystem === "pip" || ecosystem === "pip3") {
53973
53919
  if (config.json || !process.stdout.isTTY) {
@@ -53978,11 +53924,12 @@ async function handleWrapInternal(argv) {
53978
53924
  const { render: render2 } = await init_build2().then(() => build_exports);
53979
53925
  const React22 = await Promise.resolve().then(() => __toESM(require_react(), 1));
53980
53926
  const { WrapperApp: WrapperApp2 } = await init_WrapperApp().then(() => WrapperApp_exports);
53927
+ const handoff = { install: null };
53981
53928
  const { waitUntilExit } = render2(
53982
- React22.createElement(WrapperApp2, { config, args: wrapperArgs, ecosystem: "pypi" })
53929
+ React22.createElement(WrapperApp2, { config, args: wrapperArgs, ecosystem: "pypi", handoff })
53983
53930
  );
53984
53931
  await waitUntilExit();
53985
- return 0;
53932
+ return runHandoff(handoff);
53986
53933
  }
53987
53934
  if (ecosystem === "conda" || ecosystem === "mamba") {
53988
53935
  const { runStaticConda: runStaticConda2 } = await Promise.resolve().then(() => (init_conda_wrapper(), conda_wrapper_exports));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@westbayberry/dg",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Supply chain security scanner for npm and Python dependencies",
6
6
  "bin": {