@westbayberry/dg 1.0.16 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +320 -262
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -39,6 +39,149 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  mod
40
40
  ));
41
41
 
42
+ // src/auth.ts
43
+ var auth_exports = {};
44
+ __export(auth_exports, {
45
+ clearCredentials: () => clearCredentials,
46
+ createAuthSession: () => createAuthSession,
47
+ getOrCreateDeviceId: () => getOrCreateDeviceId,
48
+ getStoredApiKey: () => getStoredApiKey,
49
+ openBrowser: () => openBrowser,
50
+ pollAuthSession: () => pollAuthSession,
51
+ saveCredentials: () => saveCredentials
52
+ });
53
+ import { readFileSync, writeFileSync, chmodSync, unlinkSync, existsSync } from "node:fs";
54
+ import { join } from "node:path";
55
+ import { homedir } from "node:os";
56
+ import { exec } from "node:child_process";
57
+ import { randomUUID } from "node:crypto";
58
+ async function createAuthSession() {
59
+ let res;
60
+ try {
61
+ res = await globalThis.fetch(`${WEB_BASE}/cli/auth/sessions`, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" }
64
+ });
65
+ } catch {
66
+ throw new Error("Could not connect to westbayberry.com");
67
+ }
68
+ if (!res.ok) {
69
+ const body = await res.text().catch(() => "");
70
+ throw new Error(
71
+ `Failed to create auth session (HTTP ${res.status})${body ? `: ${body}` : ""}`
72
+ );
73
+ }
74
+ const json = await res.json();
75
+ return {
76
+ sessionId: json.session_id,
77
+ verifyUrl: json.verify_url,
78
+ expiresIn: json.expires_in
79
+ };
80
+ }
81
+ async function pollAuthSession(sessionId) {
82
+ let res;
83
+ try {
84
+ res = await globalThis.fetch(
85
+ `${WEB_BASE}/cli/auth/sessions/${sessionId}/token`
86
+ );
87
+ } catch {
88
+ return { status: "expired" };
89
+ }
90
+ if (res.status === 404) {
91
+ return { status: "expired" };
92
+ }
93
+ if (!res.ok) {
94
+ return { status: "expired" };
95
+ }
96
+ const json = await res.json();
97
+ return {
98
+ status: json.status,
99
+ apiKey: json.api_key,
100
+ email: json.email
101
+ };
102
+ }
103
+ function configPath() {
104
+ return join(homedir(), CONFIG_FILE);
105
+ }
106
+ function readConfig() {
107
+ try {
108
+ const raw = readFileSync(configPath(), "utf-8");
109
+ const parsed = JSON.parse(raw);
110
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
111
+ return parsed;
112
+ }
113
+ return {};
114
+ } catch {
115
+ return {};
116
+ }
117
+ }
118
+ function saveCredentials(apiKey) {
119
+ const data = readConfig();
120
+ data.apiKey = apiKey;
121
+ const p = configPath();
122
+ writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
123
+ chmodSync(p, 384);
124
+ }
125
+ function clearCredentials() {
126
+ const p = configPath();
127
+ if (!existsSync(p)) return;
128
+ const data = readConfig();
129
+ delete data.apiKey;
130
+ if (Object.keys(data).length === 0) {
131
+ unlinkSync(p);
132
+ } else {
133
+ writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
134
+ chmodSync(p, 384);
135
+ }
136
+ }
137
+ function getStoredApiKey() {
138
+ const data = readConfig();
139
+ if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
140
+ return data.apiKey;
141
+ }
142
+ return null;
143
+ }
144
+ function getOrCreateDeviceId() {
145
+ const data = readConfig();
146
+ if (typeof data.deviceId === "string" && data.deviceId.length > 0) {
147
+ return data.deviceId;
148
+ }
149
+ const deviceId = randomUUID();
150
+ data.deviceId = deviceId;
151
+ const p = configPath();
152
+ writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
153
+ chmodSync(p, 384);
154
+ return deviceId;
155
+ }
156
+ function openBrowser(url) {
157
+ if (!/^https?:\/\//i.test(url)) return;
158
+ const escaped = url.replace(/"/g, '\\"');
159
+ let cmd;
160
+ switch (process.platform) {
161
+ case "darwin":
162
+ cmd = `open "${escaped}"`;
163
+ break;
164
+ case "linux":
165
+ cmd = `xdg-open "${escaped}"`;
166
+ break;
167
+ case "win32":
168
+ cmd = `start "" "${escaped}"`;
169
+ break;
170
+ default:
171
+ return;
172
+ }
173
+ exec(cmd, () => {
174
+ });
175
+ }
176
+ var WEB_BASE, CONFIG_FILE;
177
+ var init_auth = __esm({
178
+ "src/auth.ts"() {
179
+ "use strict";
180
+ WEB_BASE = "https://westbayberry.com";
181
+ CONFIG_FILE = ".dgrc.json";
182
+ }
183
+ });
184
+
42
185
  // src/config.ts
43
186
  var config_exports = {};
44
187
  __export(config_exports, {
@@ -47,19 +190,19 @@ __export(config_exports, {
47
190
  parseConfig: () => parseConfig
48
191
  });
49
192
  import { parseArgs } from "node:util";
50
- import { readFileSync, existsSync } from "node:fs";
51
- import { join, dirname } from "node:path";
193
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
194
+ import { join as join2, dirname } from "node:path";
52
195
  import { fileURLToPath } from "node:url";
53
- import { homedir } from "node:os";
196
+ import { homedir as homedir2 } from "node:os";
54
197
  function loadDgrc() {
55
198
  const candidates = [
56
- join(process.cwd(), ".dgrc.json"),
57
- join(homedir(), ".dgrc.json")
199
+ join2(process.cwd(), ".dgrc.json"),
200
+ join2(homedir2(), ".dgrc.json")
58
201
  ];
59
202
  for (const filepath of candidates) {
60
- if (existsSync(filepath)) {
203
+ if (existsSync2(filepath)) {
61
204
  try {
62
- return JSON.parse(readFileSync(filepath, "utf-8"));
205
+ return JSON.parse(readFileSync2(filepath, "utf-8"));
63
206
  } catch {
64
207
  process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.
65
208
  `);
@@ -72,7 +215,7 @@ function getVersion() {
72
215
  try {
73
216
  const thisDir = dirname(fileURLToPath(import.meta.url));
74
217
  const pkg = JSON.parse(
75
- readFileSync(join(thisDir, "..", "package.json"), "utf-8")
218
+ readFileSync2(join2(thisDir, "..", "package.json"), "utf-8")
76
219
  );
77
220
  return pkg.version ?? "1.0.0";
78
221
  } catch {
@@ -113,7 +256,8 @@ function parseConfig(argv) {
113
256
  const command = positionals[0] ?? "scan";
114
257
  const noConfig = values["no-config"];
115
258
  const dgrc = noConfig ? {} : loadDgrc();
116
- const apiKey = dgrc.apiKey ?? "";
259
+ const apiKey = dgrc.apiKey && dgrc.apiKey.length > 0 ? dgrc.apiKey : null;
260
+ const deviceId = getOrCreateDeviceId();
117
261
  const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
118
262
  if (!["block", "warn", "off"].includes(modeRaw)) {
119
263
  process.stderr.write(
@@ -125,10 +269,7 @@ function parseConfig(argv) {
125
269
  const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
126
270
  const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
127
271
  const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
128
- let maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
129
- if (!apiKey) {
130
- maxPackages = Math.min(maxPackages, 50);
131
- }
272
+ const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
132
273
  const debug = values.debug || process.env.DG_DEBUG === "1";
133
274
  if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
134
275
  process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
@@ -144,6 +285,7 @@ function parseConfig(argv) {
144
285
  }
145
286
  return {
146
287
  apiKey,
288
+ deviceId,
147
289
  apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
148
290
  mode: modeRaw,
149
291
  blockThreshold,
@@ -162,6 +304,7 @@ var USAGE;
162
304
  var init_config = __esm({
163
305
  "src/config.ts"() {
164
306
  "use strict";
307
+ init_auth();
165
308
  USAGE = `
166
309
  Dependency Guardian \u2014 Supply chain security scanner
167
310
 
@@ -228,8 +371,8 @@ var init_config = __esm({
228
371
 
229
372
  // src/npm-wrapper.ts
230
373
  import { spawn } from "node:child_process";
231
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
232
- import { join as join2 } from "node:path";
374
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
375
+ import { join as join3 } from "node:path";
233
376
  function parseNpmArgs(args) {
234
377
  let dgForce = false;
235
378
  const filtered = [];
@@ -375,10 +518,10 @@ function runNpm(args) {
375
518
  });
376
519
  }
377
520
  function readBareInstallPackages(cwd2) {
378
- const pkgPath = join2(cwd2, "package.json");
379
- if (!existsSync2(pkgPath)) return [];
521
+ const pkgPath = join3(cwd2, "package.json");
522
+ if (!existsSync3(pkgPath)) return [];
380
523
  try {
381
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
524
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
382
525
  const specs = [];
383
526
  for (const [name, range] of Object.entries(pkg.dependencies ?? {})) {
384
527
  if (typeof range === "string") specs.push(`${name}@${range}`);
@@ -27445,9 +27588,9 @@ var init_ansi_styles = __esm({
27445
27588
 
27446
27589
  // node_modules/wrap-ansi/index.js
27447
27590
  function wrapAnsi(string, columns, options) {
27448
- return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) => exec(line, columns, options)).join("\n");
27591
+ return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) => exec2(line, columns, options)).join("\n");
27449
27592
  }
27450
- var ESCAPES, END_CODE, ANSI_ESCAPE_BELL, ANSI_CSI, ANSI_OSC, ANSI_SGR_TERMINATOR, ANSI_ESCAPE_LINK, wrapAnsiCode, wrapAnsiHyperlink, wordLengths, wrapWord, stringVisibleTrimSpacesRight, exec;
27593
+ var ESCAPES, END_CODE, ANSI_ESCAPE_BELL, ANSI_CSI, ANSI_OSC, ANSI_SGR_TERMINATOR, ANSI_ESCAPE_LINK, wrapAnsiCode, wrapAnsiHyperlink, wordLengths, wrapWord, stringVisibleTrimSpacesRight, exec2;
27451
27594
  var init_wrap_ansi = __esm({
27452
27595
  "node_modules/wrap-ansi/index.js"() {
27453
27596
  init_string_width();
@@ -27519,7 +27662,7 @@ var init_wrap_ansi = __esm({
27519
27662
  }
27520
27663
  return words.slice(0, last).join(" ") + words.slice(last).join("");
27521
27664
  };
27522
- exec = (string, columns, options = {}) => {
27665
+ exec2 = (string, columns, options = {}) => {
27523
27666
  if (options.trim !== false && string.trim() === "") {
27524
27667
  return "";
27525
27668
  }
@@ -35952,135 +36095,6 @@ var init_build2 = __esm({
35952
36095
  }
35953
36096
  });
35954
36097
 
35955
- // src/auth.ts
35956
- var auth_exports = {};
35957
- __export(auth_exports, {
35958
- clearCredentials: () => clearCredentials,
35959
- createAuthSession: () => createAuthSession,
35960
- getStoredApiKey: () => getStoredApiKey,
35961
- openBrowser: () => openBrowser,
35962
- pollAuthSession: () => pollAuthSession,
35963
- saveCredentials: () => saveCredentials
35964
- });
35965
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, chmodSync, unlinkSync, existsSync as existsSync4 } from "node:fs";
35966
- import { join as join4 } from "node:path";
35967
- import { homedir as homedir3 } from "node:os";
35968
- import { exec as exec2 } from "node:child_process";
35969
- async function createAuthSession() {
35970
- let res;
35971
- try {
35972
- res = await globalThis.fetch(`${WEB_BASE}/cli/auth/sessions`, {
35973
- method: "POST",
35974
- headers: { "Content-Type": "application/json" }
35975
- });
35976
- } catch {
35977
- throw new Error("Could not connect to westbayberry.com");
35978
- }
35979
- if (!res.ok) {
35980
- const body = await res.text().catch(() => "");
35981
- throw new Error(
35982
- `Failed to create auth session (HTTP ${res.status})${body ? `: ${body}` : ""}`
35983
- );
35984
- }
35985
- const json = await res.json();
35986
- return {
35987
- sessionId: json.session_id,
35988
- verifyUrl: json.verify_url,
35989
- expiresIn: json.expires_in
35990
- };
35991
- }
35992
- async function pollAuthSession(sessionId) {
35993
- let res;
35994
- try {
35995
- res = await globalThis.fetch(
35996
- `${WEB_BASE}/cli/auth/sessions/${sessionId}/token`
35997
- );
35998
- } catch {
35999
- return { status: "expired" };
36000
- }
36001
- if (res.status === 404) {
36002
- return { status: "expired" };
36003
- }
36004
- if (!res.ok) {
36005
- return { status: "expired" };
36006
- }
36007
- const json = await res.json();
36008
- return {
36009
- status: json.status,
36010
- apiKey: json.api_key,
36011
- email: json.email
36012
- };
36013
- }
36014
- function configPath() {
36015
- return join4(homedir3(), CONFIG_FILE);
36016
- }
36017
- function readConfig() {
36018
- try {
36019
- const raw = readFileSync5(configPath(), "utf-8");
36020
- const parsed = JSON.parse(raw);
36021
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
36022
- return parsed;
36023
- }
36024
- return {};
36025
- } catch {
36026
- return {};
36027
- }
36028
- }
36029
- function saveCredentials(apiKey) {
36030
- const data = readConfig();
36031
- data.apiKey = apiKey;
36032
- const p = configPath();
36033
- writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
36034
- chmodSync(p, 384);
36035
- }
36036
- function clearCredentials() {
36037
- const p = configPath();
36038
- if (!existsSync4(p)) return;
36039
- const data = readConfig();
36040
- delete data.apiKey;
36041
- if (Object.keys(data).length === 0) {
36042
- unlinkSync(p);
36043
- } else {
36044
- writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
36045
- chmodSync(p, 384);
36046
- }
36047
- }
36048
- function getStoredApiKey() {
36049
- const data = readConfig();
36050
- if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
36051
- return data.apiKey;
36052
- }
36053
- return null;
36054
- }
36055
- function openBrowser(url) {
36056
- if (!/^https?:\/\//i.test(url)) return;
36057
- const escaped = url.replace(/"/g, '\\"');
36058
- let cmd;
36059
- switch (process.platform) {
36060
- case "darwin":
36061
- cmd = `open "${escaped}"`;
36062
- break;
36063
- case "linux":
36064
- cmd = `xdg-open "${escaped}"`;
36065
- break;
36066
- case "win32":
36067
- cmd = `start "" "${escaped}"`;
36068
- break;
36069
- default:
36070
- return;
36071
- }
36072
- exec2(cmd, () => {
36073
- });
36074
- }
36075
- var WEB_BASE, CONFIG_FILE;
36076
- var init_auth = __esm({
36077
- "src/auth.ts"() {
36078
- "use strict";
36079
- WEB_BASE = "https://westbayberry.com";
36080
- CONFIG_FILE = ".dgrc.json";
36081
- }
36082
- });
36083
-
36084
36098
  // src/ui/hooks/useLogin.ts
36085
36099
  function reducer(state, action) {
36086
36100
  switch (action.type) {
@@ -38906,43 +38920,48 @@ var init_LoginApp = __esm({
38906
38920
  });
38907
38921
 
38908
38922
  // src/api.ts
38923
+ function buildHeaders(config) {
38924
+ const headers = {
38925
+ "Content-Type": "application/json",
38926
+ "User-Agent": "dependency-guardian-cli/1.0.0"
38927
+ };
38928
+ if (config.apiKey) {
38929
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
38930
+ } else {
38931
+ headers["X-Device-Id"] = config.deviceId;
38932
+ }
38933
+ return headers;
38934
+ }
38935
+ async function handleTrialExhausted(response) {
38936
+ if (response.status === 403) {
38937
+ const body = await response.json().catch(() => ({}));
38938
+ if (body && body.trialExhausted) {
38939
+ const b = body;
38940
+ throw new TrialExhaustedError(b.scansUsed, b.maxScans);
38941
+ }
38942
+ throw new APIError("Forbidden", 403, JSON.stringify(body));
38943
+ }
38944
+ }
38909
38945
  async function callAnalyzeAPI(packages, config, onProgress) {
38910
38946
  if (packages.length <= BATCH_SIZE) {
38911
- return callAnalyzeBatch(packages, config);
38947
+ if (onProgress) onProgress(0, packages.length, packages.map((p) => p.name));
38948
+ const result = await callBatchWithRetry(packages, config);
38949
+ if (onProgress) onProgress(packages.length, packages.length, packages.map((p) => p.name));
38950
+ return result;
38912
38951
  }
38913
38952
  const batches = [];
38914
38953
  for (let i = 0; i < packages.length; i += BATCH_SIZE) {
38915
38954
  batches.push(packages.slice(i, i + BATCH_SIZE));
38916
38955
  }
38917
- const MAX_CONCURRENT_BATCHES = 2;
38918
- const results = new Array(batches.length);
38956
+ const results = [];
38919
38957
  let completed = 0;
38920
- let nextIdx = 0;
38921
- async function runBatch(idx) {
38922
- const result = await callBatchWithRetry(batches[idx], config);
38923
- results[idx] = result;
38924
- completed += batches[idx].length;
38958
+ for (const batch of batches) {
38959
+ const result = await callBatchWithRetry(batch, config);
38960
+ completed += batch.length;
38925
38961
  if (onProgress) {
38926
- onProgress(completed, packages.length, batches[idx].map((p) => p.name));
38927
- }
38928
- }
38929
- const inFlight = /* @__PURE__ */ new Set();
38930
- while (nextIdx < batches.length && inFlight.size < MAX_CONCURRENT_BATCHES) {
38931
- const idx = nextIdx++;
38932
- const p = runBatch(idx).then(() => {
38933
- inFlight.delete(p);
38934
- });
38935
- inFlight.add(p);
38936
- }
38937
- while (inFlight.size > 0) {
38938
- await Promise.race(inFlight);
38939
- while (nextIdx < batches.length && inFlight.size < MAX_CONCURRENT_BATCHES) {
38940
- const idx = nextIdx++;
38941
- const p = runBatch(idx).then(() => {
38942
- inFlight.delete(p);
38943
- });
38944
- inFlight.add(p);
38962
+ onProgress(completed, packages.length, batch.map((p) => p.name));
38945
38963
  }
38964
+ results.push(result);
38946
38965
  }
38947
38966
  return mergeResponses(results, config);
38948
38967
  }
@@ -38998,32 +39017,26 @@ async function callAnalyzeBatch(packages, config) {
38998
39017
  }
38999
39018
  };
39000
39019
  const controller = new AbortController();
39001
- const timeoutId = setTimeout(() => controller.abort(), 12e4);
39020
+ const timeoutId = setTimeout(() => controller.abort(), 18e4);
39002
39021
  let response;
39003
39022
  try {
39004
- const headers = {
39005
- "Content-Type": "application/json",
39006
- "User-Agent": "dependency-guardian-cli/1.0.0"
39007
- };
39008
- if (config.apiKey) {
39009
- headers["Authorization"] = `Bearer ${config.apiKey}`;
39010
- }
39011
39023
  response = await fetch(url, {
39012
39024
  method: "POST",
39013
- headers,
39025
+ headers: buildHeaders(config),
39014
39026
  body: JSON.stringify(payload),
39015
39027
  signal: controller.signal
39016
39028
  });
39017
39029
  } catch (error) {
39018
39030
  clearTimeout(timeoutId);
39019
39031
  if (error instanceof Error && error.name === "AbortError") {
39020
- throw new APIError("Request timed out after 120s. Try scanning fewer packages.", 408, "");
39032
+ throw new APIError("Request timed out after 180s. Try scanning fewer packages.", 408, "");
39021
39033
  }
39022
39034
  const cause = error instanceof Error && error.cause;
39023
39035
  const detail = cause ? `: ${cause.message || cause}` : "";
39024
39036
  throw new Error(`fetch failed${detail}`);
39025
39037
  }
39026
39038
  clearTimeout(timeoutId);
39039
+ await handleTrialExhausted(response);
39027
39040
  if (response.status === 401) {
39028
39041
  throw new APIError(
39029
39042
  "Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
@@ -39032,14 +39045,6 @@ async function callAnalyzeBatch(packages, config) {
39032
39045
  );
39033
39046
  }
39034
39047
  if (response.status === 429) {
39035
- const body = await response.json().catch(() => ({}));
39036
- if (body.anonymous) {
39037
- throw new APIError(
39038
- "Anonymous scan limit reached. Run `dg login` for 200 free scans/month.",
39039
- 429,
39040
- ""
39041
- );
39042
- }
39043
39048
  throw new APIError(
39044
39049
  "Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
39045
39050
  429,
@@ -39089,19 +39094,12 @@ async function callPyPIBatch(packages, config) {
39089
39094
  }
39090
39095
  };
39091
39096
  const controller = new AbortController();
39092
- const timeoutId = setTimeout(() => controller.abort(), 12e4);
39097
+ const timeoutId = setTimeout(() => controller.abort(), 18e4);
39093
39098
  let response;
39094
39099
  try {
39095
- const headers = {
39096
- "Content-Type": "application/json",
39097
- "User-Agent": "dependency-guardian-cli/1.0.0"
39098
- };
39099
- if (config.apiKey) {
39100
- headers["Authorization"] = `Bearer ${config.apiKey}`;
39101
- }
39102
39100
  response = await fetch(url, {
39103
39101
  method: "POST",
39104
- headers,
39102
+ headers: buildHeaders(config),
39105
39103
  body: JSON.stringify(payload),
39106
39104
  signal: controller.signal
39107
39105
  });
@@ -39115,6 +39113,7 @@ async function callPyPIBatch(packages, config) {
39115
39113
  throw new Error(`fetch failed${detail}`);
39116
39114
  }
39117
39115
  clearTimeout(timeoutId);
39116
+ await handleTrialExhausted(response);
39118
39117
  if (response.status === 401) {
39119
39118
  throw new APIError(
39120
39119
  "Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
@@ -39123,14 +39122,6 @@ async function callPyPIBatch(packages, config) {
39123
39122
  );
39124
39123
  }
39125
39124
  if (response.status === 429) {
39126
- const body = await response.json().catch(() => ({}));
39127
- if (body.anonymous) {
39128
- throw new APIError(
39129
- "Anonymous scan limit reached. Run `dg login` for 200 free scans/month.",
39130
- 429,
39131
- ""
39132
- );
39133
- }
39134
39125
  throw new APIError(
39135
39126
  "Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
39136
39127
  429,
@@ -39143,7 +39134,7 @@ async function callPyPIBatch(packages, config) {
39143
39134
  }
39144
39135
  return await response.json();
39145
39136
  }
39146
- var APIError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
39137
+ var APIError, TrialExhaustedError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
39147
39138
  var init_api = __esm({
39148
39139
  "src/api.ts"() {
39149
39140
  "use strict";
@@ -39155,7 +39146,15 @@ var init_api = __esm({
39155
39146
  this.name = "APIError";
39156
39147
  }
39157
39148
  };
39158
- BATCH_SIZE = 200;
39149
+ TrialExhaustedError = class extends Error {
39150
+ constructor(scansUsed, maxScans) {
39151
+ super("Free trial scans used up. Run `dg login` to create a free account and continue scanning.");
39152
+ this.scansUsed = scansUsed;
39153
+ this.maxScans = maxScans;
39154
+ this.name = "TrialExhaustedError";
39155
+ }
39156
+ };
39157
+ BATCH_SIZE = 75;
39159
39158
  MAX_RETRIES = 2;
39160
39159
  RETRY_DELAY_MS = 5e3;
39161
39160
  }
@@ -39772,6 +39771,31 @@ __export(static_output_exports, {
39772
39771
  runStaticLogin: () => runStaticLogin,
39773
39772
  runStaticNpm: () => runStaticNpm
39774
39773
  });
39774
+ function printTrialBanner(result) {
39775
+ if (result.trialScansRemaining === void 0) return;
39776
+ const remaining = result.trialScansRemaining;
39777
+ if (remaining > 0) {
39778
+ process.stderr.write(
39779
+ import_chalk4.default.dim(` ${remaining} free scan${remaining !== 1 ? "s" : ""} remaining. `) + import_chalk4.default.dim(`Run \`dg login\` for unlimited scans.
39780
+ `)
39781
+ );
39782
+ } else {
39783
+ process.stderr.write(
39784
+ import_chalk4.default.yellow(` No free scans remaining. `) + import_chalk4.default.yellow(`Run \`dg login\` to continue scanning.
39785
+ `)
39786
+ );
39787
+ }
39788
+ }
39789
+ function handleTrialExhausted2(error) {
39790
+ if (error instanceof TrialExhaustedError) {
39791
+ process.stderr.write(
39792
+ 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")
39793
+ );
39794
+ process.exit(1);
39795
+ return true;
39796
+ }
39797
+ return false;
39798
+ }
39775
39799
  function severityColor(sev) {
39776
39800
  if (sev >= 5) return (s) => import_chalk4.default.bold.red(s);
39777
39801
  if (sev >= 4) return import_chalk4.default.red;
@@ -39794,7 +39818,7 @@ function truncate(s, max) {
39794
39818
  function groupPackages(packages) {
39795
39819
  const map = /* @__PURE__ */ new Map();
39796
39820
  for (const pkg of packages) {
39797
- const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
39821
+ const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
39798
39822
  const group = map.get(fingerprint) ?? [];
39799
39823
  group.push(pkg);
39800
39824
  map.set(fingerprint, group);
@@ -39876,7 +39900,7 @@ function renderResultStatic(result, config) {
39876
39900
  const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
39877
39901
  const colorFn = severityColor(finding.severity);
39878
39902
  lines.push(
39879
- ` ${colorFn(pad(sevLabel, 10))}${finding.category} \u2014 ${finding.title}`
39903
+ ` ${colorFn(pad(sevLabel, 10))}${finding.id} \u2014 ${finding.title}`
39880
39904
  );
39881
39905
  const evidenceLimit = 3;
39882
39906
  for (let i = 0; i < Math.min(finding.evidence.length, evidenceLimit); i++) {
@@ -39973,21 +39997,23 @@ async function runStatic(config) {
39973
39997
  dbg(
39974
39998
  `packages to scan: ${packages.map((p) => `${p.name}@${p.version}`).slice(0, 10).join(", ")}${packages.length > 10 ? ` (+${packages.length - 10} more)` : ""}`
39975
39999
  );
39976
- const startMs = Date.now();
39977
- const result = await callAnalyzeAPI(packages, config, (done, total) => {
39978
- process.stderr.write(import_chalk4.default.dim(` Analyzed ${done}/${total}...
40000
+ let result;
40001
+ try {
40002
+ const startMs = Date.now();
40003
+ result = await callAnalyzeAPI(packages, config, (done, total) => {
40004
+ process.stderr.write(import_chalk4.default.dim(` Analyzed ${done}/${total}...
39979
40005
  `));
39980
- });
39981
- dbg(
39982
- `API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`
39983
- );
39984
- const output = renderResultStatic(result, config);
39985
- process.stdout.write(output + "\n");
39986
- if (!config.apiKey && !config.json) {
39987
- process.stderr.write(
39988
- "\n" + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n") + import_chalk4.default.cyan(" Want this on every PR? ") + "Run " + import_chalk4.default.white("dg login") + import_chalk4.default.dim(" \u2192 free tier, 200 scans/month\n") + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n")
40006
+ });
40007
+ dbg(
40008
+ `API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`
39989
40009
  );
40010
+ } catch (error) {
40011
+ if (handleTrialExhausted2(error)) return;
40012
+ throw error;
39990
40013
  }
40014
+ const output = renderResultStatic(result, config);
40015
+ process.stdout.write(output + "\n");
40016
+ printTrialBanner(result);
39991
40017
  if (result.action === "block" && config.mode === "block") {
39992
40018
  process.exit(2);
39993
40019
  } else if (result.action === "block" || result.action === "warn") {
@@ -40082,6 +40108,7 @@ async function scanAndInstallStatic(resolved, parsed, config) {
40082
40108
  );
40083
40109
  }
40084
40110
  } catch (error) {
40111
+ if (handleTrialExhausted2(error)) return;
40085
40112
  const msg = error instanceof Error ? error.message : String(error);
40086
40113
  process.stderr.write(
40087
40114
  import_chalk4.default.yellow(
@@ -40097,20 +40124,17 @@ async function scanAndInstallStatic(resolved, parsed, config) {
40097
40124
  process.stderr.write(
40098
40125
  import_chalk4.default.green(
40099
40126
  ` ${import_chalk4.default.bold("\u2713")} ${toScan.length} package${toScan.length !== 1 ? "s" : ""} scanned \u2014 all clear
40100
-
40101
40127
  `
40102
40128
  )
40103
40129
  );
40130
+ printTrialBanner(result);
40131
+ process.stderr.write("\n");
40104
40132
  const code = await runNpm(parsed.rawArgs);
40105
40133
  process.exit(code);
40106
40134
  }
40107
40135
  const output = renderResultStatic(result, config);
40108
40136
  process.stdout.write(output + "\n");
40109
- if (!config.apiKey) {
40110
- process.stderr.write(
40111
- "\n" + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n") + import_chalk4.default.cyan(" Want this on every PR? ") + "Run " + import_chalk4.default.white("dg login") + import_chalk4.default.dim(" \u2192 free tier, 200 scans/month\n") + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n")
40112
- );
40113
- }
40137
+ printTrialBanner(result);
40114
40138
  if (result.action === "warn") {
40115
40139
  process.stderr.write(
40116
40140
  import_chalk4.default.yellow(" Warnings detected. Proceeding with install.\n\n")
@@ -40429,6 +40453,8 @@ function reducer2(_state, action) {
40429
40453
  return { phase: "done", exitCode: action.exitCode };
40430
40454
  case "ERROR":
40431
40455
  return { phase: "error", message: action.message, proceed: action.proceed };
40456
+ case "TRIAL_EXHAUSTED":
40457
+ return { phase: "trial_exhausted" };
40432
40458
  }
40433
40459
  }
40434
40460
  function useNpmWrapper(npmArgs, config) {
@@ -40533,6 +40559,10 @@ function useNpmWrapper(npmArgs, config) {
40533
40559
  return;
40534
40560
  }
40535
40561
  } catch (error) {
40562
+ if (error instanceof TrialExhaustedError) {
40563
+ dispatch({ type: "TRIAL_EXHAUSTED" });
40564
+ return;
40565
+ }
40536
40566
  const message = error instanceof Error ? error.message : String(error);
40537
40567
  dispatch({ type: "ERROR", message, proceed: true });
40538
40568
  dispatch({ type: "INSTALLING" });
@@ -40651,7 +40681,7 @@ var init_DurationLine = __esm({
40651
40681
  function groupPackages2(packages) {
40652
40682
  const map = /* @__PURE__ */ new Map();
40653
40683
  for (const pkg of packages) {
40654
- const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
40684
+ const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
40655
40685
  const group = map.get(fingerprint) ?? [];
40656
40686
  group.push(pkg);
40657
40687
  map.set(fingerprint, group);
@@ -40771,7 +40801,7 @@ var init_ResultsView = __esm({
40771
40801
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40772
40802
  " ",
40773
40803
  colorFn(pad2(sevLabel, 10)),
40774
- finding.category,
40804
+ finding.id,
40775
40805
  " ",
40776
40806
  "\u2014",
40777
40807
  " ",
@@ -40786,7 +40816,7 @@ var init_ResultsView = __esm({
40786
40816
  import_chalk6.default.dim(`... and ${overflow} more`)
40787
40817
  ] }),
40788
40818
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " })
40789
- ] }, `${finding.category}-${idx}`);
40819
+ ] }, `${finding.id}-${idx}`);
40790
40820
  }),
40791
40821
  safeVersion && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
40792
40822
  " ",
@@ -40907,6 +40937,11 @@ var init_NpmWrapperApp = __esm({
40907
40937
  const timer = setTimeout(() => exit(), 0);
40908
40938
  return () => clearTimeout(timer);
40909
40939
  }
40940
+ if (state.phase === "trial_exhausted") {
40941
+ process.exitCode = 1;
40942
+ const timer = setTimeout(() => exit(), 0);
40943
+ return () => clearTimeout(timer);
40944
+ }
40910
40945
  if (state.phase === "passthrough") {
40911
40946
  }
40912
40947
  }, [state, exit]);
@@ -41006,6 +41041,15 @@ var init_NpmWrapperApp = __esm({
41006
41041
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorView, { error: new Error(state.message) });
41007
41042
  case "passthrough":
41008
41043
  return null;
41044
+ case "trial_exhausted":
41045
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
41046
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
41047
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
41048
+ "Run ",
41049
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
41050
+ " to create a free account and continue scanning."
41051
+ ] })
41052
+ ] });
41009
41053
  }
41010
41054
  };
41011
41055
  }
@@ -41028,6 +41072,8 @@ function reducer3(_state, action) {
41028
41072
  return { phase: "results", result: action.result, durationMs: action.durationMs, skippedCount: action.skippedCount };
41029
41073
  case "ERROR":
41030
41074
  return { phase: "error", error: action.error };
41075
+ case "TRIAL_EXHAUSTED":
41076
+ return { phase: "trial_exhausted", scansUsed: action.scansUsed, maxScans: action.maxScans };
41031
41077
  }
41032
41078
  }
41033
41079
  function useScan(config) {
@@ -41086,6 +41132,10 @@ async function runNpmScan(packages, skippedCount, config, dispatch) {
41086
41132
  });
41087
41133
  dispatch({ type: "SCAN_COMPLETE", result, durationMs: Date.now() - startMs, skippedCount });
41088
41134
  } catch (error) {
41135
+ if (error instanceof TrialExhaustedError) {
41136
+ dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
41137
+ return;
41138
+ }
41089
41139
  dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
41090
41140
  }
41091
41141
  }
@@ -41156,6 +41206,10 @@ async function scanProjects(projects, config, dispatch) {
41156
41206
  };
41157
41207
  dispatch({ type: "SCAN_COMPLETE", result: merged, durationMs: merged.durationMs, skippedCount: 0 });
41158
41208
  } catch (error) {
41209
+ if (error instanceof TrialExhaustedError) {
41210
+ dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
41211
+ return;
41212
+ }
41159
41213
  dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
41160
41214
  }
41161
41215
  }
@@ -41288,7 +41342,7 @@ var init_useTerminalSize = __esm({
41288
41342
  function groupPackages3(packages) {
41289
41343
  const map = /* @__PURE__ */ new Map();
41290
41344
  for (const pkg of packages) {
41291
- const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
41345
+ const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
41292
41346
  const group = map.get(fingerprint) ?? [];
41293
41347
  group.push(pkg);
41294
41348
  map.set(fingerprint, group);
@@ -41407,8 +41461,7 @@ var init_InteractiveResultsView = __esm({
41407
41461
  const viewRef = (0, import_react30.useRef)(view);
41408
41462
  viewRef.current = view;
41409
41463
  const { rows: termRows, cols: termCols } = useTerminalSize();
41410
- const chromeHeight = FIXED_CHROME + (config.apiKey ? 0 : 2);
41411
- const availableRows = Math.max(5, termRows - chromeHeight);
41464
+ const availableRows = Math.max(5, termRows - FIXED_CHROME);
41412
41465
  const innerWidth = Math.max(40, termCols - 6);
41413
41466
  const getLevel = (idx) => {
41414
41467
  return view.expandedIndex === idx ? view.expandLevel : null;
@@ -41496,9 +41549,6 @@ var init_InteractiveResultsView = __esm({
41496
41549
  dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
41497
41550
  } else if (input === "e") {
41498
41551
  if (expIdx === cursor && expLvl === "detail") {
41499
- const newVp = adjustViewport(cursor, cursor, "summary", vpStart);
41500
- dispatchView({ type: "EXPAND", expandedIndex: cursor, expandLevel: "summary", viewport: newVp });
41501
- } else if (expIdx === cursor && expLvl === "summary") {
41502
41552
  const newVp = adjustViewport(cursor, null, null, vpStart);
41503
41553
  dispatchView({ type: "EXPAND", expandedIndex: null, expandLevel: null, viewport: newVp });
41504
41554
  } else {
@@ -41620,12 +41670,6 @@ var init_InteractiveResultsView = __esm({
41620
41670
  ]
41621
41671
  }
41622
41672
  ),
41623
- !config.apiKey && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { paddingLeft: 1, paddingRight: 1, width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
41624
- import_chalk10.default.cyan("Want this on every PR?"),
41625
- " Run ",
41626
- import_chalk10.default.white("dg login"),
41627
- import_chalk10.default.dim(" \u2192 free tier, 200 scans/month")
41628
- ] }) }),
41629
41673
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41630
41674
  " ",
41631
41675
  groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
@@ -41678,8 +41722,8 @@ var init_InteractiveResultsView = __esm({
41678
41722
  " ",
41679
41723
  sevColor(pad3(sevLabel, 4)),
41680
41724
  " ",
41681
- import_chalk10.default.dim(f.category)
41682
- ] }, `${f.category}-${idx}`)
41725
+ import_chalk10.default.dim(f.id)
41726
+ ] }, `${f.id}-${idx}`)
41683
41727
  );
41684
41728
  }
41685
41729
  if (hasAffects) {
@@ -41717,15 +41761,15 @@ var init_InteractiveResultsView = __esm({
41717
41761
  " ",
41718
41762
  sevColor(pad3(sevLabel, 4)),
41719
41763
  " ",
41720
- import_chalk10.default.bold(finding.category)
41721
- ] }, `${finding.category}-${idx}-badge`)
41764
+ import_chalk10.default.bold(finding.id)
41765
+ ] }, `${finding.id}-${idx}-badge`)
41722
41766
  );
41723
41767
  allLines.push(
41724
41768
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
41725
41769
  continuation,
41726
41770
  " ",
41727
41771
  finding.title
41728
- ] }, `${finding.category}-${idx}-title`)
41772
+ ] }, `${finding.id}-${idx}-title`)
41729
41773
  );
41730
41774
  for (let i = 0; i < evidenceSlice.length; i++) {
41731
41775
  allLines.push(
@@ -41735,7 +41779,7 @@ var init_InteractiveResultsView = __esm({
41735
41779
  import_chalk10.default.dim("\u203A"),
41736
41780
  " ",
41737
41781
  truncate3(evidenceSlice[i], evidenceWidth)
41738
- ] }, `${finding.category}-${idx}-ev-${i}`)
41782
+ ] }, `${finding.id}-${idx}-ev-${i}`)
41739
41783
  );
41740
41784
  }
41741
41785
  if (overflow > 0) {
@@ -41744,7 +41788,7 @@ var init_InteractiveResultsView = __esm({
41744
41788
  continuation,
41745
41789
  " ",
41746
41790
  import_chalk10.default.dim(`+${overflow} more`)
41747
- ] }, `${finding.category}-${idx}-overflow`)
41791
+ ] }, `${finding.id}-${idx}-overflow`)
41748
41792
  );
41749
41793
  }
41750
41794
  }
@@ -41894,6 +41938,11 @@ var init_App2 = __esm({
41894
41938
  const timer = setTimeout(() => exit(), 0);
41895
41939
  return () => clearTimeout(timer);
41896
41940
  }
41941
+ if (state.phase === "trial_exhausted") {
41942
+ process.exitCode = 1;
41943
+ const timer = setTimeout(() => exit(), 0);
41944
+ return () => clearTimeout(timer);
41945
+ }
41897
41946
  }, [state, config, exit]);
41898
41947
  const content = (() => {
41899
41948
  switch (state.phase) {
@@ -41944,6 +41993,15 @@ var init_App2 = __esm({
41944
41993
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
41945
41994
  case "error":
41946
41995
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorView, { error: state.error });
41996
+ case "trial_exhausted":
41997
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
41998
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
41999
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
42000
+ "Run ",
42001
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
42002
+ " to create a free account and continue scanning."
42003
+ ] })
42004
+ ] });
41947
42005
  }
41948
42006
  })();
41949
42007
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", height: termRows, children: content });
@@ -41956,16 +42014,16 @@ init_config();
41956
42014
  init_npm_wrapper();
41957
42015
 
41958
42016
  // src/update-check.ts
41959
- import { readFileSync as readFileSync3, writeFileSync } from "node:fs";
41960
- import { homedir as homedir2 } from "node:os";
41961
- import { join as join3 } from "node:path";
42017
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
42018
+ import { homedir as homedir3 } from "node:os";
42019
+ import { join as join4 } from "node:path";
41962
42020
  import { spawn as spawn2, execSync } from "node:child_process";
41963
42021
  var PKG_NAME = "@westbayberry/dg";
41964
- var CACHE_FILE = join3(homedir2(), ".dg-update-check.json");
42022
+ var CACHE_FILE = join4(homedir3(), ".dg-update-check.json");
41965
42023
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
41966
42024
  function readCache() {
41967
42025
  try {
41968
- const raw = readFileSync3(CACHE_FILE, "utf-8");
42026
+ const raw = readFileSync4(CACHE_FILE, "utf-8");
41969
42027
  const data = JSON.parse(raw);
41970
42028
  if (typeof data.latest === "string" && typeof data.checkedAt === "number") {
41971
42029
  return data;
@@ -41976,7 +42034,7 @@ function readCache() {
41976
42034
  }
41977
42035
  function writeCache(entry) {
41978
42036
  try {
41979
- writeFileSync(CACHE_FILE, JSON.stringify(entry), "utf-8");
42037
+ writeFileSync2(CACHE_FILE, JSON.stringify(entry), "utf-8");
41980
42038
  } catch {
41981
42039
  }
41982
42040
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@westbayberry/dg",
3
- "version": "1.0.16",
3
+ "version": "1.0.20",
4
4
  "description": "Supply chain security scanner for npm and Python dependencies — detects malicious packages, typosquatting, dependency confusion, and 26+ attack patterns",
5
5
  "bin": {
6
6
  "dependency-guardian": "dist/index.mjs",