@westbayberry/dg 1.0.34 → 1.0.37

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 (3) hide show
  1. package/README.md +2 -9
  2. package/dist/index.mjs +1177 -541
  3. package/package.json +3 -2
package/dist/index.mjs CHANGED
@@ -38,6 +38,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
39
39
  mod
40
40
  ));
41
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
42
 
42
43
  // src/auth.ts
43
44
  var auth_exports = {};
@@ -225,7 +226,7 @@ function loadDgrc() {
225
226
  function validateApiUrl(url) {
226
227
  try {
227
228
  const parsed = new URL(url);
228
- const isLocal = parsed.hostname === "localhost" || parsed.hostname.startsWith("127.");
229
+ const isLocal = parsed.hostname === "localhost" || parsed.hostname.startsWith("127.") || parsed.hostname.startsWith("100.") || parsed.hostname.startsWith("192.168.");
229
230
  if (parsed.protocol !== "https:" && !isLocal) {
230
231
  process.stderr.write(`Error: API URL must use HTTPS (got ${parsed.protocol}). Use localhost for local testing.
231
232
  `);
@@ -249,28 +250,46 @@ function getVersion() {
249
250
  return "1.0.0";
250
251
  }
251
252
  }
252
- function parseConfig(argv) {
253
- const { values, positionals } = parseArgs({
254
- args: argv.slice(2),
255
- options: {
256
- "api-url": { type: "string" },
257
- mode: { type: "string" },
258
- "block-threshold": { type: "string" },
259
- "warn-threshold": { type: "string" },
260
- "max-packages": { type: "string" },
261
- allowlist: { type: "string" },
262
- json: { type: "boolean", default: false },
263
- "scan-all": { type: "boolean", default: false },
264
- "base-lockfile": { type: "string" },
265
- workspace: { type: "string", short: "w" },
266
- debug: { type: "boolean", default: false },
267
- "no-config": { type: "boolean", default: false },
268
- help: { type: "boolean", default: false },
269
- version: { type: "boolean", default: false }
270
- },
271
- allowPositionals: true,
272
- strict: false
273
- });
253
+ function parseConfig(argv, strictFlags = true) {
254
+ let values;
255
+ let positionals;
256
+ try {
257
+ const parsed = parseArgs({
258
+ args: argv.slice(2),
259
+ options: {
260
+ "api-url": { type: "string" },
261
+ mode: { type: "string" },
262
+ "max-packages": { type: "string" },
263
+ json: { type: "boolean", default: false },
264
+ "scan-all": { type: "boolean", default: false },
265
+ "base-lockfile": { type: "string" },
266
+ workspace: { type: "string", short: "w" },
267
+ debug: { type: "boolean", default: false },
268
+ help: { type: "boolean", default: false },
269
+ version: { type: "boolean", default: false },
270
+ // Recognized but handled server-side via policy dashboard
271
+ "no-config": { type: "boolean", default: false },
272
+ allowlist: { type: "string" },
273
+ "block-threshold": { type: "string" },
274
+ "warn-threshold": { type: "string" }
275
+ },
276
+ allowPositionals: true,
277
+ strict: strictFlags
278
+ });
279
+ values = parsed.values;
280
+ positionals = parsed.positionals;
281
+ } catch (err) {
282
+ const raw = err instanceof Error ? err.message : String(err);
283
+ const match = raw.match(/Unknown option '([^']+)'/);
284
+ const msg = match ? `Unknown option '${match[1]}'.` : raw;
285
+ process.stderr.write(`
286
+ Error: ${msg}
287
+ `);
288
+ process.stderr.write(` Run 'dg --help' for available options.
289
+
290
+ `);
291
+ process.exit(1);
292
+ }
274
293
  if (values.help) {
275
294
  process.stdout.write(USAGE);
276
295
  process.exit(0);
@@ -281,8 +300,7 @@ function parseConfig(argv) {
281
300
  process.exit(0);
282
301
  }
283
302
  const command = positionals[0] ?? "scan";
284
- const noConfig = values["no-config"];
285
- const dgrc = noConfig ? {} : loadDgrc();
303
+ const dgrc = loadDgrc();
286
304
  const apiKey = dgrc.apiKey && typeof dgrc.apiKey === "string" && dgrc.apiKey.startsWith("dg_live_") ? dgrc.apiKey : null;
287
305
  const deviceId = getOrCreateDeviceId();
288
306
  const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
@@ -293,19 +311,8 @@ function parseConfig(argv) {
293
311
  );
294
312
  process.exit(1);
295
313
  }
296
- const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
297
- const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
298
- const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
299
314
  const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
300
315
  const debug = values.debug || process.env.DG_DEBUG === "1";
301
- if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
302
- process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
303
- process.exit(1);
304
- }
305
- if (isNaN(warnThreshold) || warnThreshold < 0 || warnThreshold > 100) {
306
- process.stderr.write("Error: --warn-threshold must be a number between 0 and 100\n");
307
- process.exit(1);
308
- }
309
316
  if (isNaN(maxPackages) || maxPackages < 1 || maxPackages > 1e4) {
310
317
  process.stderr.write("Error: --max-packages must be a number between 1 and 10000\n");
311
318
  process.exit(1);
@@ -317,10 +324,7 @@ function parseConfig(argv) {
317
324
  values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com"
318
325
  ),
319
326
  mode: modeRaw,
320
- blockThreshold,
321
- warnThreshold,
322
327
  maxPackages,
323
- allowlist: (allowlistRaw ? allowlistRaw.split(",").map((s) => s.trim()).filter(Boolean) : dgrc.allowlist ?? []).map((s) => s.toLowerCase()),
324
328
  json: values.json,
325
329
  scanAll: values["scan-all"],
326
330
  baseLockfile: values["base-lockfile"] ?? null,
@@ -341,11 +345,13 @@ var init_config = __esm({
341
345
  dependency-guardian scan [options]
342
346
  dg scan [options]
343
347
  dg npm install <pkg> [npm-flags]
348
+ dg pip install <pkg> [pip-flags]
344
349
  dg wrap
345
350
 
346
351
  Commands:
347
352
  scan Scan dependencies (auto-discovers npm + Python projects)
348
353
  npm Wrap npm commands \u2014 scans packages before installing
354
+ pip Wrap pip commands \u2014 scans packages before installing
349
355
  hook install Install git pre-commit hook to scan lockfile changes
350
356
  hook uninstall Remove the pre-commit hook
351
357
  update Check for and install the latest version
@@ -356,16 +362,12 @@ var init_config = __esm({
356
362
  Options:
357
363
  --api-url <url> API base URL (default: https://api.westbayberry.com)
358
364
  --mode <mode> block | warn | off (default: warn)
359
- --block-threshold <n> Score threshold for blocking (default: 70)
360
- --warn-threshold <n> Score threshold for warnings (default: 60)
361
365
  --max-packages <n> Max packages per scan (default: 200)
362
- --allowlist <pkgs> Comma-separated package names to skip
363
366
  --json Output JSON for CI parsing
364
367
  --scan-all Scan all packages, not just changed
365
368
  --base-lockfile <path> Path to base lockfile for explicit diff
366
369
  --workspace <dir> Scan a specific workspace subdirectory
367
370
  --debug Show diagnostic output (discovery, batches, timing)
368
- --no-config Skip loading .dgrc.json config file
369
371
  --help Show this help message
370
372
  --version Show version number
371
373
 
@@ -376,7 +378,6 @@ var init_config = __esm({
376
378
  Environment Variables:
377
379
  DG_API_URL API base URL
378
380
  DG_MODE Mode (block/warn/off)
379
- DG_ALLOWLIST Comma-separated allowlist
380
381
  DG_DEBUG Enable debug output (set to 1)
381
382
  DG_WORKSPACE Workspace subdirectory to scan
382
383
 
@@ -3469,15 +3470,15 @@ var require_react_development = __commonJS({
3469
3470
  var dispatcher = resolveDispatcher();
3470
3471
  return dispatcher.useState(initialState);
3471
3472
  }
3472
- function useReducer5(reducer4, initialArg, init) {
3473
+ function useReducer6(reducer5, initialArg, init) {
3473
3474
  var dispatcher = resolveDispatcher();
3474
- return dispatcher.useReducer(reducer4, initialArg, init);
3475
+ return dispatcher.useReducer(reducer5, initialArg, init);
3475
3476
  }
3476
- function useRef8(initialValue) {
3477
+ function useRef9(initialValue) {
3477
3478
  var dispatcher = resolveDispatcher();
3478
3479
  return dispatcher.useRef(initialValue);
3479
3480
  }
3480
- function useEffect14(create2, deps) {
3481
+ function useEffect16(create2, deps) {
3481
3482
  var dispatcher = resolveDispatcher();
3482
3483
  return dispatcher.useEffect(create2, deps);
3483
3484
  }
@@ -3489,7 +3490,7 @@ var require_react_development = __commonJS({
3489
3490
  var dispatcher = resolveDispatcher();
3490
3491
  return dispatcher.useLayoutEffect(create2, deps);
3491
3492
  }
3492
- function useCallback5(callback, deps) {
3493
+ function useCallback6(callback, deps) {
3493
3494
  var dispatcher = resolveDispatcher();
3494
3495
  return dispatcher.useCallback(callback, deps);
3495
3496
  }
@@ -4256,18 +4257,18 @@ var require_react_development = __commonJS({
4256
4257
  exports.memo = memo;
4257
4258
  exports.startTransition = startTransition;
4258
4259
  exports.unstable_act = act;
4259
- exports.useCallback = useCallback5;
4260
+ exports.useCallback = useCallback6;
4260
4261
  exports.useContext = useContext7;
4261
4262
  exports.useDebugValue = useDebugValue;
4262
4263
  exports.useDeferredValue = useDeferredValue;
4263
- exports.useEffect = useEffect14;
4264
+ exports.useEffect = useEffect16;
4264
4265
  exports.useId = useId;
4265
4266
  exports.useImperativeHandle = useImperativeHandle;
4266
4267
  exports.useInsertionEffect = useInsertionEffect;
4267
4268
  exports.useLayoutEffect = useLayoutEffect2;
4268
4269
  exports.useMemo = useMemo4;
4269
- exports.useReducer = useReducer5;
4270
- exports.useRef = useRef8;
4270
+ exports.useReducer = useReducer6;
4271
+ exports.useRef = useRef9;
4271
4272
  exports.useState = useState9;
4272
4273
  exports.useSyncExternalStore = useSyncExternalStore;
4273
4274
  exports.useTransition = useTransition;
@@ -11978,9 +11979,9 @@ var require_react_reconciler_development = __commonJS({
11978
11979
  module.exports = function $$$reconciler($$$hostConfig) {
11979
11980
  var exports2 = {};
11980
11981
  "use strict";
11981
- var React17 = require_react();
11982
+ var React18 = require_react();
11982
11983
  var Scheduler = require_scheduler();
11983
- var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
11984
+ var ReactSharedInternals = React18.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
11984
11985
  var suppressWarning = false;
11985
11986
  function setSuppressWarning(newSuppressWarning) {
11986
11987
  {
@@ -12044,7 +12045,7 @@ var require_react_reconciler_development = __commonJS({
12044
12045
  var HostPortal = 4;
12045
12046
  var HostComponent = 5;
12046
12047
  var HostText = 6;
12047
- var Fragment4 = 7;
12048
+ var Fragment7 = 7;
12048
12049
  var Mode = 8;
12049
12050
  var ContextConsumer = 9;
12050
12051
  var ContextProvider = 10;
@@ -12184,7 +12185,7 @@ var require_react_reconciler_development = __commonJS({
12184
12185
  return "DehydratedFragment";
12185
12186
  case ForwardRef:
12186
12187
  return getWrappedName$1(type, type.render, "ForwardRef");
12187
- case Fragment4:
12188
+ case Fragment7:
12188
12189
  return "Fragment";
12189
12190
  case HostComponent:
12190
12191
  return type;
@@ -15318,7 +15319,7 @@ var require_react_reconciler_development = __commonJS({
15318
15319
  }
15319
15320
  }
15320
15321
  function updateFragment2(returnFiber, current2, fragment, lanes, key) {
15321
- if (current2 === null || current2.tag !== Fragment4) {
15322
+ if (current2 === null || current2.tag !== Fragment7) {
15322
15323
  var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);
15323
15324
  created.return = returnFiber;
15324
15325
  return created;
@@ -15721,7 +15722,7 @@ var require_react_reconciler_development = __commonJS({
15721
15722
  if (child.key === key) {
15722
15723
  var elementType = element.type;
15723
15724
  if (elementType === REACT_FRAGMENT_TYPE) {
15724
- if (child.tag === Fragment4) {
15725
+ if (child.tag === Fragment7) {
15725
15726
  deleteRemainingChildren(returnFiber, child.sibling);
15726
15727
  var existing = useFiber(child, element.props.children);
15727
15728
  existing.return = returnFiber;
@@ -16969,7 +16970,7 @@ var require_react_reconciler_development = __commonJS({
16969
16970
  function basicStateReducer(state, action) {
16970
16971
  return typeof action === "function" ? action(state) : action;
16971
16972
  }
16972
- function mountReducer(reducer4, initialArg, init) {
16973
+ function mountReducer(reducer5, initialArg, init) {
16973
16974
  var hook = mountWorkInProgressHook();
16974
16975
  var initialState;
16975
16976
  if (init !== void 0) {
@@ -16983,20 +16984,20 @@ var require_react_reconciler_development = __commonJS({
16983
16984
  interleaved: null,
16984
16985
  lanes: NoLanes,
16985
16986
  dispatch: null,
16986
- lastRenderedReducer: reducer4,
16987
+ lastRenderedReducer: reducer5,
16987
16988
  lastRenderedState: initialState
16988
16989
  };
16989
16990
  hook.queue = queue;
16990
16991
  var dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber$1, queue);
16991
16992
  return [hook.memoizedState, dispatch];
16992
16993
  }
16993
- function updateReducer(reducer4, initialArg, init) {
16994
+ function updateReducer(reducer5, initialArg, init) {
16994
16995
  var hook = updateWorkInProgressHook();
16995
16996
  var queue = hook.queue;
16996
16997
  if (queue === null) {
16997
16998
  throw new Error("Should have a queue. This is likely a bug in React. Please file an issue.");
16998
16999
  }
16999
- queue.lastRenderedReducer = reducer4;
17000
+ queue.lastRenderedReducer = reducer5;
17000
17001
  var current2 = currentHook;
17001
17002
  var baseQueue = current2.baseQueue;
17002
17003
  var pendingQueue = queue.pending;
@@ -17058,7 +17059,7 @@ var require_react_reconciler_development = __commonJS({
17058
17059
  newState = update.eagerState;
17059
17060
  } else {
17060
17061
  var action = update.action;
17061
- newState = reducer4(newState, action);
17062
+ newState = reducer5(newState, action);
17062
17063
  }
17063
17064
  }
17064
17065
  update = update.next;
@@ -17091,13 +17092,13 @@ var require_react_reconciler_development = __commonJS({
17091
17092
  var dispatch = queue.dispatch;
17092
17093
  return [hook.memoizedState, dispatch];
17093
17094
  }
17094
- function rerenderReducer(reducer4, initialArg, init) {
17095
+ function rerenderReducer(reducer5, initialArg, init) {
17095
17096
  var hook = updateWorkInProgressHook();
17096
17097
  var queue = hook.queue;
17097
17098
  if (queue === null) {
17098
17099
  throw new Error("Should have a queue. This is likely a bug in React. Please file an issue.");
17099
17100
  }
17100
- queue.lastRenderedReducer = reducer4;
17101
+ queue.lastRenderedReducer = reducer5;
17101
17102
  var dispatch = queue.dispatch;
17102
17103
  var lastRenderPhaseUpdate = queue.pending;
17103
17104
  var newState = hook.memoizedState;
@@ -17107,7 +17108,7 @@ var require_react_reconciler_development = __commonJS({
17107
17108
  var update = firstRenderPhaseUpdate;
17108
17109
  do {
17109
17110
  var action = update.action;
17110
- newState = reducer4(newState, action);
17111
+ newState = reducer5(newState, action);
17111
17112
  update = update.next;
17112
17113
  } while (update !== firstRenderPhaseUpdate);
17113
17114
  if (!objectIs(newState, hook.memoizedState)) {
@@ -17787,13 +17788,13 @@ var require_react_reconciler_development = __commonJS({
17787
17788
  ReactCurrentDispatcher$1.current = prevDispatcher;
17788
17789
  }
17789
17790
  },
17790
- useReducer: function(reducer4, initialArg, init) {
17791
+ useReducer: function(reducer5, initialArg, init) {
17791
17792
  currentHookNameInDev = "useReducer";
17792
17793
  mountHookTypesDev();
17793
17794
  var prevDispatcher = ReactCurrentDispatcher$1.current;
17794
17795
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
17795
17796
  try {
17796
- return mountReducer(reducer4, initialArg, init);
17797
+ return mountReducer(reducer5, initialArg, init);
17797
17798
  } finally {
17798
17799
  ReactCurrentDispatcher$1.current = prevDispatcher;
17799
17800
  }
@@ -17891,13 +17892,13 @@ var require_react_reconciler_development = __commonJS({
17891
17892
  ReactCurrentDispatcher$1.current = prevDispatcher;
17892
17893
  }
17893
17894
  },
17894
- useReducer: function(reducer4, initialArg, init) {
17895
+ useReducer: function(reducer5, initialArg, init) {
17895
17896
  currentHookNameInDev = "useReducer";
17896
17897
  updateHookTypesDev();
17897
17898
  var prevDispatcher = ReactCurrentDispatcher$1.current;
17898
17899
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
17899
17900
  try {
17900
- return mountReducer(reducer4, initialArg, init);
17901
+ return mountReducer(reducer5, initialArg, init);
17901
17902
  } finally {
17902
17903
  ReactCurrentDispatcher$1.current = prevDispatcher;
17903
17904
  }
@@ -17995,13 +17996,13 @@ var require_react_reconciler_development = __commonJS({
17995
17996
  ReactCurrentDispatcher$1.current = prevDispatcher;
17996
17997
  }
17997
17998
  },
17998
- useReducer: function(reducer4, initialArg, init) {
17999
+ useReducer: function(reducer5, initialArg, init) {
17999
18000
  currentHookNameInDev = "useReducer";
18000
18001
  updateHookTypesDev();
18001
18002
  var prevDispatcher = ReactCurrentDispatcher$1.current;
18002
18003
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
18003
18004
  try {
18004
- return updateReducer(reducer4, initialArg, init);
18005
+ return updateReducer(reducer5, initialArg, init);
18005
18006
  } finally {
18006
18007
  ReactCurrentDispatcher$1.current = prevDispatcher;
18007
18008
  }
@@ -18099,13 +18100,13 @@ var require_react_reconciler_development = __commonJS({
18099
18100
  ReactCurrentDispatcher$1.current = prevDispatcher;
18100
18101
  }
18101
18102
  },
18102
- useReducer: function(reducer4, initialArg, init) {
18103
+ useReducer: function(reducer5, initialArg, init) {
18103
18104
  currentHookNameInDev = "useReducer";
18104
18105
  updateHookTypesDev();
18105
18106
  var prevDispatcher = ReactCurrentDispatcher$1.current;
18106
18107
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
18107
18108
  try {
18108
- return rerenderReducer(reducer4, initialArg, init);
18109
+ return rerenderReducer(reducer5, initialArg, init);
18109
18110
  } finally {
18110
18111
  ReactCurrentDispatcher$1.current = prevDispatcher;
18111
18112
  }
@@ -18211,14 +18212,14 @@ var require_react_reconciler_development = __commonJS({
18211
18212
  ReactCurrentDispatcher$1.current = prevDispatcher;
18212
18213
  }
18213
18214
  },
18214
- useReducer: function(reducer4, initialArg, init) {
18215
+ useReducer: function(reducer5, initialArg, init) {
18215
18216
  currentHookNameInDev = "useReducer";
18216
18217
  warnInvalidHookAccess();
18217
18218
  mountHookTypesDev();
18218
18219
  var prevDispatcher = ReactCurrentDispatcher$1.current;
18219
18220
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
18220
18221
  try {
18221
- return mountReducer(reducer4, initialArg, init);
18222
+ return mountReducer(reducer5, initialArg, init);
18222
18223
  } finally {
18223
18224
  ReactCurrentDispatcher$1.current = prevDispatcher;
18224
18225
  }
@@ -18332,14 +18333,14 @@ var require_react_reconciler_development = __commonJS({
18332
18333
  ReactCurrentDispatcher$1.current = prevDispatcher;
18333
18334
  }
18334
18335
  },
18335
- useReducer: function(reducer4, initialArg, init) {
18336
+ useReducer: function(reducer5, initialArg, init) {
18336
18337
  currentHookNameInDev = "useReducer";
18337
18338
  warnInvalidHookAccess();
18338
18339
  updateHookTypesDev();
18339
18340
  var prevDispatcher = ReactCurrentDispatcher$1.current;
18340
18341
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
18341
18342
  try {
18342
- return updateReducer(reducer4, initialArg, init);
18343
+ return updateReducer(reducer5, initialArg, init);
18343
18344
  } finally {
18344
18345
  ReactCurrentDispatcher$1.current = prevDispatcher;
18345
18346
  }
@@ -18453,14 +18454,14 @@ var require_react_reconciler_development = __commonJS({
18453
18454
  ReactCurrentDispatcher$1.current = prevDispatcher;
18454
18455
  }
18455
18456
  },
18456
- useReducer: function(reducer4, initialArg, init) {
18457
+ useReducer: function(reducer5, initialArg, init) {
18457
18458
  currentHookNameInDev = "useReducer";
18458
18459
  warnInvalidHookAccess();
18459
18460
  updateHookTypesDev();
18460
18461
  var prevDispatcher = ReactCurrentDispatcher$1.current;
18461
18462
  ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
18462
18463
  try {
18463
- return rerenderReducer(reducer4, initialArg, init);
18464
+ return rerenderReducer(reducer5, initialArg, init);
18464
18465
  } finally {
18465
18466
  ReactCurrentDispatcher$1.current = prevDispatcher;
18466
18467
  }
@@ -21212,7 +21213,7 @@ var require_react_reconciler_development = __commonJS({
21212
21213
  var _resolvedProps2 = workInProgress2.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
21213
21214
  return updateForwardRef(current2, workInProgress2, type, _resolvedProps2, renderLanes2);
21214
21215
  }
21215
- case Fragment4:
21216
+ case Fragment7:
21216
21217
  return updateFragment(current2, workInProgress2, renderLanes2);
21217
21218
  case Mode:
21218
21219
  return updateMode(current2, workInProgress2, renderLanes2);
@@ -21649,7 +21650,7 @@ var require_react_reconciler_development = __commonJS({
21649
21650
  case SimpleMemoComponent:
21650
21651
  case FunctionComponent:
21651
21652
  case ForwardRef:
21652
- case Fragment4:
21653
+ case Fragment7:
21653
21654
  case Mode:
21654
21655
  case Profiler:
21655
21656
  case ContextConsumer:
@@ -26417,7 +26418,7 @@ var require_react_reconciler_development = __commonJS({
26417
26418
  return fiber;
26418
26419
  }
26419
26420
  function createFiberFromFragment(elements, mode, lanes, key) {
26420
- var fiber = createFiber(Fragment4, elements, key, mode);
26421
+ var fiber = createFiber(Fragment7, elements, key, mode);
26421
26422
  fiber.lanes = lanes;
26422
26423
  return fiber;
26423
26424
  }
@@ -37931,7 +37932,7 @@ var require_react_jsx_runtime_development = __commonJS({
37931
37932
  if (process.env.NODE_ENV !== "production") {
37932
37933
  (function() {
37933
37934
  "use strict";
37934
- var React17 = require_react();
37935
+ var React18 = require_react();
37935
37936
  var REACT_ELEMENT_TYPE = Symbol.for("react.element");
37936
37937
  var REACT_PORTAL_TYPE = Symbol.for("react.portal");
37937
37938
  var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
@@ -37957,7 +37958,7 @@ var require_react_jsx_runtime_development = __commonJS({
37957
37958
  }
37958
37959
  return null;
37959
37960
  }
37960
- var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
37961
+ var ReactSharedInternals = React18.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
37961
37962
  function error(format) {
37962
37963
  {
37963
37964
  {
@@ -38807,11 +38808,11 @@ var require_react_jsx_runtime_development = __commonJS({
38807
38808
  return jsxWithValidation(type, props, key, false);
38808
38809
  }
38809
38810
  }
38810
- var jsx11 = jsxWithValidationDynamic;
38811
- var jsxs13 = jsxWithValidationStatic;
38811
+ var jsx12 = jsxWithValidationDynamic;
38812
+ var jsxs14 = jsxWithValidationStatic;
38812
38813
  exports.Fragment = REACT_FRAGMENT_TYPE;
38813
- exports.jsx = jsx11;
38814
- exports.jsxs = jsxs13;
38814
+ exports.jsx = jsx12;
38815
+ exports.jsxs = jsxs14;
38815
38816
  })();
38816
38817
  }
38817
38818
  }
@@ -39030,7 +39031,7 @@ async function callAnalyzeAPI(packages, config, onProgress) {
39030
39031
  results.push(result);
39031
39032
  }
39032
39033
  if (process.env.DG_PERF) console.error(`[CLI-PERF] total: ${packages.length} packages \u2192 ${Date.now() - tTotal}ms`);
39033
- return mergeResponses(results, config);
39034
+ return mergeResponses(results);
39034
39035
  }
39035
39036
  async function callBatchWithRetry(packages, config) {
39036
39037
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
@@ -39053,10 +39054,10 @@ async function callBatchWithRetry(packages, config) {
39053
39054
  }
39054
39055
  throw new Error("Exhausted retries");
39055
39056
  }
39056
- function mergeResponses(results, config) {
39057
+ function mergeResponses(results) {
39057
39058
  const allPackages = results.flatMap((r) => r.packages);
39058
39059
  const maxScore = Math.max(0, ...allPackages.map((p) => p.score));
39059
- const action = maxScore >= config.blockThreshold ? "block" : maxScore >= config.warnThreshold ? "warn" : "pass";
39060
+ const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : "pass";
39060
39061
  const safeVersions = {};
39061
39062
  for (const r of results) {
39062
39063
  Object.assign(safeVersions, r.safeVersions);
@@ -39077,11 +39078,7 @@ async function callAnalyzeBatch(packages, config) {
39077
39078
  version: p.version,
39078
39079
  previousVersion: p.previousVersion,
39079
39080
  isNew: p.isNew
39080
- })),
39081
- config: {
39082
- blockThreshold: config.blockThreshold,
39083
- warnThreshold: config.warnThreshold
39084
- }
39081
+ }))
39085
39082
  };
39086
39083
  const controller = new AbortController();
39087
39084
  const timeoutId = setTimeout(() => controller.abort(), 18e4);
@@ -39149,7 +39146,7 @@ async function callPyPIAnalyzeAPI(packages, config, onProgress) {
39149
39146
  if (onProgress) onProgress(completed, packages.length);
39150
39147
  results.push(result);
39151
39148
  }
39152
- return mergeResponses(results, config);
39149
+ return mergeResponses(results);
39153
39150
  }
39154
39151
  async function callPyPIBatch(packages, config) {
39155
39152
  const url = `${config.apiUrl}/v1/pypi/analyze`;
@@ -39159,11 +39156,7 @@ async function callPyPIBatch(packages, config) {
39159
39156
  version: p.version,
39160
39157
  previousVersion: p.previousVersion ?? null,
39161
39158
  isNew: p.isNew ?? true
39162
- })),
39163
- config: {
39164
- blockThreshold: config.blockThreshold,
39165
- warnThreshold: config.warnThreshold
39166
- }
39159
+ }))
39167
39160
  };
39168
39161
  const controller = new AbortController();
39169
39162
  const timeoutId = setTimeout(() => controller.abort(), 18e4);
@@ -39249,13 +39242,15 @@ function parseLockfile(content) {
39249
39242
  const name = extractPackageName(path);
39250
39243
  if (name && !packages.has(name)) {
39251
39244
  const e = entry;
39245
+ const deps = e.dependencies;
39252
39246
  packages.set(name, {
39253
39247
  version: e.version ?? "",
39254
39248
  resolved: e.resolved,
39255
39249
  integrity: e.integrity,
39256
39250
  dev: e.dev,
39257
39251
  optional: e.optional,
39258
- hasPlatformRestriction: !!(e.os || e.cpu)
39252
+ hasPlatformRestriction: !!(e.os || e.cpu),
39253
+ dependencies: deps && typeof deps === "object" ? deps : void 0
39259
39254
  });
39260
39255
  }
39261
39256
  }
@@ -39493,8 +39488,13 @@ function discoverChanges(cwd2, config) {
39493
39488
  }
39494
39489
  const lockfileInfo = findLockfile(cwd2);
39495
39490
  if (!lockfileInfo) {
39491
+ const pythonFiles = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
39492
+ const hasPython = pythonFiles.some((f) => existsSync5(join5(cwd2, f)));
39493
+ if (hasPython) {
39494
+ return { packages: [], method: "fallback", skipped: [] };
39495
+ }
39496
39496
  throw new Error(
39497
- "No lockfile found (package-lock.json, yarn.lock, or pnpm-lock.yaml). Run from your project root or use --base-lockfile."
39497
+ "No lockfile found (package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Pipfile.lock, or poetry.lock). Run from your project root or use --base-lockfile."
39498
39498
  );
39499
39499
  }
39500
39500
  const headContent = readFileSafe(lockfileInfo.path);
@@ -39505,6 +39505,7 @@ function discoverChanges(cwd2, config) {
39505
39505
  for (const [name, entry] of headParsed.packages) {
39506
39506
  if (packages2.length >= config.maxPackages) break;
39507
39507
  if (entry.optional && entry.hasPlatformRestriction) continue;
39508
+ if (name === SELF_PACKAGE) continue;
39508
39509
  packages2.push({
39509
39510
  name,
39510
39511
  version: entry.version,
@@ -39522,7 +39523,7 @@ function discoverChanges(cwd2, config) {
39522
39523
  const baseParsed = parseLockfile(baseContent2);
39523
39524
  const diff2 = diffLockfiles(baseParsed, headParsed, config.maxPackages, directDeps);
39524
39525
  return {
39525
- packages: diff2.changes.map(toPackageInput),
39526
+ packages: diff2.changes.map(toPackageInput).filter((p) => p.name !== SELF_PACKAGE),
39526
39527
  method: "base-lockfile",
39527
39528
  skipped: diff2.skipped
39528
39529
  };
@@ -39532,7 +39533,7 @@ function discoverChanges(cwd2, config) {
39532
39533
  const baseParsed = parseLockfile(baseContent);
39533
39534
  const diff2 = diffLockfiles(baseParsed, headParsed, config.maxPackages, directDeps);
39534
39535
  return {
39535
- packages: diff2.changes.map(toPackageInput),
39536
+ packages: diff2.changes.map(toPackageInput).filter((p) => p.name !== SELF_PACKAGE),
39536
39537
  method: "git-diff",
39537
39538
  skipped: diff2.skipped
39538
39539
  };
@@ -39551,7 +39552,7 @@ function discoverChanges(cwd2, config) {
39551
39552
  };
39552
39553
  });
39553
39554
  return {
39554
- packages: resolved.map(toPackageInput),
39555
+ packages: resolved.map(toPackageInput).filter((p) => p.name !== SELF_PACKAGE),
39555
39556
  method: "fallback",
39556
39557
  skipped: []
39557
39558
  };
@@ -39561,6 +39562,7 @@ function discoverChanges(cwd2, config) {
39561
39562
  for (const [name, entry] of headParsed.packages) {
39562
39563
  if (packages.length >= config.maxPackages) break;
39563
39564
  if (entry.optional && entry.hasPlatformRestriction) continue;
39565
+ if (name === SELF_PACKAGE) continue;
39564
39566
  packages.push({
39565
39567
  name,
39566
39568
  version: entry.version,
@@ -39703,7 +39705,7 @@ function parsePythonDepFile(projectDir, depFile) {
39703
39705
  }
39704
39706
  return packages;
39705
39707
  }
39706
- var MAX_LOCKFILE_BYTES;
39708
+ var MAX_LOCKFILE_BYTES, SELF_PACKAGE;
39707
39709
  var init_lockfile = __esm({
39708
39710
  "src/lockfile.ts"() {
39709
39711
  "use strict";
@@ -39713,6 +39715,207 @@ var init_lockfile = __esm({
39713
39715
  init_diff2();
39714
39716
  init_parse_package_json();
39715
39717
  MAX_LOCKFILE_BYTES = 50 * 1024 * 1024;
39718
+ SELF_PACKAGE = "@westbayberry/dg";
39719
+ }
39720
+ });
39721
+
39722
+ // src/pip-wrapper.ts
39723
+ import { spawn as spawn4 } from "node:child_process";
39724
+ import { readFileSync as readFileSync7, existsSync as existsSync6 } from "node:fs";
39725
+ function parsePipArgs(args) {
39726
+ let dgForce = false;
39727
+ const filtered = [];
39728
+ for (const arg of args) {
39729
+ if (arg === "--dg-force") {
39730
+ dgForce = true;
39731
+ } else {
39732
+ filtered.push(arg);
39733
+ }
39734
+ }
39735
+ const command = filtered[0] ?? "";
39736
+ const shouldScan = INSTALL_COMMANDS2.has(command);
39737
+ const packages = [];
39738
+ let requirementsFile;
39739
+ if (shouldScan) {
39740
+ for (let i = 1; i < filtered.length; i++) {
39741
+ const arg = filtered[i];
39742
+ if (arg === "-r" || arg === "--requirement") {
39743
+ if (i + 1 < filtered.length) {
39744
+ requirementsFile = filtered[i + 1];
39745
+ i++;
39746
+ }
39747
+ continue;
39748
+ }
39749
+ if (arg.startsWith("-")) {
39750
+ if (pipFlagTakesValue(arg)) i++;
39751
+ continue;
39752
+ }
39753
+ packages.push(arg);
39754
+ }
39755
+ }
39756
+ return {
39757
+ command,
39758
+ packages,
39759
+ rawArgs: filtered,
39760
+ dgForce,
39761
+ shouldScan,
39762
+ requirementsFile
39763
+ };
39764
+ }
39765
+ function pipFlagTakesValue(flag) {
39766
+ const valueFlags = [
39767
+ "--index-url",
39768
+ "-i",
39769
+ "--extra-index-url",
39770
+ "--constraint",
39771
+ "-c",
39772
+ "--requirement",
39773
+ "-r",
39774
+ "--target",
39775
+ "-t",
39776
+ "--prefix",
39777
+ "--src",
39778
+ "--root",
39779
+ "--config-settings"
39780
+ ];
39781
+ for (const f of valueFlags) {
39782
+ if (flag === f) return true;
39783
+ }
39784
+ return false;
39785
+ }
39786
+ function parseRequirementsFile(filePath) {
39787
+ if (!existsSync6(filePath)) return [];
39788
+ try {
39789
+ const content = readFileSync7(filePath, "utf-8");
39790
+ const specs = [];
39791
+ for (const rawLine of content.split("\n")) {
39792
+ let line = rawLine.trim();
39793
+ if (!line) continue;
39794
+ if (line.startsWith("#")) continue;
39795
+ if (line.startsWith("-e") || line.startsWith("--editable")) continue;
39796
+ if (/^(git|svn|hg|bzr)\+/.test(line)) continue;
39797
+ if (line.startsWith("-r") || line.startsWith("--requirement")) continue;
39798
+ if (line.startsWith("--")) continue;
39799
+ const commentIdx = line.indexOf(" #");
39800
+ if (commentIdx !== -1) line = line.slice(0, commentIdx).trim();
39801
+ const markerIdx = line.indexOf(";");
39802
+ if (markerIdx !== -1) line = line.slice(0, markerIdx).trim();
39803
+ if (line) specs.push(line);
39804
+ }
39805
+ return specs;
39806
+ } catch {
39807
+ return [];
39808
+ }
39809
+ }
39810
+ function parsePipSpec(spec) {
39811
+ const match = spec.match(/^([a-zA-Z0-9][-a-zA-Z0-9._]*)(([><=!~]=?).+)?$/);
39812
+ if (!match) return { name: spec, versionSpec: null };
39813
+ return {
39814
+ name: match[1],
39815
+ versionSpec: match[2] ?? null
39816
+ };
39817
+ }
39818
+ async function resolvePipVersion(spec) {
39819
+ const { name, versionSpec } = parsePipSpec(spec);
39820
+ if (versionSpec?.startsWith("==")) {
39821
+ const version = versionSpec.slice(2);
39822
+ try {
39823
+ const controller = new AbortController();
39824
+ const timer = setTimeout(() => controller.abort(), 15e3);
39825
+ const resp = await fetch(`https://pypi.org/pypi/${name}/${version}/json`, {
39826
+ signal: controller.signal
39827
+ });
39828
+ clearTimeout(timer);
39829
+ if (resp.ok) return version;
39830
+ return null;
39831
+ } catch {
39832
+ return null;
39833
+ }
39834
+ }
39835
+ try {
39836
+ const controller = new AbortController();
39837
+ const timer = setTimeout(() => controller.abort(), 15e3);
39838
+ const resp = await fetch(`https://pypi.org/pypi/${name}/json`, {
39839
+ signal: controller.signal
39840
+ });
39841
+ clearTimeout(timer);
39842
+ if (!resp.ok) return null;
39843
+ const data = await resp.json();
39844
+ return data.info?.version ?? null;
39845
+ } catch {
39846
+ return null;
39847
+ }
39848
+ }
39849
+ async function resolvePackages2(specs) {
39850
+ const results = await Promise.allSettled(
39851
+ specs.map(async (spec) => {
39852
+ const { name } = parsePipSpec(spec);
39853
+ const version = await resolvePipVersion(spec);
39854
+ return { spec, name, version };
39855
+ })
39856
+ );
39857
+ const resolved = [];
39858
+ const failed = [];
39859
+ for (const result of results) {
39860
+ if (result.status === "rejected") {
39861
+ failed.push("unknown");
39862
+ continue;
39863
+ }
39864
+ const { spec, name, version } = result.value;
39865
+ if (version) {
39866
+ resolved.push({
39867
+ name,
39868
+ version,
39869
+ previousVersion: null,
39870
+ isNew: true
39871
+ });
39872
+ } else {
39873
+ failed.push(spec);
39874
+ }
39875
+ }
39876
+ return { resolved, failed };
39877
+ }
39878
+ async function detectPipBinary() {
39879
+ if (cachedPipBinary) return cachedPipBinary;
39880
+ const candidates = ["python3 -m pip", "python -m pip", "pip3", "pip"];
39881
+ for (const cmd of candidates) {
39882
+ try {
39883
+ const parts = cmd.split(" ");
39884
+ const code = await new Promise((resolve) => {
39885
+ const child = spawn4(parts[0], [...parts.slice(1), "--version"], {
39886
+ stdio: ["pipe", "pipe", "pipe"],
39887
+ timeout: 5e3
39888
+ });
39889
+ child.on("close", (c) => resolve(c ?? 1));
39890
+ child.on("error", () => resolve(1));
39891
+ });
39892
+ if (code === 0) {
39893
+ cachedPipBinary = cmd;
39894
+ return cmd;
39895
+ }
39896
+ } catch {
39897
+ }
39898
+ }
39899
+ throw new Error("pip not found. Install pip or ensure 'python -m pip' is available.");
39900
+ }
39901
+ async function runPip(args) {
39902
+ const pipCmd = await detectPipBinary();
39903
+ const parts = pipCmd.split(" ");
39904
+ return new Promise((resolve) => {
39905
+ const child = spawn4(parts[0], [...parts.slice(1), ...args], {
39906
+ stdio: "inherit",
39907
+ shell: false
39908
+ });
39909
+ child.on("close", (code) => resolve(code ?? 1));
39910
+ child.on("error", () => resolve(1));
39911
+ });
39912
+ }
39913
+ var INSTALL_COMMANDS2, cachedPipBinary;
39914
+ var init_pip_wrapper = __esm({
39915
+ "src/pip-wrapper.ts"() {
39916
+ "use strict";
39917
+ INSTALL_COMMANDS2 = /* @__PURE__ */ new Set(["install", "update"]);
39918
+ cachedPipBinary = null;
39716
39919
  }
39717
39920
  });
39718
39921
 
@@ -39721,7 +39924,7 @@ var discover_exports = {};
39721
39924
  __export(discover_exports, {
39722
39925
  discoverProjects: () => discoverProjects
39723
39926
  });
39724
- import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync, lstatSync } from "node:fs";
39927
+ import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync, lstatSync } from "node:fs";
39725
39928
  import { join as join6, relative, basename } from "node:path";
39726
39929
  function discoverProjects(root) {
39727
39930
  const projects = [];
@@ -39732,7 +39935,7 @@ function walk(dir, root, depth, out) {
39732
39935
  if (depth > MAX_DEPTH) return;
39733
39936
  for (const lockfile of NPM_LOCKFILES) {
39734
39937
  const lockPath = join6(dir, lockfile);
39735
- if (existsSync6(lockPath)) {
39938
+ if (existsSync7(lockPath)) {
39736
39939
  const count = countNpmPackages(lockPath);
39737
39940
  if (count > 0) {
39738
39941
  out.push({
@@ -39748,7 +39951,7 @@ function walk(dir, root, depth, out) {
39748
39951
  }
39749
39952
  for (const depFile of PYTHON_DEPFILES) {
39750
39953
  const depPath = join6(dir, depFile);
39751
- if (existsSync6(depPath)) {
39954
+ if (existsSync7(depPath)) {
39752
39955
  const count = countPythonPackages(depPath, depFile);
39753
39956
  if (count > 0) {
39754
39957
  out.push({
@@ -39783,7 +39986,7 @@ function walk(dir, root, depth, out) {
39783
39986
  function countNpmPackages(lockPath) {
39784
39987
  try {
39785
39988
  const name = basename(lockPath);
39786
- const content = readFileSync7(lockPath, "utf-8");
39989
+ const content = readFileSync8(lockPath, "utf-8");
39787
39990
  if (name === "yarn.lock") {
39788
39991
  return (content.match(/^\S.*:$/gm) || []).length;
39789
39992
  }
@@ -39804,7 +40007,7 @@ function countNpmPackages(lockPath) {
39804
40007
  }
39805
40008
  function countPythonPackages(depPath, depFile) {
39806
40009
  try {
39807
- const content = readFileSync7(depPath, "utf-8");
40010
+ const content = readFileSync8(depPath, "utf-8");
39808
40011
  if (depFile === "Pipfile.lock") {
39809
40012
  const parsed = JSON.parse(content);
39810
40013
  const defaultCount = Object.keys(parsed.default || {}).length;
@@ -39861,40 +40064,50 @@ __export(static_output_exports, {
39861
40064
  renderResultStatic: () => renderResultStatic,
39862
40065
  runStatic: () => runStatic,
39863
40066
  runStaticLogin: () => runStaticLogin,
39864
- runStaticNpm: () => runStaticNpm
40067
+ runStaticNpm: () => runStaticNpm,
40068
+ runStaticPip: () => runStaticPip
39865
40069
  });
39866
40070
  function printTrialBanner(result) {
39867
40071
  if (result.trialScansRemaining === void 0) return;
39868
- const remaining = result.trialScansRemaining;
39869
- if (remaining > 0) {
39870
- process.stderr.write(
39871
- import_chalk4.default.dim(` ${remaining} free scan${remaining !== 1 ? "s" : ""} remaining. `) + import_chalk4.default.dim(`Run \`dg login\` for unlimited scans.
39872
- `)
39873
- );
39874
- } else {
39875
- process.stderr.write(
39876
- import_chalk4.default.yellow(` No free scans remaining. `) + import_chalk4.default.yellow(`Run \`dg login\` to continue scanning.
40072
+ process.stderr.write(
40073
+ import_chalk4.default.dim(` Free tier \xB7 Run \`dg login\` for higher scan limits.
39877
40074
  `)
39878
- );
39879
- }
40075
+ );
39880
40076
  }
39881
- function handleTrialExhausted2(error) {
40077
+ function handleTrialExhausted2(error, jsonMode = false) {
39882
40078
  if (error instanceof TrialExhaustedError) {
39883
- process.stderr.write(
39884
- import_chalk4.default.yellow("\n Free trial scans used up.\n") + import_chalk4.default.white(" Run `dg login` to create a free account and continue scanning.\n\n")
39885
- );
40079
+ let hasKey = false;
40080
+ try {
40081
+ const { getStoredApiKey: getStoredApiKey2 } = (init_auth(), __toCommonJS(auth_exports));
40082
+ hasKey = !!getStoredApiKey2();
40083
+ } catch {
40084
+ }
40085
+ const message = hasKey ? "Your API key may be invalid or expired. Run `dg logout` then `dg login` to re-authenticate." : "Monthly scan limit reached. Run `dg login` to create a free account for higher limits.";
40086
+ if (jsonMode) {
40087
+ process.stdout.write(JSON.stringify({
40088
+ error: true,
40089
+ code: "trial_exhausted",
40090
+ message,
40091
+ scansUsed: error.scansUsed,
40092
+ maxScans: error.maxScans
40093
+ }, null, 2) + "\n");
40094
+ } else {
40095
+ if (hasKey) {
40096
+ process.stderr.write(import_chalk4.default.yellow(`
40097
+ ${message}
40098
+
40099
+ `));
40100
+ } else {
40101
+ process.stderr.write(
40102
+ import_chalk4.default.yellow("\n Monthly scan limit reached (200 scans/month on free tier).\n") + import_chalk4.default.white(" Run `dg login` for higher limits, or upgrade at westbayberry.com/pricing\n\n")
40103
+ );
40104
+ }
40105
+ }
39886
40106
  process.exit(1);
39887
40107
  return true;
39888
40108
  }
39889
40109
  return false;
39890
40110
  }
39891
- function severityColor(sev) {
39892
- if (sev >= 5) return (s) => import_chalk4.default.bold.red(s);
39893
- if (sev >= 4) return import_chalk4.default.red;
39894
- if (sev >= 3) return import_chalk4.default.yellow;
39895
- if (sev >= 2) return import_chalk4.default.cyan;
39896
- return import_chalk4.default.dim;
39897
- }
39898
40111
  function actionColor(action) {
39899
40112
  if (action === "block") return import_chalk4.default.red;
39900
40113
  if (action === "warn") return import_chalk4.default.yellow;
@@ -39917,14 +40130,22 @@ function groupPackages(packages) {
39917
40130
  }
39918
40131
  return [...map.values()].map((pkgs) => ({ packages: pkgs, key: pkgs[0].name })).sort((a, b) => b.packages[0].score - a.packages[0].score);
39919
40132
  }
39920
- function actionBadge(score, config) {
39921
- if (score >= config.blockThreshold) return { label: "BLOCK", color: import_chalk4.default.red };
39922
- if (score >= config.warnThreshold) return { label: "WARN", color: import_chalk4.default.yellow };
40133
+ function actionBadge(score) {
40134
+ if (score >= 70) return { label: "BLOCK", color: import_chalk4.default.red };
40135
+ if (score >= 60) return { label: "WARN", color: import_chalk4.default.yellow };
39923
40136
  return { label: "pass", color: import_chalk4.default.green };
39924
40137
  }
39925
40138
  function renderResultStatic(result, config) {
39926
40139
  if (config.json) {
39927
- return JSON.stringify(result, null, 2);
40140
+ const json = JSON.stringify(result, null, 2);
40141
+ const fs2 = __require("fs");
40142
+ const path = __require("path");
40143
+ const filename = `dg-scan-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.json`;
40144
+ const filepath = path.resolve(process.cwd(), filename);
40145
+ fs2.writeFileSync(filepath, json + "\n");
40146
+ process.stderr.write(import_chalk4.default.green(` Scan results saved to ${filename}
40147
+ `));
40148
+ return json;
39928
40149
  }
39929
40150
  const lines = [];
39930
40151
  const actionStr = result.action.toUpperCase();
@@ -39949,6 +40170,15 @@ function renderResultStatic(result, config) {
39949
40170
  ` ${total} package${total !== 1 ? "s" : ""} scanned ${import_chalk4.default.dim("\u2502")} ${import_chalk4.default.green("all clean")}`
39950
40171
  );
39951
40172
  }
40173
+ const licensedPkgs = result.packages.filter((p) => p.license);
40174
+ const licenseIssues = licensedPkgs.filter(
40175
+ (p) => p.license && p.license.riskCategory !== "permissive"
40176
+ );
40177
+ if (licenseIssues.length > 0) {
40178
+ lines.push(
40179
+ ` ${import_chalk4.default.dim("Licenses:")} ${import_chalk4.default.yellow(`${licenseIssues.length} need review`)} \u2502 ${import_chalk4.default.green(`${licensedPkgs.length - licenseIssues.length} permissive`)}`
40180
+ );
40181
+ }
39952
40182
  lines.push("");
39953
40183
  if (total === 0) {
39954
40184
  lines.push(" No packages to scan.");
@@ -39961,10 +40191,13 @@ function renderResultStatic(result, config) {
39961
40191
  lines.push(` ${import_chalk4.default.dim("\u2500".repeat(60))}`);
39962
40192
  for (const group of groups) {
39963
40193
  const rep = group.packages[0];
39964
- const { label, color } = actionBadge(rep.score, config);
40194
+ const { label, color } = actionBadge(rep.score);
39965
40195
  const names = group.packages.length === 1 ? rep.name : group.packages.length <= 3 ? group.packages.map((p) => p.name).join(", ") : `${group.packages[0].name} + ${group.packages.length - 1} similar`;
40196
+ const lcInfo = rep.license;
40197
+ const lcStr = lcInfo ? truncate(lcInfo.spdx ?? lcInfo.raw ?? "", 14) : "";
40198
+ const lcColor = !lcInfo ? import_chalk4.default.dim : lcInfo.riskCategory === "permissive" ? import_chalk4.default.green : lcInfo.riskCategory === "no-license" || lcInfo.riskCategory === "unlicensed" || lcInfo.riskCategory === "network-copyleft" ? import_chalk4.default.red : import_chalk4.default.yellow;
39966
40199
  lines.push(
39967
- ` ${color(pad(label, 7))}${import_chalk4.default.bold(pad(truncate(names, 50), 52))}${import_chalk4.default.dim(`score ${rep.score}`)}`
40200
+ ` ${color(pad(label, 7))}${import_chalk4.default.bold(pad(truncate(names, 36), 38))}${lcColor(pad(lcStr, 16))}${import_chalk4.default.dim(`score ${rep.score}`)}`
39968
40201
  );
39969
40202
  }
39970
40203
  lines.push("");
@@ -39975,46 +40208,6 @@ function renderResultStatic(result, config) {
39975
40208
  );
39976
40209
  lines.push("");
39977
40210
  }
39978
- for (const group of groups) {
39979
- const rep = group.packages[0];
39980
- const names = group.packages.length === 1 ? `${rep.name}@${rep.version}` : group.packages.length <= 3 ? group.packages.map((p) => `${p.name}@${p.version}`).join(", ") : `${rep.name}@${rep.version} + ${group.packages.length - 1} identical packages`;
39981
- lines.push(
39982
- ` ${import_chalk4.default.dim("\u2500\u2500")} ${import_chalk4.default.bold(`${names} (score: ${rep.score})`)}`
39983
- );
39984
- if (group.packages.length > 3) {
39985
- lines.push(
39986
- ` ${import_chalk4.default.dim("Affects: " + group.packages.map((p) => p.name).join(", "))}`
39987
- );
39988
- }
39989
- lines.push("");
39990
- const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical).sort((a, b) => b.severity - a.severity);
39991
- for (const finding of visibleFindings) {
39992
- const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
39993
- const colorFn = severityColor(finding.severity);
39994
- lines.push(
39995
- ` ${colorFn(pad(sevLabel, 10))}${finding.id} \u2014 ${finding.title}`
39996
- );
39997
- const evidenceLimit = 3;
39998
- for (let i = 0; i < Math.min(finding.evidence.length, evidenceLimit); i++) {
39999
- lines.push(
40000
- ` ${" ".repeat(10)}${import_chalk4.default.dim(truncate(finding.evidence[i], 76))}`
40001
- );
40002
- }
40003
- if (finding.evidence.length > evidenceLimit) {
40004
- lines.push(
40005
- ` ${" ".repeat(10)}${import_chalk4.default.dim(`... and ${finding.evidence.length - evidenceLimit} more`)}`
40006
- );
40007
- }
40008
- lines.push("");
40009
- }
40010
- const safeVersion = result.safeVersions[rep.name];
40011
- if (safeVersion) {
40012
- lines.push(
40013
- ` ${import_chalk4.default.green(`Safe version: ${rep.name}@${safeVersion}`)}`
40014
- );
40015
- lines.push("");
40016
- }
40017
- }
40018
40211
  if (result.durationMs) {
40019
40212
  lines.push(
40020
40213
  ` ${import_chalk4.default.dim(`Completed in ${(result.durationMs / 1e3).toFixed(1)}s`)}`
@@ -40036,7 +40229,7 @@ async function runStatic(config) {
40036
40229
  process.exit(0);
40037
40230
  }
40038
40231
  dbg(
40039
- `mode=${config.mode} block=${config.blockThreshold} warn=${config.warnThreshold} max=${config.maxPackages}`
40232
+ `mode=${config.mode} max=${config.maxPackages}`
40040
40233
  );
40041
40234
  dbg(`api=${config.apiUrl}`);
40042
40235
  process.stderr.write(import_chalk4.default.dim(" Discovering package changes...\n"));
@@ -40063,15 +40256,7 @@ async function runStatic(config) {
40063
40256
  }
40064
40257
  process.exit(0);
40065
40258
  }
40066
- const packages = discovery.packages.filter(
40067
- (p) => !config.allowlist.includes(p.name.toLowerCase())
40068
- );
40069
- if (packages.length === 0) {
40070
- process.stderr.write(
40071
- import_chalk4.default.dim(" All changed packages are allowlisted.\n")
40072
- );
40073
- process.exit(0);
40074
- }
40259
+ const packages = discovery.packages;
40075
40260
  process.stderr.write(
40076
40261
  import_chalk4.default.dim(
40077
40262
  ` Scanning ${packages.length} package${packages.length !== 1 ? "s" : ""} (${discovery.method})...
@@ -40100,7 +40285,7 @@ async function runStatic(config) {
40100
40285
  `API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`
40101
40286
  );
40102
40287
  } catch (error) {
40103
- if (handleTrialExhausted2(error)) return;
40288
+ if (handleTrialExhausted2(error, config.json)) return;
40104
40289
  throw error;
40105
40290
  }
40106
40291
  const output = renderResultStatic(result, config);
@@ -40172,24 +40357,16 @@ async function runStaticNpm(npmArgs, config) {
40172
40357
  return scanAndInstallStatic(resolved, parsed, config);
40173
40358
  }
40174
40359
  async function scanAndInstallStatic(resolved, parsed, config) {
40175
- const toScan = resolved.filter((p) => !config.allowlist.includes(p.name.toLowerCase()));
40176
- if (toScan.length === 0) {
40177
- process.stderr.write(
40178
- import_chalk4.default.dim(" All packages are allowlisted. Passing through to npm.\n")
40179
- );
40180
- const code = await runNpm(parsed.rawArgs);
40181
- process.exit(code);
40182
- }
40183
40360
  process.stderr.write(
40184
40361
  import_chalk4.default.dim(
40185
- ` Scanning ${toScan.length} package${toScan.length !== 1 ? "s" : ""}...
40362
+ ` Scanning ${resolved.length} package${resolved.length !== 1 ? "s" : ""}...
40186
40363
  `
40187
40364
  )
40188
40365
  );
40189
40366
  let result;
40190
40367
  try {
40191
40368
  const startMs = Date.now();
40192
- result = await callAnalyzeAPI(toScan, config);
40369
+ result = await callAnalyzeAPI(resolved, config);
40193
40370
  const elapsed = Date.now() - startMs;
40194
40371
  if (config.debug) {
40195
40372
  process.stderr.write(
@@ -40200,7 +40377,7 @@ async function scanAndInstallStatic(resolved, parsed, config) {
40200
40377
  );
40201
40378
  }
40202
40379
  } catch (error) {
40203
- if (handleTrialExhausted2(error)) return;
40380
+ if (handleTrialExhausted2(error, config.json)) return;
40204
40381
  const msg = error instanceof Error ? error.message : String(error);
40205
40382
  process.stderr.write(
40206
40383
  import_chalk4.default.yellow(
@@ -40215,7 +40392,7 @@ async function scanAndInstallStatic(resolved, parsed, config) {
40215
40392
  if (result.action === "pass") {
40216
40393
  process.stderr.write(
40217
40394
  import_chalk4.default.green(
40218
- ` ${import_chalk4.default.bold("\u2713")} ${toScan.length} package${toScan.length !== 1 ? "s" : ""} scanned \u2014 all clear
40395
+ ` ${import_chalk4.default.bold("\u2713")} ${resolved.length} package${resolved.length !== 1 ? "s" : ""} scanned \u2014 all clear
40219
40396
  `
40220
40397
  )
40221
40398
  );
@@ -40255,6 +40432,102 @@ async function scanAndInstallStatic(resolved, parsed, config) {
40255
40432
  process.exit(2);
40256
40433
  }
40257
40434
  }
40435
+ async function runStaticPip(pipArgs, config) {
40436
+ const parsed = parsePipArgs(pipArgs);
40437
+ if (!parsed.shouldScan) {
40438
+ const code = await runPip(parsed.rawArgs);
40439
+ process.exit(code);
40440
+ }
40441
+ let specs = parsed.packages;
40442
+ if (specs.length === 0 && parsed.requirementsFile) {
40443
+ specs = parseRequirementsFile(parsed.requirementsFile);
40444
+ }
40445
+ if (specs.length === 0) {
40446
+ process.stderr.write(
40447
+ import_chalk4.default.dim(" Dependency Guardian: no packages to scan, passing through to pip.\n")
40448
+ );
40449
+ const code = await runPip(parsed.rawArgs);
40450
+ process.exit(code);
40451
+ }
40452
+ process.stderr.write(
40453
+ import_chalk4.default.dim(
40454
+ ` Resolving ${specs.length} package${specs.length !== 1 ? "s" : ""} from PyPI...
40455
+ `
40456
+ )
40457
+ );
40458
+ const { resolved, failed } = await resolvePackages2(specs);
40459
+ if (failed.length > 0) {
40460
+ process.stderr.write(
40461
+ import_chalk4.default.yellow(` Warning: Could not resolve versions for: ${failed.join(", ")}
40462
+ `)
40463
+ );
40464
+ }
40465
+ if (resolved.length === 0) {
40466
+ process.stderr.write(
40467
+ import_chalk4.default.dim(" No packages to scan. Passing through to pip.\n")
40468
+ );
40469
+ const code = await runPip(parsed.rawArgs);
40470
+ process.exit(code);
40471
+ }
40472
+ process.stderr.write(
40473
+ import_chalk4.default.dim(
40474
+ ` Scanning ${resolved.length} Python package${resolved.length !== 1 ? "s" : ""}...
40475
+ `
40476
+ )
40477
+ );
40478
+ let result;
40479
+ try {
40480
+ result = await callPyPIAnalyzeAPI(resolved, config);
40481
+ } catch (error) {
40482
+ if (handleTrialExhausted2(error, config.json)) return;
40483
+ const msg = error instanceof Error ? error.message : String(error);
40484
+ process.stderr.write(
40485
+ import_chalk4.default.yellow(` Warning: Scan failed (${msg}). Proceeding with install.
40486
+ `)
40487
+ );
40488
+ const code = await runPip(parsed.rawArgs);
40489
+ process.exit(code);
40490
+ return;
40491
+ }
40492
+ if (result.action === "pass") {
40493
+ process.stderr.write(
40494
+ import_chalk4.default.green(
40495
+ ` ${import_chalk4.default.bold("\u2713")} ${resolved.length} package${resolved.length !== 1 ? "s" : ""} scanned \u2014 all clear
40496
+ `
40497
+ )
40498
+ );
40499
+ printTrialBanner(result);
40500
+ process.stderr.write("\n");
40501
+ const code = await runPip(parsed.rawArgs);
40502
+ process.exit(code);
40503
+ }
40504
+ const output = renderResultStatic(result, config);
40505
+ process.stdout.write(output + "\n");
40506
+ printTrialBanner(result);
40507
+ if (result.action === "warn") {
40508
+ process.stderr.write(
40509
+ import_chalk4.default.yellow(" Warnings detected. Proceeding with install.\n\n")
40510
+ );
40511
+ const code = await runPip(parsed.rawArgs);
40512
+ process.exit(code);
40513
+ }
40514
+ if (result.action === "block") {
40515
+ if (parsed.dgForce) {
40516
+ process.stderr.write(
40517
+ import_chalk4.default.yellow(import_chalk4.default.bold(" --dg-force: Bypassing block. Install at your own risk.\n\n"))
40518
+ );
40519
+ const code = await runPip(parsed.rawArgs);
40520
+ process.exit(code);
40521
+ }
40522
+ process.stderr.write(
40523
+ import_chalk4.default.red(import_chalk4.default.bold(" BLOCKED: ")) + import_chalk4.default.red("High-risk packages detected. Install aborted.\n")
40524
+ );
40525
+ process.stderr.write(
40526
+ import_chalk4.default.dim(" Use --dg-force to bypass this check.\n\n")
40527
+ );
40528
+ process.exit(2);
40529
+ }
40530
+ }
40258
40531
  async function runStaticLogin() {
40259
40532
  const {
40260
40533
  createAuthSession: createAuthSession2,
@@ -40332,7 +40605,7 @@ async function runStaticLogin() {
40332
40605
  );
40333
40606
  process.exit(1);
40334
40607
  }
40335
- var import_chalk4, SEVERITY_LABELS;
40608
+ var import_chalk4;
40336
40609
  var init_static_output = __esm({
40337
40610
  "src/static-output.ts"() {
40338
40611
  "use strict";
@@ -40340,13 +40613,7 @@ var init_static_output = __esm({
40340
40613
  init_api();
40341
40614
  init_lockfile();
40342
40615
  init_npm_wrapper();
40343
- SEVERITY_LABELS = {
40344
- 5: "CRITICAL",
40345
- 4: "HIGH",
40346
- 3: "MEDIUM",
40347
- 2: "LOW",
40348
- 1: "INFO"
40349
- };
40616
+ init_pip_wrapper();
40350
40617
  }
40351
40618
  });
40352
40619
 
@@ -40357,8 +40624,8 @@ __export(hook_exports, {
40357
40624
  });
40358
40625
  import { execFileSync as execFileSync3 } from "node:child_process";
40359
40626
  import {
40360
- existsSync as existsSync7,
40361
- readFileSync as readFileSync8,
40627
+ existsSync as existsSync8,
40628
+ readFileSync as readFileSync9,
40362
40629
  writeFileSync as writeFileSync3,
40363
40630
  mkdirSync,
40364
40631
  chmodSync as chmodSync2,
@@ -40380,8 +40647,8 @@ function installHook() {
40380
40647
  const hooksDir = join7(gitDir, "hooks");
40381
40648
  const hookPath = join7(hooksDir, "pre-commit");
40382
40649
  mkdirSync(hooksDir, { recursive: true });
40383
- if (existsSync7(hookPath)) {
40384
- const existing = readFileSync8(hookPath, "utf-8");
40650
+ if (existsSync8(hookPath)) {
40651
+ const existing = readFileSync9(hookPath, "utf-8");
40385
40652
  if (existing.includes(HOOK_MARKER)) {
40386
40653
  process.stderr.write(" Hook already installed.\n");
40387
40654
  return;
@@ -40402,11 +40669,11 @@ function installHook() {
40402
40669
  function uninstallHook() {
40403
40670
  const gitDir = findGitDir();
40404
40671
  const hookPath = join7(gitDir, "hooks", "pre-commit");
40405
- if (!existsSync7(hookPath)) {
40672
+ if (!existsSync8(hookPath)) {
40406
40673
  process.stderr.write(" No hook to remove.\n");
40407
40674
  return;
40408
40675
  }
40409
- const content = readFileSync8(hookPath, "utf-8");
40676
+ const content = readFileSync9(hookPath, "utf-8");
40410
40677
  if (!content.includes(HOOK_MARKER)) {
40411
40678
  process.stderr.write(
40412
40679
  " No Dependency Guardian hook found in pre-commit.\n"
@@ -40601,19 +40868,10 @@ function useNpmWrapper(npmArgs, config) {
40601
40868
  dispatch({ type: "DONE", exitCode: code });
40602
40869
  return;
40603
40870
  }
40604
- const toScan = resolved.filter(
40605
- (p) => !config.allowlist.includes(p.name.toLowerCase())
40606
- );
40607
- if (toScan.length === 0) {
40608
- dispatch({ type: "INSTALLING" });
40609
- const code = await runNpm(parsed.rawArgs);
40610
- dispatch({ type: "DONE", exitCode: code });
40611
- return;
40612
- }
40613
- dispatch({ type: "SCANNING", count: toScan.length });
40614
- const result = await callAnalyzeAPI(toScan, config);
40871
+ dispatch({ type: "SCANNING", count: resolved.length });
40872
+ const result = await callAnalyzeAPI(resolved, config);
40615
40873
  if (result.action === "pass") {
40616
- dispatch({ type: "PASS", count: toScan.length });
40874
+ dispatch({ type: "PASS", count: resolved.length });
40617
40875
  dispatch({ type: "INSTALLING" });
40618
40876
  const code = await runNpm(parsed.rawArgs);
40619
40877
  dispatch({ type: "DONE", exitCode: code });
@@ -40886,16 +41144,16 @@ function groupPackages2(packages) {
40886
41144
  }
40887
41145
  return [...map.values()].map((pkgs) => ({ packages: pkgs, key: pkgs[0].name })).sort((a, b) => b.packages[0].score - a.packages[0].score);
40888
41146
  }
40889
- function severityColor2(sev) {
41147
+ function severityColor(sev) {
40890
41148
  if (sev >= 5) return (s) => import_chalk6.default.bold.red(s);
40891
41149
  if (sev >= 4) return import_chalk6.default.magenta;
40892
41150
  if (sev >= 3) return import_chalk6.default.yellow;
40893
41151
  if (sev >= 2) return import_chalk6.default.dim;
40894
41152
  return import_chalk6.default.dim;
40895
41153
  }
40896
- function actionBadge2(score, config) {
40897
- if (score >= config.blockThreshold) return { label: "BLOCK", color: import_chalk6.default.red };
40898
- if (score >= config.warnThreshold) return { label: "WARN", color: import_chalk6.default.yellow };
41154
+ function actionBadge2(score) {
41155
+ if (score >= 70) return { label: "BLOCK", color: import_chalk6.default.red };
41156
+ if (score >= 60) return { label: "WARN", color: import_chalk6.default.yellow };
40899
41157
  return { label: "PASS", color: import_chalk6.default.green };
40900
41158
  }
40901
41159
  function truncate2(s, max) {
@@ -40904,7 +41162,7 @@ function truncate2(s, max) {
40904
41162
  function pad2(s, len) {
40905
41163
  return s + " ".repeat(Math.max(0, len - s.length));
40906
41164
  }
40907
- var import_chalk6, import_jsx_runtime5, SEVERITY_LABELS2, EVIDENCE_LIMIT, ResultsView;
41165
+ var import_chalk6, import_jsx_runtime5, SEVERITY_LABELS, EVIDENCE_LIMIT, ResultsView;
40908
41166
  var init_ResultsView = __esm({
40909
41167
  async "src/ui/components/ResultsView.tsx"() {
40910
41168
  "use strict";
@@ -40913,7 +41171,7 @@ var init_ResultsView = __esm({
40913
41171
  await init_ScoreHeader();
40914
41172
  await init_DurationLine();
40915
41173
  import_jsx_runtime5 = __toESM(require_jsx_runtime());
40916
- SEVERITY_LABELS2 = {
41174
+ SEVERITY_LABELS = {
40917
41175
  5: "CRITICAL",
40918
41176
  4: "HIGH",
40919
41177
  3: "MEDIUM",
@@ -40952,7 +41210,7 @@ var init_ResultsView = __esm({
40952
41210
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: "\u2500".repeat(60) }),
40953
41211
  groups.map((group) => {
40954
41212
  const rep = group.packages[0];
40955
- const { label, color } = actionBadge2(rep.score, config);
41213
+ const { label, color } = actionBadge2(rep.score);
40956
41214
  const names = group.packages.length === 1 ? rep.name : group.packages.length <= 3 ? group.packages.map((p) => p.name).join(", ") : `${group.packages[0].name} + ${group.packages.length - 1} similar`;
40957
41215
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40958
41216
  " ",
@@ -40988,8 +41246,8 @@ var init_ResultsView = __esm({
40988
41246
  ] }),
40989
41247
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
40990
41248
  visibleFindings.map((finding, idx) => {
40991
- const sevLabel = SEVERITY_LABELS2[finding.severity] ?? "INFO";
40992
- const colorFn = severityColor2(finding.severity);
41249
+ const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
41250
+ const colorFn = severityColor(finding.severity);
40993
41251
  const evidenceSlice = finding.evidence.slice(0, EVIDENCE_LIMIT);
40994
41252
  const overflow = finding.evidence.length - EVIDENCE_LIMIT;
40995
41253
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
@@ -41247,22 +41505,349 @@ var init_NpmWrapperApp = __esm({
41247
41505
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorView, { error: new Error(state.message) });
41248
41506
  case "passthrough":
41249
41507
  return null;
41250
- case "trial_exhausted":
41251
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
41508
+ case "trial_exhausted": {
41509
+ let hasKey = false;
41510
+ try {
41511
+ const { getStoredApiKey: getStoredApiKey2 } = (init_auth(), __toCommonJS(auth_exports));
41512
+ hasKey = !!getStoredApiKey2();
41513
+ } catch {
41514
+ }
41515
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexDirection: "column", paddingLeft: 2, children: hasKey ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
41516
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "yellow", bold: true, children: "Your API key may be invalid or expired." }),
41517
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
41518
+ "Run ",
41519
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "cyan", bold: true, children: "dg logout" }),
41520
+ " then ",
41521
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
41522
+ " to re-authenticate."
41523
+ ] })
41524
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
41252
41525
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
41253
41526
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
41254
41527
  "Run ",
41255
41528
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
41256
41529
  " to create a free account and continue scanning."
41257
41530
  ] })
41531
+ ] }) });
41532
+ }
41533
+ }
41534
+ };
41535
+ }
41536
+ });
41537
+
41538
+ // src/ui/hooks/usePipWrapper.ts
41539
+ function reducer3(_state, action) {
41540
+ switch (action.type) {
41541
+ case "PASSTHROUGH":
41542
+ return { phase: "passthrough" };
41543
+ case "RESOLVING":
41544
+ return { phase: "resolving", count: action.count };
41545
+ case "SCANNING":
41546
+ return { phase: "scanning", count: action.count };
41547
+ case "PASS":
41548
+ return { phase: "pass", count: action.count };
41549
+ case "WARN":
41550
+ return { phase: "warn", result: action.result };
41551
+ case "BLOCKED":
41552
+ return { phase: "blocked", result: action.result, dgForce: action.dgForce };
41553
+ case "INSTALLING":
41554
+ return { phase: "installing" };
41555
+ case "DONE":
41556
+ return { phase: "done", exitCode: action.exitCode };
41557
+ case "ERROR":
41558
+ return { phase: "error", message: action.message, proceed: action.proceed };
41559
+ case "TRIAL_EXHAUSTED":
41560
+ return { phase: "trial_exhausted" };
41561
+ }
41562
+ }
41563
+ function usePipWrapper(pipArgs, config) {
41564
+ const [state, dispatch] = (0, import_react27.useReducer)(reducer3, { phase: "resolving", count: 0 });
41565
+ const started = (0, import_react27.useRef)(false);
41566
+ const parsedRef = (0, import_react27.useRef)(parsePipArgs(pipArgs));
41567
+ const pendingInstall = (0, import_react27.useRef)(null);
41568
+ const rejectRef = (0, import_react27.useRef)(null);
41569
+ const confirmInstall = () => {
41570
+ if (pendingInstall.current) {
41571
+ pendingInstall.current();
41572
+ pendingInstall.current = null;
41573
+ rejectRef.current = null;
41574
+ }
41575
+ };
41576
+ const rejectInstall = () => {
41577
+ if (rejectRef.current) {
41578
+ rejectRef.current();
41579
+ pendingInstall.current = null;
41580
+ rejectRef.current = null;
41581
+ }
41582
+ };
41583
+ (0, import_react27.useEffect)(() => {
41584
+ if (started.current) return;
41585
+ started.current = true;
41586
+ const parsed = parsedRef.current;
41587
+ (async () => {
41588
+ try {
41589
+ if (!parsed.shouldScan) {
41590
+ dispatch({ type: "PASSTHROUGH" });
41591
+ const code = await runPip(parsed.rawArgs);
41592
+ dispatch({ type: "DONE", exitCode: code });
41593
+ return;
41594
+ }
41595
+ let specs;
41596
+ if (parsed.packages.length === 0 && parsed.requirementsFile) {
41597
+ specs = parseRequirementsFile(parsed.requirementsFile);
41598
+ if (specs.length === 0) {
41599
+ dispatch({ type: "PASSTHROUGH" });
41600
+ const code = await runPip(parsed.rawArgs);
41601
+ dispatch({ type: "DONE", exitCode: code });
41602
+ return;
41603
+ }
41604
+ } else if (parsed.packages.length === 0) {
41605
+ dispatch({ type: "PASSTHROUGH" });
41606
+ const code = await runPip(parsed.rawArgs);
41607
+ dispatch({ type: "DONE", exitCode: code });
41608
+ return;
41609
+ } else {
41610
+ specs = parsed.packages;
41611
+ }
41612
+ dispatch({ type: "RESOLVING", count: specs.length });
41613
+ const { resolved, failed } = await resolvePackages2(specs);
41614
+ if (resolved.length === 0) {
41615
+ dispatch({ type: "PASSTHROUGH" });
41616
+ const code = await runPip(parsed.rawArgs);
41617
+ dispatch({ type: "DONE", exitCode: code });
41618
+ return;
41619
+ }
41620
+ dispatch({ type: "SCANNING", count: resolved.length });
41621
+ const result = await callPyPIAnalyzeAPI(resolved, config);
41622
+ if (result.action === "pass") {
41623
+ dispatch({ type: "PASS", count: resolved.length });
41624
+ dispatch({ type: "INSTALLING" });
41625
+ const code = await runPip(parsed.rawArgs);
41626
+ dispatch({ type: "DONE", exitCode: code });
41627
+ return;
41628
+ }
41629
+ if (result.action === "warn") {
41630
+ dispatch({ type: "WARN", result });
41631
+ process.stdout.write(renderResultStatic(result, config) + "\n");
41632
+ dispatch({ type: "INSTALLING" });
41633
+ const code = await runPip(parsed.rawArgs);
41634
+ dispatch({ type: "DONE", exitCode: code });
41635
+ return;
41636
+ }
41637
+ if (result.action === "block") {
41638
+ process.stdout.write(renderResultStatic(result, config) + "\n");
41639
+ if (parsed.dgForce) {
41640
+ dispatch({ type: "BLOCKED", result, dgForce: true });
41641
+ dispatch({ type: "INSTALLING" });
41642
+ const code = await runPip(parsed.rawArgs);
41643
+ dispatch({ type: "DONE", exitCode: code });
41644
+ return;
41645
+ }
41646
+ dispatch({ type: "BLOCKED", result, dgForce: false });
41647
+ const shouldProceed = await new Promise((resolve) => {
41648
+ pendingInstall.current = () => resolve(true);
41649
+ rejectRef.current = () => resolve(false);
41650
+ });
41651
+ if (shouldProceed) {
41652
+ dispatch({ type: "INSTALLING" });
41653
+ const code = await runPip(parsed.rawArgs);
41654
+ dispatch({ type: "DONE", exitCode: code });
41655
+ } else {
41656
+ dispatch({ type: "DONE", exitCode: 2 });
41657
+ }
41658
+ return;
41659
+ }
41660
+ } catch (error) {
41661
+ if (error instanceof TrialExhaustedError) {
41662
+ dispatch({ type: "TRIAL_EXHAUSTED" });
41663
+ return;
41664
+ }
41665
+ const message = error instanceof Error ? error.message : String(error);
41666
+ dispatch({ type: "ERROR", message, proceed: true });
41667
+ dispatch({ type: "INSTALLING" });
41668
+ const code = await runPip(parsedRef.current.rawArgs);
41669
+ dispatch({ type: "DONE", exitCode: code });
41670
+ }
41671
+ })();
41672
+ }, [pipArgs, config]);
41673
+ return { state, confirmInstall, rejectInstall };
41674
+ }
41675
+ var import_react27;
41676
+ var init_usePipWrapper = __esm({
41677
+ "src/ui/hooks/usePipWrapper.ts"() {
41678
+ "use strict";
41679
+ import_react27 = __toESM(require_react());
41680
+ init_pip_wrapper();
41681
+ init_api();
41682
+ init_static_output();
41683
+ }
41684
+ });
41685
+
41686
+ // src/ui/PipWrapperApp.tsx
41687
+ var PipWrapperApp_exports = {};
41688
+ __export(PipWrapperApp_exports, {
41689
+ PipWrapperApp: () => PipWrapperApp
41690
+ });
41691
+ var import_react28, import_jsx_runtime9, PipWrapperApp;
41692
+ var init_PipWrapperApp = __esm({
41693
+ async "src/ui/PipWrapperApp.tsx"() {
41694
+ "use strict";
41695
+ import_react28 = __toESM(require_react());
41696
+ await init_build2();
41697
+ init_usePipWrapper();
41698
+ await init_Spinner();
41699
+ await init_ResultsView();
41700
+ await init_ConfirmPrompt();
41701
+ await init_ErrorView();
41702
+ import_jsx_runtime9 = __toESM(require_jsx_runtime());
41703
+ PipWrapperApp = ({
41704
+ pipArgs,
41705
+ config
41706
+ }) => {
41707
+ const { state, confirmInstall, rejectInstall } = usePipWrapper(pipArgs, config);
41708
+ const { exit } = use_app_default();
41709
+ (0, import_react28.useEffect)(() => {
41710
+ if (state.phase === "done") {
41711
+ process.exitCode = state.exitCode;
41712
+ const timer = setTimeout(() => exit(), 0);
41713
+ return () => clearTimeout(timer);
41714
+ }
41715
+ if (state.phase === "trial_exhausted") {
41716
+ process.exitCode = 1;
41717
+ const timer = setTimeout(() => exit(), 0);
41718
+ return () => clearTimeout(timer);
41719
+ }
41720
+ if (state.phase === "passthrough") {
41721
+ }
41722
+ }, [state, exit]);
41723
+ const handleConfirm = (0, import_react28.useCallback)(() => {
41724
+ confirmInstall();
41725
+ }, [confirmInstall]);
41726
+ const handleReject = (0, import_react28.useCallback)(() => {
41727
+ rejectInstall();
41728
+ }, [rejectInstall]);
41729
+ switch (state.phase) {
41730
+ case "resolving":
41731
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41732
+ Spinner2,
41733
+ {
41734
+ label: `Resolving ${state.count} package${state.count !== 1 ? "s" : ""} from PyPI...`
41735
+ }
41736
+ );
41737
+ case "scanning":
41738
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41739
+ Spinner2,
41740
+ {
41741
+ label: `Scanning ${state.count} Python package${state.count !== 1 ? "s" : ""}...`
41742
+ }
41743
+ );
41744
+ case "pass":
41745
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: "green", children: [
41746
+ "\u2713",
41747
+ " ",
41748
+ state.count,
41749
+ " package",
41750
+ state.count !== 1 ? "s" : "",
41751
+ " scanned",
41752
+ " \u2014 ",
41753
+ "all clear"
41754
+ ] });
41755
+ case "warn":
41756
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", children: [
41757
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41758
+ ResultsView,
41759
+ {
41760
+ result: state.result,
41761
+ config,
41762
+ durationMs: state.result.durationMs
41763
+ }
41764
+ ),
41765
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "yellow", children: "Warnings detected. Proceeding with install..." })
41766
+ ] });
41767
+ case "blocked":
41768
+ if (state.dgForce) {
41769
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", children: [
41770
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41771
+ ResultsView,
41772
+ {
41773
+ result: state.result,
41774
+ config,
41775
+ durationMs: state.result.durationMs
41776
+ }
41777
+ ),
41778
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "yellow", bold: true, children: "--dg-force: Bypassing block. Install at your own risk." })
41779
+ ] });
41780
+ }
41781
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", children: [
41782
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41783
+ ResultsView,
41784
+ {
41785
+ result: state.result,
41786
+ config,
41787
+ durationMs: state.result.durationMs
41788
+ }
41789
+ ),
41790
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: "red", bold: true, children: [
41791
+ "BLOCKED:",
41792
+ " ",
41793
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "red", children: "High-risk packages detected." })
41794
+ ] }),
41795
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
41796
+ ConfirmPrompt,
41797
+ {
41798
+ message: "Proceed with install anyway?",
41799
+ onConfirm: handleConfirm,
41800
+ onReject: handleReject
41801
+ }
41802
+ )
41258
41803
  ] });
41804
+ case "installing":
41805
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner2, { label: "Installing with pip..." });
41806
+ case "done":
41807
+ return null;
41808
+ case "error":
41809
+ if (state.proceed) {
41810
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: "yellow", children: [
41811
+ "Warning: Scan failed (",
41812
+ state.message,
41813
+ "). Proceeding with install."
41814
+ ] });
41815
+ }
41816
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorView, { error: new Error(state.message) });
41817
+ case "passthrough":
41818
+ return null;
41819
+ case "trial_exhausted": {
41820
+ let hasKey = false;
41821
+ try {
41822
+ const { getStoredApiKey: getStoredApiKey2 } = (init_auth(), __toCommonJS(auth_exports));
41823
+ hasKey = !!getStoredApiKey2();
41824
+ } catch {
41825
+ }
41826
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { flexDirection: "column", paddingLeft: 2, children: hasKey ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
41827
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "yellow", bold: true, children: "Your API key may be invalid or expired." }),
41828
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
41829
+ "Run ",
41830
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", bold: true, children: "dg logout" }),
41831
+ " then ",
41832
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
41833
+ " to re-authenticate."
41834
+ ] })
41835
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
41836
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
41837
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
41838
+ "Run ",
41839
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
41840
+ " to create a free account and continue scanning."
41841
+ ] })
41842
+ ] }) });
41843
+ }
41259
41844
  }
41260
41845
  };
41261
41846
  }
41262
41847
  });
41263
41848
 
41264
41849
  // src/ui/hooks/useScan.ts
41265
- function reducer3(_state, action) {
41850
+ function reducer4(_state, action) {
41266
41851
  switch (action.type) {
41267
41852
  case "PROJECTS_FOUND":
41268
41853
  return { phase: "selecting", projects: action.projects };
@@ -41283,10 +41868,10 @@ function reducer3(_state, action) {
41283
41868
  }
41284
41869
  }
41285
41870
  function useScan(config) {
41286
- const [state, dispatch] = (0, import_react27.useReducer)(reducer3, { phase: "discovering" });
41287
- const started = (0, import_react27.useRef)(false);
41288
- const [multiProjects, setMultiProjects] = (0, import_react27.useState)(null);
41289
- (0, import_react27.useEffect)(() => {
41871
+ const [state, dispatch] = (0, import_react29.useReducer)(reducer4, { phase: "discovering" });
41872
+ const started = (0, import_react29.useRef)(false);
41873
+ const [multiProjects, setMultiProjects] = (0, import_react29.useState)(null);
41874
+ (0, import_react29.useEffect)(() => {
41290
41875
  if (started.current) return;
41291
41876
  started.current = true;
41292
41877
  const projects = discoverProjects(process.cwd());
@@ -41297,10 +41882,9 @@ function useScan(config) {
41297
41882
  }
41298
41883
  try {
41299
41884
  const discovery = discoverChanges(process.cwd(), config);
41300
- const packages = discovery.packages.filter((p) => !config.allowlist.includes(p.name.toLowerCase()));
41885
+ const packages = discovery.packages;
41301
41886
  if (packages.length === 0) {
41302
- const message = discovery.packages.length === 0 ? "No package changes detected." : "All changed packages are allowlisted.";
41303
- dispatch({ type: "DISCOVERY_EMPTY", message });
41887
+ dispatch({ type: "DISCOVERY_EMPTY", message: "No package changes detected." });
41304
41888
  return;
41305
41889
  }
41306
41890
  dispatch({ type: "DISCOVERY_COMPLETE", packages, skippedCount: discovery.skipped.length });
@@ -41314,11 +41898,11 @@ function useScan(config) {
41314
41898
  scanProjects(projects, config, dispatch);
41315
41899
  }
41316
41900
  }, [config]);
41317
- const scanSelectedProjects = (0, import_react27.useCallback)((projects) => {
41901
+ const scanSelectedProjects = (0, import_react29.useCallback)((projects) => {
41318
41902
  dispatch({ type: "DISCOVERY_COMPLETE", packages: [], skippedCount: 0 });
41319
41903
  scanProjects(projects, config, dispatch);
41320
41904
  }, [config]);
41321
- const restartSelection = (0, import_react27.useCallback)(() => {
41905
+ const restartSelection = (0, import_react29.useCallback)(() => {
41322
41906
  if (!multiProjects) return;
41323
41907
  dispatch({ type: "RESTART_SELECTION", projects: multiProjects });
41324
41908
  }, [multiProjects]);
@@ -41359,7 +41943,7 @@ async function scanProjects(projects, config, dispatch) {
41359
41943
  const discovery = discoverChanges(proj.path, fullScanConfig);
41360
41944
  for (const pkg of discovery.packages) {
41361
41945
  const key = `${pkg.name}@${pkg.version}`;
41362
- if (!config.allowlist.includes(pkg.name.toLowerCase()) && !seenNpm.has(key)) {
41946
+ if (!seenNpm.has(key)) {
41363
41947
  seenNpm.add(key);
41364
41948
  npmPackages.push(pkg);
41365
41949
  }
@@ -41372,7 +41956,7 @@ async function scanProjects(projects, config, dispatch) {
41372
41956
  const packages = parsePythonDepFile(proj.path, proj.depFile);
41373
41957
  for (const pkg of packages) {
41374
41958
  const key = `${pkg.name}@${pkg.version}`;
41375
- if (!config.allowlist.includes(pkg.name.toLowerCase()) && !seenPypi.has(key)) {
41959
+ if (!seenPypi.has(key)) {
41376
41960
  seenPypi.add(key);
41377
41961
  pypiPackages.push(pkg);
41378
41962
  }
@@ -41421,7 +42005,7 @@ async function scanProjects(projects, config, dispatch) {
41421
42005
  }
41422
42006
  const allPackages = results.flatMap((r) => r.packages);
41423
42007
  const maxScore = Math.max(0, ...allPackages.map((p) => p.score));
41424
- const action = maxScore >= config.blockThreshold ? "block" : maxScore >= config.warnThreshold ? "warn" : "pass";
42008
+ const action = maxScore >= 70 ? "block" : maxScore >= 60 ? "warn" : "pass";
41425
42009
  const safeVersions = {};
41426
42010
  for (const r of results) Object.assign(safeVersions, r.safeVersions);
41427
42011
  const merged = {
@@ -41440,11 +42024,11 @@ async function scanProjects(projects, config, dispatch) {
41440
42024
  dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
41441
42025
  }
41442
42026
  }
41443
- var import_react27;
42027
+ var import_react29;
41444
42028
  var init_useScan = __esm({
41445
42029
  "src/ui/hooks/useScan.ts"() {
41446
42030
  "use strict";
41447
- import_react27 = __toESM(require_react());
42031
+ import_react29 = __toESM(require_react());
41448
42032
  init_lockfile();
41449
42033
  init_api();
41450
42034
  init_discover();
@@ -41454,7 +42038,7 @@ var init_useScan = __esm({
41454
42038
 
41455
42039
  // src/ui/components/ProgressBar.tsx
41456
42040
  function estimateScanSeconds(packageCount) {
41457
- return Math.ceil(8 + packageCount * 0.12);
42041
+ return Math.ceil(Math.max(5, packageCount * 0.35 - 10));
41458
42042
  }
41459
42043
  function formatTime(seconds) {
41460
42044
  if (seconds < 60) return `${seconds}s`;
@@ -41462,23 +42046,23 @@ function formatTime(seconds) {
41462
42046
  const s = seconds % 60;
41463
42047
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
41464
42048
  }
41465
- var import_react28, import_chalk9, import_jsx_runtime9, ProgressBar;
42049
+ var import_react30, import_chalk9, import_jsx_runtime10, ProgressBar;
41466
42050
  var init_ProgressBar = __esm({
41467
42051
  async "src/ui/components/ProgressBar.tsx"() {
41468
42052
  "use strict";
41469
- import_react28 = __toESM(require_react());
42053
+ import_react30 = __toESM(require_react());
41470
42054
  await init_build2();
41471
42055
  await init_build3();
41472
42056
  import_chalk9 = __toESM(require_source());
41473
- import_jsx_runtime9 = __toESM(require_jsx_runtime());
42057
+ import_jsx_runtime10 = __toESM(require_jsx_runtime());
41474
42058
  ProgressBar = ({
41475
42059
  value,
41476
42060
  total,
41477
42061
  label
41478
42062
  }) => {
41479
- const startRef = (0, import_react28.useRef)(Date.now());
41480
- const [elapsed, setElapsed] = (0, import_react28.useState)(0);
41481
- (0, import_react28.useEffect)(() => {
42063
+ const startRef = (0, import_react30.useRef)(Date.now());
42064
+ const [elapsed, setElapsed] = (0, import_react30.useState)(0);
42065
+ (0, import_react30.useEffect)(() => {
41482
42066
  const timer = setInterval(() => {
41483
42067
  setElapsed(Math.floor((Date.now() - startRef.current) / 1e3));
41484
42068
  }, 1e3);
@@ -41495,32 +42079,32 @@ var init_ProgressBar = __esm({
41495
42079
  const empty = barWidth - filled;
41496
42080
  const filledBar = "\u2501".repeat(filled);
41497
42081
  const emptyBar = "\u2501".repeat(empty);
41498
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
41499
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
42082
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
42083
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41500
42084
  import_chalk9.default.cyan("\u25C6"),
41501
42085
  " ",
41502
42086
  import_chalk9.default.bold("Dependency Guardian")
41503
42087
  ] }),
41504
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: "" }),
41505
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
41506
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "cyan", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(build_default, { type: "dots" }) }),
41507
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
42088
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42089
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { children: [
42090
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: "cyan", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(build_default, { type: "dots" }) }),
42091
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41508
42092
  " Scanning ",
41509
42093
  total,
41510
42094
  " packages... "
41511
42095
  ] }),
41512
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { dimColor: true, children: timeInfo })
42096
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: timeInfo })
41513
42097
  ] }),
41514
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { children: [
41515
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { children: " " }),
41516
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: "green", children: filledBar }),
41517
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { dimColor: true, children: emptyBar }),
41518
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { children: [
42098
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { children: [
42099
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: " " }),
42100
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { color: "green", children: filledBar }),
42101
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: emptyBar }),
42102
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41519
42103
  " ",
41520
42104
  counter
41521
42105
  ] })
41522
42106
  ] }),
41523
- label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { dimColor: true, children: [
42107
+ label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41524
42108
  " ",
41525
42109
  import_chalk9.default.dim("\u203A"),
41526
42110
  " ",
@@ -41533,9 +42117,9 @@ var init_ProgressBar = __esm({
41533
42117
 
41534
42118
  // src/ui/hooks/useExpandAnimation.ts
41535
42119
  function useExpandAnimation(targetHeight, active, durationMs = 180) {
41536
- const [visibleLines, setVisibleLines] = (0, import_react29.useState)(0);
41537
- const timerRef = (0, import_react29.useRef)(null);
41538
- (0, import_react29.useEffect)(() => {
42120
+ const [visibleLines, setVisibleLines] = (0, import_react31.useState)(0);
42121
+ const timerRef = (0, import_react31.useRef)(null);
42122
+ (0, import_react31.useEffect)(() => {
41539
42123
  if (timerRef.current) {
41540
42124
  clearInterval(timerRef.current);
41541
42125
  timerRef.current = null;
@@ -41569,22 +42153,22 @@ function useExpandAnimation(targetHeight, active, durationMs = 180) {
41569
42153
  isAnimating: active && visibleLines > 0 && visibleLines < targetHeight
41570
42154
  };
41571
42155
  }
41572
- var import_react29;
42156
+ var import_react31;
41573
42157
  var init_useExpandAnimation = __esm({
41574
42158
  "src/ui/hooks/useExpandAnimation.ts"() {
41575
42159
  "use strict";
41576
- import_react29 = __toESM(require_react());
42160
+ import_react31 = __toESM(require_react());
41577
42161
  }
41578
42162
  });
41579
42163
 
41580
42164
  // src/ui/hooks/useTerminalSize.ts
41581
42165
  function useTerminalSize() {
41582
42166
  const { stdout } = use_stdout_default();
41583
- const [size, setSize] = (0, import_react30.useState)({
42167
+ const [size, setSize] = (0, import_react32.useState)({
41584
42168
  rows: stdout?.rows ?? process.stdout.rows ?? 24,
41585
42169
  cols: stdout?.columns ?? process.stdout.columns ?? 80
41586
42170
  });
41587
- (0, import_react30.useEffect)(() => {
42171
+ (0, import_react32.useEffect)(() => {
41588
42172
  const handle = () => {
41589
42173
  const rows = process.stdout.rows ?? 24;
41590
42174
  const cols = process.stdout.columns ?? 80;
@@ -41602,11 +42186,11 @@ function useTerminalSize() {
41602
42186
  }, []);
41603
42187
  return size;
41604
42188
  }
41605
- var import_react30;
42189
+ var import_react32;
41606
42190
  var init_useTerminalSize = __esm({
41607
42191
  async "src/ui/hooks/useTerminalSize.ts"() {
41608
42192
  "use strict";
41609
- import_react30 = __toESM(require_react());
42193
+ import_react32 = __toESM(require_react());
41610
42194
  await init_build2();
41611
42195
  }
41612
42196
  });
@@ -41622,10 +42206,10 @@ function groupPackages3(packages) {
41622
42206
  }
41623
42207
  return [...map.entries()].map(([fingerprint, pkgs]) => ({ packages: pkgs, key: fingerprint })).sort((a, b) => b.packages[0].score - a.packages[0].score);
41624
42208
  }
41625
- function actionBadge3(score, config) {
41626
- if (score >= config.blockThreshold)
42209
+ function actionBadge3(score) {
42210
+ if (score >= 70)
41627
42211
  return { label: "BLOCK", color: import_chalk10.default.red };
41628
- if (score >= config.warnThreshold)
42212
+ if (score >= 60)
41629
42213
  return { label: "WARN", color: import_chalk10.default.yellow };
41630
42214
  return { label: "PASS", color: import_chalk10.default.green };
41631
42215
  }
@@ -41639,6 +42223,7 @@ function findingsSummaryHeight(group) {
41639
42223
  const rep = group.packages[0];
41640
42224
  const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical);
41641
42225
  let h = visibleFindings.length;
42226
+ if (rep.license) h += 1;
41642
42227
  if (visibleFindings.length === 0 && rep.score > 0) {
41643
42228
  h += (rep.reasons ?? []).length;
41644
42229
  h += 1;
@@ -41650,6 +42235,7 @@ function findingsDetailHeight(group, safeVersions) {
41650
42235
  const rep = group.packages[0];
41651
42236
  const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical).sort((a, b) => b.severity - a.severity);
41652
42237
  let h = 0;
42238
+ if (rep.license) h += 1;
41653
42239
  if (group.packages.length > 3) h += 1;
41654
42240
  if (visibleFindings.length === 0 && rep.score > 0) {
41655
42241
  h += (rep.reasons ?? []).length;
@@ -41691,88 +42277,26 @@ function buildDetailLines(group, safeVersion, maxWidth) {
41691
42277
  const evidenceWidth = Math.max(30, maxWidth - 12);
41692
42278
  if (group.packages.length > 3) {
41693
42279
  lines.push(
41694
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42280
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
41695
42281
  "Affects: ",
41696
42282
  affectsLine(group)
41697
42283
  ] }, "affects")
41698
42284
  );
41699
- lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "affects-gap"));
41700
- }
41701
- if (visibleFindings.length === 0 && rep.score > 0) {
41702
- const reasons = rep.reasons ?? [];
41703
- for (let i = 0; i < reasons.length; i++) {
41704
- lines.push(
41705
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: truncate3(reasons[i], maxWidth - 4) }, `reason-${i}`)
41706
- );
41707
- }
41708
- lines.push(
41709
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41710
- import_chalk10.default.yellow("\u2192"),
41711
- " ",
41712
- import_chalk10.default.yellow("Upgrade to Pro"),
41713
- " ",
41714
- import_chalk10.default.dim("to see finding details \u2014 dg login")
41715
- ] }, "upgrade")
41716
- );
41717
- return lines;
42285
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }, "affects-gap"));
41718
42286
  }
41719
- const hasEvidence = visibleFindings.some((f) => f.evidence && f.evidence.length > 0);
41720
- for (let idx = 0; idx < visibleFindings.length; idx++) {
41721
- const finding = visibleFindings[idx];
41722
- const sevLabel = SEVERITY_LABELS3[finding.severity] ?? "INFO";
41723
- const sevColor = SEVERITY_COLORS[finding.severity] ?? SEVERITY_COLORS[1];
41724
- const evidence = finding.evidence ?? [];
41725
- const findingId = finding.id ?? finding.category ?? "";
41726
- if (idx > 0) lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, `gap-${idx}`));
41727
- lines.push(
41728
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41729
- sevColor(pad3(sevLabel, 5)),
41730
- " ",
41731
- findingId
41732
- ] }, `${findingId}-${idx}-badge`)
41733
- );
42287
+ if (rep.score > 0) {
41734
42288
  lines.push(
41735
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41736
- " ",
41737
- finding.title
41738
- ] }, `${findingId}-${idx}-title`)
41739
- );
41740
- const evidenceCap = Math.min(evidence.length, DETAIL_EVIDENCE_LIMIT);
41741
- for (let i = 0; i < evidenceCap; i++) {
41742
- lines.push(
41743
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41744
- " ",
41745
- import_chalk10.default.dim("\u203A"),
41746
- " ",
41747
- truncate3(evidence[i], evidenceWidth)
41748
- ] }, `${findingId}-${idx}-ev-${i}`)
41749
- );
41750
- }
41751
- if (evidence.length > DETAIL_EVIDENCE_LIMIT) {
41752
- lines.push(
41753
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41754
- " ",
41755
- import_chalk10.default.dim(`+${evidence.length - DETAIL_EVIDENCE_LIMIT} more`)
41756
- ] }, `${findingId}-${idx}-overflow`)
41757
- );
41758
- }
41759
- }
41760
- if (visibleFindings.length > 0 && !hasEvidence) {
41761
- lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "upgrade-gap"));
41762
- lines.push(
41763
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41764
- import_chalk10.default.yellow("\u2192"),
41765
- " ",
41766
- import_chalk10.default.yellow("Upgrade to Team"),
41767
- " ",
41768
- import_chalk10.default.dim("for evidence and file locations")
41769
- ] }, "upgrade-evidence")
42289
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42290
+ "Score: ",
42291
+ rep.score,
42292
+ "/100"
42293
+ ] }, "score-info")
41770
42294
  );
41771
42295
  }
41772
42296
  if (rep.recommendation) {
41773
- lines.push(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }, "rec-gap"));
42297
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }, "rec-gap"));
41774
42298
  lines.push(
41775
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42299
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
41776
42300
  import_chalk10.default.dim("Rec:"),
41777
42301
  " ",
41778
42302
  import_chalk10.default.cyan(truncate3(rep.recommendation, maxWidth - 8))
@@ -41781,7 +42305,7 @@ function buildDetailLines(group, safeVersion, maxWidth) {
41781
42305
  }
41782
42306
  if (safeVersion) {
41783
42307
  lines.push(
41784
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: import_chalk10.default.green(`Safe: ${rep.name}@${safeVersion}`) }, "safe")
42308
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: import_chalk10.default.green(`Safe: ${rep.name}@${safeVersion}`) }, "safe")
41785
42309
  );
41786
42310
  }
41787
42311
  return lines;
@@ -41796,18 +42320,34 @@ function viewReducer(_state, action) {
41796
42320
  return { cursor: action.cursor, expandedIndex: action.expandedIndex, expandLevel: action.expandLevel, viewport: action.viewport };
41797
42321
  }
41798
42322
  }
41799
- var import_react31, import_chalk10, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, DETAIL_PANE_CHROME, DETAIL_EVIDENCE_LIMIT, InteractiveResultsView, T, FindingsSummary;
42323
+ function licenseLine(rep) {
42324
+ const lc = rep.license;
42325
+ if (!lc) return null;
42326
+ const spdx = lc.spdx ?? lc.raw ?? "";
42327
+ const desc = LICENSE_DESCRIPTIONS[lc.riskCategory] ?? "";
42328
+ const lcColor = lc.riskCategory === "permissive" ? import_chalk10.default.green : lc.riskCategory === "no-license" || lc.riskCategory === "unlicensed" || lc.riskCategory === "network-copyleft" ? import_chalk10.default.red : import_chalk10.default.yellow;
42329
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42330
+ T.branch,
42331
+ " ",
42332
+ lcColor(spdx),
42333
+ " ",
42334
+ import_chalk10.default.dim("\u2014"),
42335
+ " ",
42336
+ import_chalk10.default.dim(desc)
42337
+ ] }, "license-info");
42338
+ }
42339
+ var import_react33, import_chalk10, import_jsx_runtime11, SEVERITY_LABELS2, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, DETAIL_PANE_CHROME, InteractiveResultsView, T, LICENSE_DESCRIPTIONS, FindingsSummary;
41800
42340
  var init_InteractiveResultsView = __esm({
41801
42341
  async "src/ui/components/InteractiveResultsView.tsx"() {
41802
42342
  "use strict";
41803
- import_react31 = __toESM(require_react());
42343
+ import_react33 = __toESM(require_react());
41804
42344
  await init_build2();
41805
42345
  import_chalk10 = __toESM(require_source());
41806
42346
  await init_ScoreHeader();
41807
42347
  init_useExpandAnimation();
41808
42348
  await init_useTerminalSize();
41809
- import_jsx_runtime10 = __toESM(require_jsx_runtime());
41810
- SEVERITY_LABELS3 = {
42349
+ import_jsx_runtime11 = __toESM(require_jsx_runtime());
42350
+ SEVERITY_LABELS2 = {
41811
42351
  5: "CRIT",
41812
42352
  4: "HIGH",
41813
42353
  3: "MED",
@@ -41824,7 +42364,6 @@ var init_InteractiveResultsView = __esm({
41824
42364
  EVIDENCE_LIMIT2 = 2;
41825
42365
  FIXED_CHROME = 21;
41826
42366
  DETAIL_PANE_CHROME = 20;
41827
- DETAIL_EVIDENCE_LIMIT = 10;
41828
42367
  InteractiveResultsView = ({
41829
42368
  result,
41830
42369
  config,
@@ -41833,24 +42372,24 @@ var init_InteractiveResultsView = __esm({
41833
42372
  onBack,
41834
42373
  discoveredTotal
41835
42374
  }) => {
41836
- const flagged = (0, import_react31.useMemo)(
42375
+ const flagged = (0, import_react33.useMemo)(
41837
42376
  () => result.packages.filter((p) => p.score > 0),
41838
42377
  [result.packages]
41839
42378
  );
41840
- const clean = (0, import_react31.useMemo)(
42379
+ const clean = (0, import_react33.useMemo)(
41841
42380
  () => result.packages.filter((p) => p.score === 0),
41842
42381
  [result.packages]
41843
42382
  );
41844
42383
  const total = result.packages.length;
41845
- const [searchQuery, setSearchQuery] = (0, import_react31.useState)("");
41846
- const allGroups = (0, import_react31.useMemo)(() => groupPackages3(flagged), [flagged]);
42384
+ const [searchQuery, setSearchQuery] = (0, import_react33.useState)("");
42385
+ const allGroups = (0, import_react33.useMemo)(() => groupPackages3(flagged), [flagged]);
41847
42386
  const allGroupCount = allGroups.length;
41848
- const groups = (0, import_react31.useMemo)(() => {
42387
+ const groups = (0, import_react33.useMemo)(() => {
41849
42388
  if (!searchQuery) return allGroups;
41850
42389
  const q = searchQuery.toLowerCase();
41851
42390
  return allGroups.filter((g) => g.packages.some((p) => p.name.toLowerCase().includes(q)));
41852
42391
  }, [allGroups, searchQuery]);
41853
- const severityCounts = (0, import_react31.useMemo)(() => {
42392
+ const severityCounts = (0, import_react33.useMemo)(() => {
41854
42393
  const counts = {};
41855
42394
  for (const pkg of flagged) {
41856
42395
  const maxSev = pkg.findings.reduce((m, f) => Math.max(m, f.severity), 0);
@@ -41858,28 +42397,28 @@ var init_InteractiveResultsView = __esm({
41858
42397
  }
41859
42398
  return counts;
41860
42399
  }, [flagged]);
41861
- const [view, dispatchView] = (0, import_react31.useReducer)(viewReducer, {
42400
+ const [view, dispatchView] = (0, import_react33.useReducer)(viewReducer, {
41862
42401
  cursor: 0,
41863
42402
  expandLevel: null,
41864
42403
  expandedIndex: null,
41865
42404
  viewport: 0
41866
42405
  });
41867
- const viewRef = (0, import_react31.useRef)(view);
42406
+ const viewRef = (0, import_react33.useRef)(view);
41868
42407
  viewRef.current = view;
41869
- const [detailPane, setDetailPane] = (0, import_react31.useState)(null);
41870
- const detailPaneRef = (0, import_react31.useRef)(detailPane);
42408
+ const [detailPane, setDetailPane] = (0, import_react33.useState)(null);
42409
+ const detailPaneRef = (0, import_react33.useRef)(detailPane);
41871
42410
  detailPaneRef.current = detailPane;
41872
- const [showHelp, setShowHelp] = (0, import_react31.useState)(false);
41873
- const showHelpRef = (0, import_react31.useRef)(showHelp);
42411
+ const [showHelp, setShowHelp] = (0, import_react33.useState)(false);
42412
+ const showHelpRef = (0, import_react33.useRef)(showHelp);
41874
42413
  showHelpRef.current = showHelp;
41875
- const [searchMode, setSearchMode] = (0, import_react31.useState)(false);
41876
- const searchModeRef = (0, import_react31.useRef)(searchMode);
42414
+ const [searchMode, setSearchMode] = (0, import_react33.useState)(false);
42415
+ const searchModeRef = (0, import_react33.useRef)(searchMode);
41877
42416
  searchModeRef.current = searchMode;
41878
42417
  const { rows: termRows, cols: termCols } = useTerminalSize();
41879
42418
  const availableRows = Math.max(5, termRows - FIXED_CHROME);
41880
42419
  const innerWidth = Math.max(40, termCols - 6);
41881
42420
  const detailGroupIdx = detailPane?.groupIndex ?? -1;
41882
- const detailLines = (0, import_react31.useMemo)(() => {
42421
+ const detailLines = (0, import_react33.useMemo)(() => {
41883
42422
  if (detailGroupIdx < 0) return [];
41884
42423
  const group = groups[detailGroupIdx];
41885
42424
  if (!group) return [];
@@ -41889,7 +42428,7 @@ var init_InteractiveResultsView = __esm({
41889
42428
  const getLevel = (idx) => {
41890
42429
  return view.expandedIndex === idx ? view.expandLevel : null;
41891
42430
  };
41892
- const expandTargetHeight = (0, import_react31.useMemo)(() => {
42431
+ const expandTargetHeight = (0, import_react33.useMemo)(() => {
41893
42432
  if (view.expandedIndex === null || view.expandLevel === null) return 0;
41894
42433
  const group = groups[view.expandedIndex];
41895
42434
  if (!group) return 0;
@@ -41905,7 +42444,7 @@ var init_InteractiveResultsView = __esm({
41905
42444
  if (idx === view.expandedIndex) return 1 + animVisibleLines;
41906
42445
  return groupRowHeight(group, level, result.safeVersions);
41907
42446
  };
41908
- const visibleEnd = (0, import_react31.useMemo)(() => {
42447
+ const visibleEnd = (0, import_react33.useMemo)(() => {
41909
42448
  let consumed = 0;
41910
42449
  let end = view.viewport;
41911
42450
  while (end < groups.length) {
@@ -41937,14 +42476,14 @@ var init_InteractiveResultsView = __esm({
41937
42476
  }
41938
42477
  return newStart;
41939
42478
  };
41940
- (0, import_react31.useEffect)(() => {
42479
+ (0, import_react33.useEffect)(() => {
41941
42480
  if (groups.length === 0) return;
41942
42481
  const { cursor, expandedIndex, expandLevel, viewport } = viewRef.current;
41943
42482
  const clamped = Math.min(viewport, Math.max(0, groups.length - 1));
41944
42483
  const newVp = adjustViewport(cursor, expandedIndex, expandLevel, clamped);
41945
42484
  dispatchView({ type: "MOVE", cursor, viewport: newVp });
41946
42485
  }, [availableRows]);
41947
- (0, import_react31.useEffect)(() => {
42486
+ (0, import_react33.useEffect)(() => {
41948
42487
  const dp = detailPaneRef.current;
41949
42488
  if (dp && detailLines.length > 0) {
41950
42489
  const maxScroll = Math.max(0, detailLines.length - detailContentRows);
@@ -41994,7 +42533,7 @@ var init_InteractiveResultsView = __esm({
41994
42533
  setDetailPane({ groupIndex: dp.groupIndex, scroll: 0 });
41995
42534
  } else if (input === "G") {
41996
42535
  setDetailPane({ groupIndex: dp.groupIndex, scroll: maxScroll });
41997
- } else if (input === "e" || input === "b" || key.escape) {
42536
+ } else if (input === " " || input === "e" || input === "b" || key.escape) {
41998
42537
  setDetailPane(null);
41999
42538
  } else if (input === "q") {
42000
42539
  onExit();
@@ -42042,7 +42581,7 @@ var init_InteractiveResultsView = __esm({
42042
42581
  }
42043
42582
  const newVp = adjustViewport(cursor, newExpIdx, newExpLvl, vpStart);
42044
42583
  dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
42045
- } else if (input === "e") {
42584
+ } else if (input === " " || input === "e") {
42046
42585
  setDetailPane({ groupIndex: view.cursor, scroll: 0 });
42047
42586
  } else if (input === "/") {
42048
42587
  setSearchMode(true);
@@ -42055,12 +42594,13 @@ var init_InteractiveResultsView = __esm({
42055
42594
  const visibleGroups = groups.slice(view.viewport, visibleEnd);
42056
42595
  const aboveCount = view.viewport;
42057
42596
  const belowCount = groups.length - visibleEnd;
42058
- const nameCol = Math.max(20, innerWidth - 22);
42597
+ const lcCol = 16;
42598
+ const nameCol = Math.max(20, innerWidth - 22 - lcCol);
42059
42599
  const clampedCursor = groups.length > 0 ? Math.min(view.cursor, groups.length - 1) : 0;
42060
42600
  if (showHelp) {
42061
42601
  const isDetail = detailPane !== null;
42062
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
42063
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
42602
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
42603
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
42064
42604
  ScoreHeader,
42065
42605
  {
42066
42606
  score: result.score,
@@ -42071,7 +42611,7 @@ var init_InteractiveResultsView = __esm({
42071
42611
  severityCounts
42072
42612
  }
42073
42613
  ),
42074
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42614
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
42075
42615
  Box_default,
42076
42616
  {
42077
42617
  flexDirection: "column",
@@ -42081,82 +42621,82 @@ var init_InteractiveResultsView = __esm({
42081
42621
  paddingRight: 2,
42082
42622
  width: "100%",
42083
42623
  children: [
42084
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { bold: true, children: [
42624
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
42085
42625
  import_chalk10.default.cyan("\u25C6"),
42086
42626
  " Keyboard Shortcuts"
42087
42627
  ] }),
42088
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42089
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: " Navigation" }),
42090
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42628
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42629
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Navigation" }),
42630
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42091
42631
  " ",
42092
42632
  import_chalk10.default.cyan("\u2191 k"),
42093
42633
  " ",
42094
42634
  import_chalk10.default.dim("Move up")
42095
42635
  ] }),
42096
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42636
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42097
42637
  " ",
42098
42638
  import_chalk10.default.cyan("\u2193 j"),
42099
42639
  " ",
42100
42640
  import_chalk10.default.dim("Move down")
42101
42641
  ] }),
42102
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42642
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42103
42643
  " ",
42104
42644
  import_chalk10.default.cyan("g"),
42105
42645
  " ",
42106
42646
  import_chalk10.default.dim("Jump to top")
42107
42647
  ] }),
42108
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42648
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42109
42649
  " ",
42110
42650
  import_chalk10.default.cyan("G"),
42111
42651
  " ",
42112
42652
  import_chalk10.default.dim("Jump to bottom")
42113
42653
  ] }),
42114
- !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42654
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42115
42655
  " ",
42116
42656
  import_chalk10.default.cyan("PgUp"),
42117
42657
  " ",
42118
42658
  import_chalk10.default.dim("Page up")
42119
42659
  ] }),
42120
- !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42660
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42121
42661
  " ",
42122
42662
  import_chalk10.default.cyan("PgDn"),
42123
42663
  " ",
42124
42664
  import_chalk10.default.dim("Page down")
42125
42665
  ] }),
42126
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42127
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: " Actions" }),
42128
- !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42666
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42667
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: " Actions" }),
42668
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42129
42669
  " ",
42130
42670
  import_chalk10.default.cyan("\u23CE"),
42131
42671
  " ",
42132
42672
  import_chalk10.default.dim("Toggle summary")
42133
42673
  ] }),
42134
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42674
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42675
+ " ",
42676
+ import_chalk10.default.cyan("space"),
42135
42677
  " ",
42136
- import_chalk10.default.cyan("e"),
42137
- " ",
42138
42678
  import_chalk10.default.dim(isDetail ? "Back to list" : "Open detail view")
42139
42679
  ] }),
42140
- !isDetail && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42680
+ !isDetail && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42141
42681
  " ",
42142
42682
  import_chalk10.default.cyan("/"),
42143
42683
  " ",
42144
42684
  import_chalk10.default.dim("Search packages")
42145
42685
  ] }),
42146
- !isDetail && onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42686
+ !isDetail && onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42147
42687
  " ",
42148
42688
  import_chalk10.default.cyan("b"),
42149
42689
  " ",
42150
42690
  import_chalk10.default.dim("Back to project selector")
42151
42691
  ] }),
42152
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42692
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42153
42693
  " ",
42154
42694
  import_chalk10.default.cyan("q"),
42155
42695
  " ",
42156
42696
  import_chalk10.default.dim("Quit")
42157
42697
  ] }),
42158
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: "" }),
42159
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42698
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42699
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42160
42700
  " Press ",
42161
42701
  import_chalk10.default.bold.cyan("?"),
42162
42702
  " or ",
@@ -42174,13 +42714,13 @@ var init_InteractiveResultsView = __esm({
42174
42714
  setDetailPane(null);
42175
42715
  } else {
42176
42716
  const dpRep = dpGroup.packages[0];
42177
- const { color: dpColor } = actionBadge3(dpRep.score, config);
42717
+ const { color: dpColor } = actionBadge3(dpRep.score);
42178
42718
  const dpScroll = detailPane.scroll;
42179
42719
  const dpAbove = dpScroll;
42180
42720
  const dpBelow = Math.max(0, detailLines.length - dpScroll - detailContentRows);
42181
42721
  const dpVisible = detailLines.slice(dpScroll, dpScroll + detailContentRows);
42182
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
42183
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
42722
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
42723
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
42184
42724
  ScoreHeader,
42185
42725
  {
42186
42726
  score: result.score,
@@ -42191,7 +42731,7 @@ var init_InteractiveResultsView = __esm({
42191
42731
  severityCounts
42192
42732
  }
42193
42733
  ),
42194
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42734
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
42195
42735
  Box_default,
42196
42736
  {
42197
42737
  flexDirection: "column",
@@ -42201,18 +42741,21 @@ var init_InteractiveResultsView = __esm({
42201
42741
  paddingRight: 1,
42202
42742
  width: "100%",
42203
42743
  children: [
42204
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42205
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: groupNames(dpGroup) }),
42206
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { children: dpColor(`score ${dpRep.score}`) })
42744
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
42745
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
42746
+ groupNames(dpGroup),
42747
+ dpRep.license ? import_chalk10.default.dim(" \xB7 ") + (dpRep.license.riskCategory === "permissive" ? import_chalk10.default.green(dpRep.license.spdx ?? dpRep.license.raw ?? "") : dpRep.license.riskCategory === "no-license" || dpRep.license.riskCategory === "network-copyleft" ? import_chalk10.default.red(dpRep.license.spdx ?? dpRep.license.raw ?? "No license") : import_chalk10.default.yellow(dpRep.license.spdx ?? dpRep.license.raw ?? "")) : ""
42748
+ ] }),
42749
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: dpColor(`score ${dpRep.score}`) })
42207
42750
  ] }),
42208
- dpAbove > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42751
+ dpAbove > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42209
42752
  import_chalk10.default.cyan(" \u2191"),
42210
42753
  " ",
42211
42754
  dpAbove,
42212
42755
  " more above"
42213
42756
  ] }),
42214
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: dpVisible }),
42215
- dpBelow > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42757
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", marginLeft: 2, children: dpVisible }),
42758
+ dpBelow > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42216
42759
  import_chalk10.default.cyan(" \u2193"),
42217
42760
  " ",
42218
42761
  dpBelow,
@@ -42221,7 +42764,7 @@ var init_InteractiveResultsView = __esm({
42221
42764
  ]
42222
42765
  }
42223
42766
  ),
42224
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42767
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
42225
42768
  Box_default,
42226
42769
  {
42227
42770
  flexDirection: "column",
@@ -42231,36 +42774,31 @@ var init_InteractiveResultsView = __esm({
42231
42774
  paddingRight: 1,
42232
42775
  width: "100%",
42233
42776
  children: [
42234
- clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42777
+ clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42235
42778
  import_chalk10.default.green("\u2713"),
42236
42779
  " ",
42237
42780
  import_chalk10.default.green.bold(String(clean.length)),
42238
42781
  " ",
42239
42782
  import_chalk10.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
42240
42783
  ] }),
42241
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42242
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42784
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
42785
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42243
42786
  (durationMs / 1e3).toFixed(1),
42244
42787
  "s"
42245
42788
  ] }),
42246
- result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42247
- result.trialScansRemaining,
42248
- " free scan",
42249
- result.trialScansRemaining !== 1 ? "s" : "",
42250
- " remaining"
42251
- ] })
42789
+ result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "Free tier \xB7 dg login for higher scan limits" })
42252
42790
  ] })
42253
42791
  ]
42254
42792
  }
42255
42793
  ),
42256
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42257
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42794
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42795
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42258
42796
  " ",
42259
42797
  import_chalk10.default.bold.cyan("\u2191\u2193"),
42260
42798
  " ",
42261
42799
  import_chalk10.default.dim("scroll"),
42262
42800
  " ",
42263
- import_chalk10.default.bold.cyan("e"),
42801
+ import_chalk10.default.bold.cyan("space"),
42264
42802
  " ",
42265
42803
  import_chalk10.default.dim("back"),
42266
42804
  " ",
@@ -42271,8 +42809,8 @@ var init_InteractiveResultsView = __esm({
42271
42809
  ] });
42272
42810
  }
42273
42811
  }
42274
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
42275
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
42812
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
42813
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
42276
42814
  ScoreHeader,
42277
42815
  {
42278
42816
  score: result.score,
@@ -42283,7 +42821,7 @@ var init_InteractiveResultsView = __esm({
42283
42821
  severityCounts
42284
42822
  }
42285
42823
  ),
42286
- groups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42824
+ groups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
42287
42825
  Box_default,
42288
42826
  {
42289
42827
  flexDirection: "column",
@@ -42293,11 +42831,11 @@ var init_InteractiveResultsView = __esm({
42293
42831
  paddingRight: 1,
42294
42832
  width: "100%",
42295
42833
  children: [
42296
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42297
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { bold: true, children: "Flagged Packages" }),
42298
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: searchQuery ? `${groups.length} of ${allGroupCount}` : `${clampedCursor + 1}/${groups.length}` })
42834
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
42835
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: "Flagged Packages" }),
42836
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: searchQuery ? `${groups.length} of ${allGroupCount}` : `${clampedCursor + 1}/${groups.length}` })
42299
42837
  ] }),
42300
- aboveCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42838
+ aboveCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42301
42839
  import_chalk10.default.cyan(" \u2191"),
42302
42840
  " ",
42303
42841
  aboveCount,
@@ -42308,24 +42846,29 @@ var init_InteractiveResultsView = __esm({
42308
42846
  const isCursor = globalIdx === clampedCursor;
42309
42847
  const level = getLevel(globalIdx);
42310
42848
  const rep = group.packages[0];
42311
- const { label, color } = actionBadge3(rep.score, config);
42849
+ const { label, color } = actionBadge3(rep.score);
42312
42850
  const names = groupNames(group);
42313
42851
  const chevron = level !== null ? "\u25BE" : "\u25B8";
42314
42852
  const scoreStr = String(rep.score);
42315
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
42316
- isCursor ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42853
+ const lcInfo = rep.license;
42854
+ const lcStr = lcInfo ? truncate3(lcInfo.spdx ?? lcInfo.raw ?? "", lcCol - 2) : "";
42855
+ const lcColor = !lcInfo ? import_chalk10.default.dim : lcInfo.riskCategory === "permissive" ? import_chalk10.default.green : lcInfo.riskCategory === "no-license" || lcInfo.riskCategory === "unlicensed" || lcInfo.riskCategory === "network-copyleft" ? import_chalk10.default.red : import_chalk10.default.yellow;
42856
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
42857
+ isCursor ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42317
42858
  import_chalk10.default.cyan("\u258C"),
42318
42859
  ` ${chevron} `,
42319
42860
  color(pad3(label, 6)),
42320
42861
  import_chalk10.default.bold(pad3(truncate3(names, nameCol - 2), nameCol)),
42862
+ lcColor(pad3(lcStr, lcCol)),
42321
42863
  color(scoreStr.padStart(3))
42322
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42864
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42323
42865
  ` ${import_chalk10.default.dim(chevron)} `,
42324
42866
  color(pad3(label, 6)),
42325
42867
  pad3(truncate3(names, nameCol - 2), nameCol),
42868
+ lcColor(pad3(lcStr, lcCol)),
42326
42869
  color(scoreStr.padStart(3))
42327
42870
  ] }),
42328
- level === "summary" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
42871
+ level === "summary" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
42329
42872
  FindingsSummary,
42330
42873
  {
42331
42874
  group,
@@ -42335,7 +42878,7 @@ var init_InteractiveResultsView = __esm({
42335
42878
  )
42336
42879
  ] }, group.key);
42337
42880
  }),
42338
- belowCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42881
+ belowCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42339
42882
  import_chalk10.default.cyan(" \u2193"),
42340
42883
  " ",
42341
42884
  belowCount,
@@ -42344,7 +42887,7 @@ var init_InteractiveResultsView = __esm({
42344
42887
  ]
42345
42888
  }
42346
42889
  ),
42347
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
42890
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
42348
42891
  Box_default,
42349
42892
  {
42350
42893
  flexDirection: "column",
@@ -42354,38 +42897,33 @@ var init_InteractiveResultsView = __esm({
42354
42897
  paddingRight: 1,
42355
42898
  width: "100%",
42356
42899
  children: [
42357
- clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42900
+ clean.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42358
42901
  import_chalk10.default.green("\u2713"),
42359
42902
  " ",
42360
42903
  import_chalk10.default.green.bold(String(clean.length)),
42361
42904
  " ",
42362
42905
  import_chalk10.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)
42363
42906
  ] }),
42364
- discoveredTotal !== void 0 && discoveredTotal > total && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42907
+ discoveredTotal !== void 0 && discoveredTotal > total && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42365
42908
  "Scanned ",
42366
42909
  total,
42367
42910
  " of ",
42368
42911
  discoveredTotal,
42369
42912
  " packages ",
42370
- import_chalk10.default.dim("\u2014 dg login for full scans")
42913
+ import_chalk10.default.dim("\u2014 increase --max-packages for full scans")
42371
42914
  ] }),
42372
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { justifyContent: "space-between", children: [
42373
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42915
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { justifyContent: "space-between", children: [
42916
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42374
42917
  (durationMs / 1e3).toFixed(1),
42375
42918
  "s"
42376
42919
  ] }),
42377
- result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42378
- result.trialScansRemaining,
42379
- " free scan",
42380
- result.trialScansRemaining !== 1 ? "s" : "",
42381
- " remaining"
42382
- ] })
42920
+ result.trialScansRemaining !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "Free tier \xB7 dg login for higher scan limits" })
42383
42921
  ] })
42384
42922
  ]
42385
42923
  }
42386
42924
  ),
42387
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42388
- searchMode ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42925
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: import_chalk10.default.dim("\u2500".repeat(Math.min(60, termCols - 4))) }),
42926
+ searchMode ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42389
42927
  " ",
42390
42928
  import_chalk10.default.bold.cyan("/"),
42391
42929
  " ",
@@ -42393,9 +42931,9 @@ var init_InteractiveResultsView = __esm({
42393
42931
  import_chalk10.default.cyan("\u2588"),
42394
42932
  " ",
42395
42933
  import_chalk10.default.dim("Esc clear")
42396
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
42934
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42397
42935
  " ",
42398
- groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
42936
+ groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
42399
42937
  import_chalk10.default.bold.cyan("\u2191\u2193"),
42400
42938
  " ",
42401
42939
  import_chalk10.default.dim("navigate"),
@@ -42404,7 +42942,7 @@ var init_InteractiveResultsView = __esm({
42404
42942
  " ",
42405
42943
  import_chalk10.default.dim("toggle"),
42406
42944
  " ",
42407
- import_chalk10.default.bold.cyan("e"),
42945
+ import_chalk10.default.bold.cyan("space"),
42408
42946
  " ",
42409
42947
  import_chalk10.default.dim("detail"),
42410
42948
  " ",
@@ -42416,7 +42954,7 @@ var init_InteractiveResultsView = __esm({
42416
42954
  " ",
42417
42955
  import_chalk10.default.dim("help"),
42418
42956
  " ",
42419
- onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
42957
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
42420
42958
  import_chalk10.default.bold.cyan("b"),
42421
42959
  " ",
42422
42960
  import_chalk10.default.dim("back"),
@@ -42425,7 +42963,7 @@ var init_InteractiveResultsView = __esm({
42425
42963
  import_chalk10.default.bold.cyan("q"),
42426
42964
  " ",
42427
42965
  import_chalk10.default.dim("quit")
42428
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
42966
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
42429
42967
  "Press ",
42430
42968
  import_chalk10.default.bold.cyan("q"),
42431
42969
  " or ",
@@ -42442,40 +42980,43 @@ var init_InteractiveResultsView = __esm({
42442
42980
  pipe: import_chalk10.default.dim("\u2502"),
42443
42981
  blank: " "
42444
42982
  };
42983
+ LICENSE_DESCRIPTIONS = {
42984
+ "permissive": "Permissive \u2014 free to use, modify, and distribute. Include the copyright notice.",
42985
+ "weak-copyleft": "Weak copyleft \u2014 changes to this library must be shared, but your code stays private.",
42986
+ "strong-copyleft": "Strong copyleft \u2014 your entire project must be open-sourced under the same license.",
42987
+ "network-copyleft": "Network copyleft \u2014 even SaaS/server use requires releasing your source code.",
42988
+ "no-license": "No license found \u2014 legally all rights reserved. Use may require permission from the author.",
42989
+ "unlicensed": "Explicitly unlicensed \u2014 proprietary software. A commercial agreement is required.",
42990
+ "unknown": "Unrecognized license \u2014 have your legal team review before using.",
42991
+ "deferred": "License declared in a file \u2014 check the LICENSE file in the package."
42992
+ };
42445
42993
  FindingsSummary = ({ group, maxWidth, maxLines }) => {
42446
42994
  const rep = group.packages[0];
42447
42995
  const visibleFindings = rep.findings.filter((f) => f.severity > 1 || f.critical).sort((a, b) => b.severity - a.severity);
42448
42996
  const hasAffects = group.packages.length > 3;
42449
42997
  const allLines = [];
42998
+ const lcLine = licenseLine(rep);
42999
+ if (lcLine) allLines.push(lcLine);
42450
43000
  if (visibleFindings.length === 0 && rep.score > 0) {
42451
43001
  const reasons = rep.reasons ?? [];
42452
43002
  for (let i = 0; i < reasons.length; i++) {
42453
43003
  allLines.push(
42454
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
43004
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42455
43005
  i < reasons.length - 1 || hasAffects ? T.branch : T.branch,
42456
43006
  " ",
42457
43007
  truncate3(reasons[i], maxWidth - 8)
42458
43008
  ] }, `reason-${i}`)
42459
43009
  );
42460
43010
  }
42461
- allLines.push(
42462
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
42463
- import_chalk10.default.yellow(" \u2192"),
42464
- " ",
42465
- import_chalk10.default.yellow("Upgrade to Pro"),
42466
- " to see finding details ",
42467
- import_chalk10.default.dim("\u2014 dg login")
42468
- ] }, "upgrade")
42469
- );
42470
43011
  }
42471
43012
  for (let idx = 0; idx < visibleFindings.length; idx++) {
42472
43013
  const f = visibleFindings[idx];
42473
43014
  const isLast = !hasAffects && idx === visibleFindings.length - 1;
42474
43015
  const connector = isLast ? T.last : T.branch;
42475
- const sevLabel = SEVERITY_LABELS3[f.severity] ?? "INFO";
43016
+ const sevLabel = SEVERITY_LABELS2[f.severity] ?? "INFO";
42476
43017
  const sevColor = SEVERITY_COLORS[f.severity] ?? SEVERITY_COLORS[1];
42477
43018
  allLines.push(
42478
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
43019
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
42479
43020
  connector,
42480
43021
  " ",
42481
43022
  sevColor(pad3(sevLabel, 5)),
@@ -42486,7 +43027,7 @@ var init_InteractiveResultsView = __esm({
42486
43027
  }
42487
43028
  if (hasAffects) {
42488
43029
  allLines.push(
42489
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
43030
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
42490
43031
  T.last,
42491
43032
  " ",
42492
43033
  truncate3(affectsLine(group), maxWidth - 8)
@@ -42494,24 +43035,24 @@ var init_InteractiveResultsView = __esm({
42494
43035
  );
42495
43036
  }
42496
43037
  const linesToShow = maxLines !== void 0 ? allLines.slice(0, maxLines) : allLines;
42497
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { flexDirection: "column", marginLeft: 5, children: linesToShow });
43038
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", marginLeft: 5, children: linesToShow });
42498
43039
  };
42499
43040
  }
42500
43041
  });
42501
43042
 
42502
43043
  // src/ui/components/ProjectSelector.tsx
42503
- var import_react32, import_chalk11, import_jsx_runtime11, ProjectSelector;
43044
+ var import_react34, import_chalk11, import_jsx_runtime12, ProjectSelector;
42504
43045
  var init_ProjectSelector = __esm({
42505
43046
  async "src/ui/components/ProjectSelector.tsx"() {
42506
43047
  "use strict";
42507
- import_react32 = __toESM(require_react());
43048
+ import_react34 = __toESM(require_react());
42508
43049
  await init_build2();
42509
43050
  import_chalk11 = __toESM(require_source());
42510
43051
  init_sanitize();
42511
- import_jsx_runtime11 = __toESM(require_jsx_runtime());
43052
+ import_jsx_runtime12 = __toESM(require_jsx_runtime());
42512
43053
  ProjectSelector = ({ projects, onConfirm, onCancel }) => {
42513
- const [cursor, setCursor] = (0, import_react32.useState)(0);
42514
- const [selected, setSelected] = (0, import_react32.useState)(() => new Set(projects.map((_, i) => i)));
43054
+ const [cursor, setCursor] = (0, import_react34.useState)(0);
43055
+ const [selected, setSelected] = (0, import_react34.useState)(() => new Set(projects.map((_, i) => i)));
42515
43056
  use_input_default((input, key) => {
42516
43057
  if (key.upArrow) {
42517
43058
  setCursor((c) => Math.max(0, c - 1));
@@ -42537,26 +43078,26 @@ var init_ProjectSelector = __esm({
42537
43078
  }
42538
43079
  });
42539
43080
  const ecosystemLabel = (eco) => eco === "npm" ? import_chalk11.default.magenta("npm") : import_chalk11.default.blue("pip");
42540
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
42541
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
43081
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
43082
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
42542
43083
  import_chalk11.default.cyan("\u25C6"),
42543
43084
  " ",
42544
43085
  import_chalk11.default.bold("Dependency Guardian")
42545
43086
  ] }),
42546
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42547
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
43087
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: "" }),
43088
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, children: [
42548
43089
  "Found ",
42549
43090
  projects.length,
42550
43091
  " project",
42551
43092
  projects.length !== 1 ? "s" : ""
42552
43093
  ] }),
42553
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
43094
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: "" }),
42554
43095
  projects.map((proj, i) => {
42555
43096
  const isCursor = i === cursor;
42556
43097
  const isSelected = selected.has(i);
42557
43098
  const prefix = isCursor ? import_chalk11.default.cyan("\u258C") : " ";
42558
43099
  const check = isSelected ? import_chalk11.default.green("\u25C9") : import_chalk11.default.dim("\u25CB");
42559
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
43100
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
42560
43101
  prefix,
42561
43102
  check,
42562
43103
  " ",
@@ -42568,10 +43109,10 @@ var init_ProjectSelector = __esm({
42568
43109
  " packages"
42569
43110
  ] }, i);
42570
43111
  }),
42571
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42572
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: selected.size === 0 ? import_chalk11.default.yellow("Select at least 1 project to scan") : `${selected.size} of ${projects.length} selected` }),
42573
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
42574
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
43112
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: "" }),
43113
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: selected.size === 0 ? import_chalk11.default.yellow("Select at least 1 project to scan") : `${selected.size} of ${projects.length} selected` }),
43114
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: "" }),
43115
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
42575
43116
  import_chalk11.default.bold.cyan("space"),
42576
43117
  " ",
42577
43118
  import_chalk11.default.dim("toggle"),
@@ -42580,9 +43121,9 @@ var init_ProjectSelector = __esm({
42580
43121
  " ",
42581
43122
  import_chalk11.default.dim("all"),
42582
43123
  " ",
42583
- import_chalk11.default.bold.cyan("\u23CE"),
43124
+ import_chalk11.default.bold.hex("#FFD700")("\u23CE"),
42584
43125
  " ",
42585
- import_chalk11.default.dim("scan"),
43126
+ import_chalk11.default.bold.hex("#FFD700")("scan"),
42586
43127
  " ",
42587
43128
  import_chalk11.default.bold.cyan("q"),
42588
43129
  " ",
@@ -42598,11 +43139,11 @@ var App_exports = {};
42598
43139
  __export(App_exports, {
42599
43140
  App: () => App2
42600
43141
  });
42601
- var import_react33, import_jsx_runtime12, App2;
43142
+ var import_react35, import_jsx_runtime13, App2;
42602
43143
  var init_App2 = __esm({
42603
43144
  async "src/ui/App.tsx"() {
42604
43145
  "use strict";
42605
- import_react33 = __toESM(require_react());
43146
+ import_react35 = __toESM(require_react());
42606
43147
  await init_build2();
42607
43148
  init_useScan();
42608
43149
  await init_Spinner();
@@ -42611,17 +43152,19 @@ var init_App2 = __esm({
42611
43152
  await init_ErrorView();
42612
43153
  await init_ProjectSelector();
42613
43154
  await init_useTerminalSize();
42614
- import_jsx_runtime12 = __toESM(require_jsx_runtime());
43155
+ import_jsx_runtime13 = __toESM(require_jsx_runtime());
42615
43156
  App2 = ({ config }) => {
42616
43157
  const { state, scanSelectedProjects, restartSelection } = useScan(config);
42617
43158
  const { exit } = use_app_default();
42618
43159
  useTerminalSize();
42619
- const prevPhaseRef = (0, import_react33.useRef)(state.phase);
42620
- const altScreenActiveRef = (0, import_react33.useRef)(false);
42621
- (0, import_react33.useEffect)(() => {
43160
+ const prevPhaseRef = (0, import_react35.useRef)(state.phase);
43161
+ const altScreenActiveRef = (0, import_react35.useRef)(false);
43162
+ (0, import_react35.useEffect)(() => {
42622
43163
  if (!process.stdout.isTTY) return;
42623
43164
  process.stdout.write("\x1B[?1049h");
42624
43165
  process.stdout.write("\x1B[2J\x1B[H");
43166
+ process.stdout.write("\x1B[?1003l");
43167
+ process.stdout.write("\x1B[?1000l");
42625
43168
  altScreenActiveRef.current = true;
42626
43169
  return () => {
42627
43170
  if (altScreenActiveRef.current) {
@@ -42631,20 +43174,20 @@ var init_App2 = __esm({
42631
43174
  process.stdout.write("\x1B[?25h");
42632
43175
  };
42633
43176
  }, []);
42634
- (0, import_react33.useEffect)(() => {
43177
+ (0, import_react35.useEffect)(() => {
42635
43178
  if (prevPhaseRef.current !== state.phase && process.stdout.isTTY) {
42636
43179
  process.stdout.write("\x1B[2J\x1B[H");
42637
43180
  }
42638
43181
  prevPhaseRef.current = state.phase;
42639
43182
  }, [state.phase]);
42640
- const leaveAltScreen = (0, import_react33.useCallback)(() => {
43183
+ const leaveAltScreen = (0, import_react35.useCallback)(() => {
42641
43184
  if (altScreenActiveRef.current && process.stdout.isTTY) {
42642
43185
  process.stdout.write("\x1B[?1049l");
42643
43186
  altScreenActiveRef.current = false;
42644
43187
  }
42645
43188
  process.stdout.write("\x1B[?25h");
42646
43189
  }, []);
42647
- const handleResultsExit = (0, import_react33.useCallback)(() => {
43190
+ const handleResultsExit = (0, import_react35.useCallback)(() => {
42648
43191
  if (state.phase === "results") {
42649
43192
  const { result } = state;
42650
43193
  if (result.action === "block" && config.mode === "block") {
@@ -42658,13 +43201,13 @@ var init_App2 = __esm({
42658
43201
  leaveAltScreen();
42659
43202
  exit();
42660
43203
  }, [state, config, exit, leaveAltScreen]);
42661
- const exitWithMessage = (0, import_react33.useCallback)((message, exitCode) => {
43204
+ const exitWithMessage = (0, import_react35.useCallback)((message, exitCode) => {
42662
43205
  process.exitCode = exitCode;
42663
43206
  leaveAltScreen();
42664
43207
  process.stderr.write(message);
42665
43208
  return setTimeout(() => exit(), 0);
42666
43209
  }, [exit, leaveAltScreen]);
42667
- (0, import_react33.useEffect)(() => {
43210
+ (0, import_react35.useEffect)(() => {
42668
43211
  if (state.phase === "empty") {
42669
43212
  const timer = exitWithMessage(`${state.message}
42670
43213
  `, 0);
@@ -42676,10 +43219,15 @@ var init_App2 = __esm({
42676
43219
  return () => clearTimeout(timer);
42677
43220
  }
42678
43221
  if (state.phase === "trial_exhausted") {
42679
- const timer = exitWithMessage(
42680
- "Free trial scans used up. Run `dg login` to create a free account and continue scanning.\n",
42681
- 1
42682
- );
43222
+ let msg = "Free trial scans used up. Run `dg login` to create a free account and continue scanning.\n";
43223
+ try {
43224
+ const { getStoredApiKey: getStoredApiKey2 } = (init_auth(), __toCommonJS(auth_exports));
43225
+ if (getStoredApiKey2()) {
43226
+ msg = "Your API key may be invalid or expired. Run `dg logout` then `dg login` to re-authenticate.\n";
43227
+ }
43228
+ } catch {
43229
+ }
43230
+ const timer = exitWithMessage(msg, 1);
42683
43231
  return () => clearTimeout(timer);
42684
43232
  }
42685
43233
  }, [state, exitWithMessage]);
@@ -42695,9 +43243,9 @@ var init_App2 = __esm({
42695
43243
  const content = (() => {
42696
43244
  switch (state.phase) {
42697
43245
  case "discovering":
42698
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner2, { label: `Searching for dependencies in ${process.cwd()} ...` });
43246
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner2, { label: `Searching for dependencies in ${process.cwd()} ...` });
42699
43247
  case "selecting":
42700
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
43248
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
42701
43249
  ProjectSelector,
42702
43250
  {
42703
43251
  projects: state.projects,
@@ -42710,7 +43258,7 @@ var init_App2 = __esm({
42710
43258
  }
42711
43259
  );
42712
43260
  case "scanning":
42713
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
43261
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
42714
43262
  ProgressBar,
42715
43263
  {
42716
43264
  value: state.done,
@@ -42719,7 +43267,7 @@ var init_App2 = __esm({
42719
43267
  }
42720
43268
  );
42721
43269
  case "results":
42722
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
43270
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
42723
43271
  InteractiveResultsView,
42724
43272
  {
42725
43273
  result: state.result,
@@ -42731,21 +43279,37 @@ var init_App2 = __esm({
42731
43279
  }
42732
43280
  );
42733
43281
  case "empty":
42734
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
43282
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: state.message });
42735
43283
  case "error":
42736
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorView, { error: state.error });
42737
- case "trial_exhausted":
42738
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
42739
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
42740
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
43284
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorView, { error: state.error });
43285
+ case "trial_exhausted": {
43286
+ let hasKey = false;
43287
+ try {
43288
+ const { getStoredApiKey: getStoredApiKey2 } = (init_auth(), __toCommonJS(auth_exports));
43289
+ hasKey = !!getStoredApiKey2();
43290
+ } catch {
43291
+ }
43292
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexDirection: "column", paddingLeft: 2, children: hasKey ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
43293
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "yellow", bold: true, children: "Your API key may be invalid or expired." }),
43294
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
43295
+ "Run ",
43296
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "cyan", bold: true, children: "dg logout" }),
43297
+ " then ",
43298
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
43299
+ " to re-authenticate."
43300
+ ] })
43301
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
43302
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
43303
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { children: [
42741
43304
  "Run ",
42742
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
43305
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
42743
43306
  " to create a free account and continue scanning."
42744
43307
  ] })
42745
- ] });
43308
+ ] }) });
43309
+ }
42746
43310
  }
42747
43311
  })();
42748
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", children: content });
43312
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexDirection: "column", children: content });
42749
43313
  };
42750
43314
  }
42751
43315
  });
@@ -42876,6 +43440,23 @@ async function runUpdate(currentVersion) {
42876
43440
 
42877
43441
  // src/bin.ts
42878
43442
  var CLI_VERSION = getVersion();
43443
+ function closestCommand(input, commands) {
43444
+ let best = "", bestDist = Infinity;
43445
+ for (const cmd of commands) {
43446
+ const m = input.length, n = cmd.length;
43447
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1));
43448
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
43449
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
43450
+ for (let i = 1; i <= m; i++)
43451
+ for (let j = 1; j <= n; j++)
43452
+ dp[i][j] = input[i - 1] === cmd[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
43453
+ if (dp[m][n] < bestDist) {
43454
+ bestDist = dp[m][n];
43455
+ best = cmd;
43456
+ }
43457
+ }
43458
+ return bestDist <= 3 ? best : null;
43459
+ }
42879
43460
  process.on("SIGINT", () => {
42880
43461
  process.stderr.write("\n");
42881
43462
  process.exit(130);
@@ -42900,9 +43481,9 @@ async function main() {
42900
43481
  if (rawCommand === "login") {
42901
43482
  if (isInteractive) {
42902
43483
  const { render: render3 } = await init_build2().then(() => build_exports);
42903
- const React18 = await Promise.resolve().then(() => __toESM(require_react()));
43484
+ const React19 = await Promise.resolve().then(() => __toESM(require_react()));
42904
43485
  const { LoginApp: LoginApp2 } = await init_LoginApp().then(() => LoginApp_exports);
42905
- const { waitUntilExit } = render3(React18.createElement(LoginApp2));
43486
+ const { waitUntilExit } = render3(React19.createElement(LoginApp2));
42906
43487
  await waitUntilExit();
42907
43488
  } else {
42908
43489
  const { runStaticLogin: runStaticLogin2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
@@ -42910,6 +43491,19 @@ async function main() {
42910
43491
  }
42911
43492
  return;
42912
43493
  }
43494
+ if (rawCommand === "status") {
43495
+ const { getStoredApiKey: getStoredApiKey2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
43496
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
43497
+ const apiKey = getStoredApiKey2();
43498
+ if (apiKey) {
43499
+ process.stderr.write(chalk10.green(` Authenticated`) + chalk10.dim(` (key: ${apiKey.slice(0, 12)}...)
43500
+ `));
43501
+ } else {
43502
+ process.stderr.write(chalk10.yellow(` Not authenticated.`) + chalk10.dim(` Run \`dg login\` to sign in.
43503
+ `));
43504
+ }
43505
+ return;
43506
+ }
42913
43507
  if (rawCommand === "hook") {
42914
43508
  const { handleHookCommand: handleHookCommand2 } = await Promise.resolve().then(() => (init_hook(), hook_exports));
42915
43509
  handleHookCommand2(process.argv.slice(3));
@@ -42920,18 +43514,46 @@ async function main() {
42920
43514
  return;
42921
43515
  }
42922
43516
  if (rawCommand === "logout") {
42923
- const { clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
42924
- clearCredentials2();
43517
+ const { getStoredApiKey: getStoredApiKey2, clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
42925
43518
  const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
43519
+ const apiKey = getStoredApiKey2();
43520
+ if (apiKey) {
43521
+ try {
43522
+ await fetch("https://api.westbayberry.com/v1/auth/revoke", {
43523
+ method: "POST",
43524
+ headers: { Authorization: `Bearer ${apiKey}` },
43525
+ signal: AbortSignal.timeout(5e3)
43526
+ });
43527
+ } catch {
43528
+ }
43529
+ }
43530
+ clearCredentials2();
42926
43531
  process.stderr.write(chalk10.green(" Logged out.\n"));
42927
43532
  return;
42928
43533
  }
42929
- const config = parseConfig(process.argv);
43534
+ const KNOWN_COMMANDS = ["scan", "npm", "pip", "wrap", "login", "logout", "status", "hook", "update"];
43535
+ if (rawCommand && !rawCommand.startsWith("-") && !KNOWN_COMMANDS.includes(rawCommand)) {
43536
+ const chalk10 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
43537
+ const best = closestCommand(rawCommand, KNOWN_COMMANDS);
43538
+ const hint = best ? ` Did you mean '${best}'?` : "";
43539
+ process.stderr.write(`
43540
+ ${chalk10.bold.red("Error:")} Unknown command '${rawCommand}'.${hint}
43541
+ `);
43542
+ process.stderr.write(chalk10.dim(` Run 'dg --help' for available commands.
43543
+
43544
+ `));
43545
+ process.exit(1);
43546
+ }
43547
+ const strictFlags = rawCommand !== "npm" && rawCommand !== "pip";
43548
+ const config = parseConfig(process.argv, strictFlags);
42930
43549
  const updatePromise = checkForUpdate(CLI_VERSION).catch(() => null);
42931
43550
  if (config.json || !isInteractive) {
42932
43551
  if (rawCommand === "npm") {
42933
43552
  const { runStaticNpm: runStaticNpm2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
42934
43553
  await runStaticNpm2(process.argv.slice(3), config);
43554
+ } else if (rawCommand === "pip") {
43555
+ const { runStaticPip: runStaticPip2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
43556
+ await runStaticPip2(process.argv.slice(3), config);
42935
43557
  } else {
42936
43558
  const { runStatic: runStatic2 } = await Promise.resolve().then(() => (init_static_output(), static_output_exports));
42937
43559
  await runStatic2(config);
@@ -42944,11 +43566,17 @@ async function main() {
42944
43566
  return;
42945
43567
  }
42946
43568
  const { render: render2 } = await init_build2().then(() => build_exports);
42947
- const React17 = await Promise.resolve().then(() => __toESM(require_react()));
43569
+ const React18 = await Promise.resolve().then(() => __toESM(require_react()));
42948
43570
  if (rawCommand === "npm") {
42949
43571
  const { NpmWrapperApp: NpmWrapperApp2 } = await init_NpmWrapperApp().then(() => NpmWrapperApp_exports);
42950
43572
  const { waitUntilExit } = render2(
42951
- React17.createElement(NpmWrapperApp2, { config, npmArgs: process.argv.slice(3) })
43573
+ React18.createElement(NpmWrapperApp2, { config, npmArgs: process.argv.slice(3) })
43574
+ );
43575
+ await waitUntilExit();
43576
+ } else if (rawCommand === "pip") {
43577
+ const { PipWrapperApp: PipWrapperApp2 } = await init_PipWrapperApp().then(() => PipWrapperApp_exports);
43578
+ const { waitUntilExit } = render2(
43579
+ React18.createElement(PipWrapperApp2, { config, pipArgs: process.argv.slice(3) })
42952
43580
  );
42953
43581
  await waitUntilExit();
42954
43582
  } else {
@@ -42959,7 +43587,7 @@ async function main() {
42959
43587
  }
42960
43588
  const { App: App3 } = await init_App2().then(() => App_exports);
42961
43589
  const { waitUntilExit } = render2(
42962
- React17.createElement(App3, { config })
43590
+ React18.createElement(App3, { config })
42963
43591
  );
42964
43592
  await waitUntilExit();
42965
43593
  }
@@ -42970,17 +43598,25 @@ async function main() {
42970
43598
  }
42971
43599
  }
42972
43600
  main().catch((err) => {
42973
- try {
42974
- const chalk10 = require_source();
42975
- process.stderr.write(`
43601
+ if (process.argv.includes("--json")) {
43602
+ process.stdout.write(JSON.stringify({
43603
+ error: true,
43604
+ code: err.statusCode ? "api_error" : "internal_error",
43605
+ message: err.message
43606
+ }, null, 2) + "\n");
43607
+ } else {
43608
+ try {
43609
+ const chalk10 = require_source();
43610
+ process.stderr.write(`
42976
43611
  ${chalk10.bold.red("Error:")} ${err.message}
42977
43612
 
42978
43613
  `);
42979
- } catch {
42980
- process.stderr.write(`
43614
+ } catch {
43615
+ process.stderr.write(`
42981
43616
  Error: ${err.message}
42982
43617
 
42983
43618
  `);
43619
+ }
42984
43620
  }
42985
43621
  process.exit(3);
42986
43622
  });