@westbayberry/dg 2.3.3 → 2.3.4

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 (122) hide show
  1. package/README.md +94 -217
  2. package/dist/agents/copilot-cli.js +7 -2
  3. package/dist/agents/persistence.js +83 -8
  4. package/dist/agents/registry.js +5 -2
  5. package/dist/agents/routing.js +31 -14
  6. package/dist/api/analyze.js +138 -41
  7. package/dist/audit/deep.js +2 -1
  8. package/dist/audit/detectors.js +23 -2
  9. package/dist/audit/rules.js +4 -1
  10. package/dist/audit-ui/export.js +5 -4
  11. package/dist/auth/device-login.js +33 -11
  12. package/dist/auth/login-app.js +17 -12
  13. package/dist/auth/store.js +65 -9
  14. package/dist/bin/dg.js +1 -1
  15. package/dist/commands/audit.js +12 -20
  16. package/dist/commands/{guard-commit.js → commit-guard.js} +12 -12
  17. package/dist/commands/cooldown.js +2 -1
  18. package/dist/commands/decisions.js +8 -7
  19. package/dist/commands/help.js +11 -3
  20. package/dist/commands/licenses.js +17 -8
  21. package/dist/commands/login.js +58 -21
  22. package/dist/commands/router.js +12 -2
  23. package/dist/commands/sbom.js +20 -5
  24. package/dist/commands/scan.js +5 -4
  25. package/dist/commands/service.js +1 -1
  26. package/dist/commands/setup.js +12 -12
  27. package/dist/commands/status.js +4 -4
  28. package/dist/commands/uninstall.js +1 -1
  29. package/dist/commands/update.js +20 -0
  30. package/dist/commands/verify.js +4 -4
  31. package/dist/config/settings.js +81 -12
  32. package/dist/export-ui/ExportDialog.js +1 -1
  33. package/dist/gate/cooldown-request.js +22 -0
  34. package/dist/gate/egress-guard.js +185 -0
  35. package/dist/gate/host-match.js +48 -0
  36. package/dist/gate/verdict-core.js +101 -0
  37. package/dist/gate/verdict-fetch.js +214 -0
  38. package/dist/launcher/agent-check.js +1096 -42
  39. package/dist/launcher/agent-hook-exec.js +8 -1
  40. package/dist/launcher/agent-hook-io.js +5 -3
  41. package/dist/launcher/classify.js +110 -26
  42. package/dist/launcher/env.js +84 -18
  43. package/dist/launcher/install-preflight.js +26 -3
  44. package/dist/launcher/live-install.js +4 -2
  45. package/dist/launcher/manifest-screen.js +88 -7
  46. package/dist/launcher/output-redaction.js +153 -15
  47. package/dist/launcher/preflight-prompt.js +1 -1
  48. package/dist/launcher/resolve-real-binary.js +8 -3
  49. package/dist/launcher/run.js +237 -40
  50. package/dist/origin/artifact-cache.js +55 -0
  51. package/dist/origin/cargo-config.js +12 -0
  52. package/dist/origin/gate-mode.js +7 -0
  53. package/dist/origin/platform-paths.js +14 -0
  54. package/dist/origin/precedence.js +345 -0
  55. package/dist/origin/server.js +611 -0
  56. package/dist/origin/spike-worker.js +35 -0
  57. package/dist/origin/worker.js +116 -0
  58. package/dist/presentation/package-page.js +21 -1
  59. package/dist/presentation/safe-version.js +11 -0
  60. package/dist/presentation/sarif.js +109 -0
  61. package/dist/project/dgfile.js +54 -17
  62. package/dist/project/override-trust.js +0 -0
  63. package/dist/proxy/enforcement.js +27 -7
  64. package/dist/proxy/metadata-map.js +33 -7
  65. package/dist/proxy/preverified.js +0 -3
  66. package/dist/proxy/server.js +104 -446
  67. package/dist/proxy/upstream-proxy.js +23 -4
  68. package/dist/proxy/worker.js +3 -2
  69. package/dist/publish-set/collect.js +6 -0
  70. package/dist/runtime/fatal.js +2 -1
  71. package/dist/runtime/first-run.js +5 -1
  72. package/dist/runtime/node-version.js +51 -0
  73. package/dist/runtime/nudges.js +27 -1
  74. package/dist/sbom/cyclonedx.js +120 -33
  75. package/dist/sbom/graph.js +236 -0
  76. package/dist/sbom/spdx.js +53 -0
  77. package/dist/sbom-ui/SbomApp.js +5 -3
  78. package/dist/sbom-ui/inventory.js +8 -10
  79. package/dist/scan/analyze-worker.js +3 -1
  80. package/dist/scan/collect.js +38 -8
  81. package/dist/scan/command.js +77 -27
  82. package/dist/scan/discovery.js +18 -7
  83. package/dist/scan/installed.js +202 -0
  84. package/dist/scan/manifest-coverage.js +128 -0
  85. package/dist/scan/render.js +125 -40
  86. package/dist/scan/scanner-report.js +92 -22
  87. package/dist/scan/staged.js +68 -15
  88. package/dist/scan/types.js +7 -1
  89. package/dist/scan-ui/LegacyApp.js +3 -3
  90. package/dist/scan-ui/components/InteractiveResultsView.js +17 -14
  91. package/dist/scan-ui/hooks/useScan.js +37 -11
  92. package/dist/scan-ui/launch.js +2 -2
  93. package/dist/scan-ui/logo.js +6 -2
  94. package/dist/scan-ui/shims.js +6 -6
  95. package/dist/scripts/gate.js +53 -35
  96. package/dist/security/csv.js +9 -0
  97. package/dist/security/sanitize.js +11 -2
  98. package/dist/service/state.js +120 -37
  99. package/dist/service/trust-refresh.js +12 -1
  100. package/dist/service/trust-store.js +36 -5
  101. package/dist/service/worker.js +5 -7
  102. package/dist/setup/git-hook.js +2 -2
  103. package/dist/setup/install-method.js +36 -0
  104. package/dist/setup/plan.js +119 -57
  105. package/dist/setup/uninstall-standalone.js +7 -2
  106. package/dist/setup-ui/gate.js +1 -8
  107. package/dist/setup-ui/offer.js +14 -7
  108. package/dist/setup-ui/selector.js +1 -1
  109. package/dist/setup-ui/wizard.js +73 -78
  110. package/dist/standalone/uninstall.mjs +1114 -386
  111. package/dist/state/cleanup-registry.js +14 -3
  112. package/dist/state/cooldown-held.js +36 -13
  113. package/dist/state/locks.js +239 -34
  114. package/dist/state/sessions.js +2 -2
  115. package/dist/util/sh-escape.js +6 -0
  116. package/dist/util/tty-prompt.js +13 -4
  117. package/dist/verify/local.js +60 -10
  118. package/dist/verify/package-check.js +35 -49
  119. package/dist/verify/preflight.js +59 -33
  120. package/dist/verify/render.js +11 -12
  121. package/npm-shrinkwrap.json +714 -213
  122. package/package.json +3 -2
@@ -0,0 +1,12 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ // Materialise a throwaway CARGO_HOME config that replaces crates-io with the
4
+ // local origin's sparse endpoint. cargo has no env var to override the default
5
+ // registry, so this is the only way to route it env-only — the user's real
6
+ // ~/.cargo/config.toml is never touched.
7
+ export function writeCargoOriginConfig(cargoHome, originUrl) {
8
+ mkdirSync(cargoHome, { recursive: true });
9
+ const sparse = `sparse+${originUrl.endsWith("/") ? originUrl : `${originUrl}/`}`;
10
+ const config = `[source.crates-io]\nreplace-with = "dg-origin"\n\n[source.dg-origin]\nregistry = "${sparse}"\n`;
11
+ writeFileSync(join(cargoHome, "config.toml"), config, { encoding: "utf8", mode: 0o600 });
12
+ }
@@ -0,0 +1,7 @@
1
+ // The kill-switch: revert to the proxy door without a re-publish. Kept for at
2
+ // least one release so an installed base can fall back if the origin path
3
+ // regresses. It disables the ORIGIN, not the gate — the proxy still screens.
4
+ export function isRegistryGateDisabled(env) {
5
+ const raw = env.DG_DISABLE_REGISTRY_GATE;
6
+ return raw === "1" || raw === "true";
7
+ }
@@ -0,0 +1,14 @@
1
+ import { join } from "node:path";
2
+ export function originPlatform(platform = process.platform) {
3
+ return platform === "win32" ? "win32" : "posix";
4
+ }
5
+ // pip reads %APPDATA%\pip\pip.ini on Windows; dg never writes it (env-only via
6
+ // PIP_INDEX_URL/PIP_TRUSTED_HOST), but doctor needs the path to detect a
7
+ // conflicting user config that would out-rank the env.
8
+ export function pipUserConfigPath(env, platform = originPlatform()) {
9
+ if (platform === "win32") {
10
+ const base = env.APPDATA ?? join(env.USERPROFILE ?? "", "AppData", "Roaming");
11
+ return join(base, "pip", "pip.ini");
12
+ }
13
+ return join(env.HOME ?? "", ".config", "pip", "pip.conf");
14
+ }
@@ -0,0 +1,345 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { pipUserConfigPath } from "./platform-paths.js";
4
+ import { readPipRequirementSpecs } from "../launcher/manifest-screen.js";
5
+ // CLI flags that point a manager at a different registry/index than the env var
6
+ // the origin sets. A CLI value out-ranks env for every manager, so any of these
7
+ // means the origin is not actually in front of (some of) the fetches.
8
+ const OVERRIDE_FLAGS = {
9
+ javascript: new Set(["--registry"]),
10
+ python: new Set(["--index-url", "-i", "--extra-index-url", "--index", "--default-index", "--find-links", "-f"]),
11
+ // cargo --config can inline a `source.crates-io.replace-with` (or point at a
12
+ // config file) that redirects the crates-io source past the origin door.
13
+ rust: new Set(["--registry", "--index", "--config"]),
14
+ gated: new Set()
15
+ };
16
+ // pip/uv (optparse) cluster short flags and may glue a value flag's value onto
17
+ // the token: `-ihttps://evil`, `-vi https://evil` (=`-v -i …`), `-vihttps://evil`.
18
+ // A value flag can sit anywhere in the cluster, so scan each two-char short flag
19
+ // and return the first override before the rest is read as its value. Over-
20
+ // detecting only routes the install to the proxy door (which gates all
21
+ // transport), so a false match is safe. optparse also accepts an unambiguous
22
+ // prefix of a long option (`--index-u` => `--index-url`); treat any `--` token
23
+ // that prefixes an override flag as that flag so the abbreviation can't slip by.
24
+ function overrideFlagToken(arg, flags) {
25
+ if (/^-[a-zA-Z]/.test(arg) && !arg.startsWith("--") && arg.length > 2) {
26
+ for (let i = 1; i < arg.length; i++) {
27
+ const short = `-${arg[i]}`;
28
+ if (flags.has(short)) {
29
+ return short;
30
+ }
31
+ }
32
+ }
33
+ const head = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg;
34
+ if (head.startsWith("--") && head.length > 2 && !flags.has(head)) {
35
+ for (const flag of flags) {
36
+ if (flag.startsWith("--") && flag.startsWith(head)) {
37
+ return flag;
38
+ }
39
+ }
40
+ }
41
+ return head;
42
+ }
43
+ export function resolveOriginPrecedence(classification, env, cwd) {
44
+ const overrides = [];
45
+ let active = true;
46
+ const flags = OVERRIDE_FLAGS[classification.ecosystem];
47
+ for (const arg of classification.args) {
48
+ const flag = overrideFlagToken(arg, flags);
49
+ if (flags.has(flag)) {
50
+ overrides.push(`command-line ${flag}`);
51
+ active = false;
52
+ }
53
+ else if (classification.ecosystem === "javascript" && /^--@[^:=\s]+:registry$/.test(flag)) {
54
+ // `--@scope:registry=URL` points that scope at its own upstream the origin
55
+ // does not front; fall back to the proxy door, which gates all transport.
56
+ overrides.push(`command-line ${flag} routes a scope to its own registry`);
57
+ active = false;
58
+ }
59
+ else if (classification.ecosystem === "javascript" && /^--config\.(@[^:=\s]+:)?registry$/.test(flag)) {
60
+ // pnpm `--config.registry=URL` / `--config.@scope:registry=URL` sets the
61
+ // registry the same as the npm config key.
62
+ overrides.push(`command-line ${flag} overrides the registry`);
63
+ active = false;
64
+ }
65
+ }
66
+ if (classification.ecosystem === "javascript") {
67
+ for (const scope of scopedNpmRegistries(env, cwd)) {
68
+ overrides.push(`${scope} routes to its own registry (not gated by dg)`);
69
+ }
70
+ }
71
+ // The origin door points pip/uv at the loopback index via env, but pip also
72
+ // MERGES extra-index-url / find-links and a non-default index-url from its
73
+ // config file (pip.conf), which the env does not override. Detect those so the
74
+ // install falls back to the proxy door instead of fetching from an un-fronted
75
+ // source while dg reports the gate active.
76
+ if (classification.ecosystem === "python") {
77
+ for (const reason of pipConfigIndexes(env, cwd)) {
78
+ overrides.push(reason);
79
+ active = false;
80
+ }
81
+ // pip also honors --index-url / --extra-index-url / -f declared INSIDE a
82
+ // requirements/constraints file (and any nested -r/-c include). Reuse the
83
+ // static hook's include-walk so the runtime gate sees the same in-file index
84
+ // directives and falls back to the proxy door instead of fetching un-fronted.
85
+ const requirementIndexes = readPipRequirementSpecs(classification.args, cwd)?.alternateIndexes ?? [];
86
+ for (const idx of requirementIndexes) {
87
+ if (isNonDefaultPythonIndex(idx)) {
88
+ overrides.push(`requirements-file index ${idx} (origin fronts the public index only)`);
89
+ active = false;
90
+ }
91
+ }
92
+ }
93
+ // cargo merges config from the project `.cargo/config.toml` (walking up from
94
+ // cwd) and the user CARGO_HOME, with the project file out-ranking the throwaway
95
+ // CARGO_HOME the origin door writes. A `[source.crates-io] replace-with` there
96
+ // shadows the dg-origin source, so the crates install un-gated — detect it and
97
+ // fall back to the proxy door.
98
+ if (classification.ecosystem === "rust") {
99
+ for (const reason of cargoConfigOverride(env, cwd)) {
100
+ overrides.push(reason);
101
+ active = false;
102
+ }
103
+ for (const reason of cargoEnvOverride(env)) {
104
+ overrides.push(reason);
105
+ active = false;
106
+ }
107
+ }
108
+ // The origin chains only to the PUBLIC default registry/index. A configured
109
+ // custom primary upstream (corporate Nexus/Verdaccio, a private index) is one
110
+ // the origin cannot front correctly, so it counts as an override and the proxy
111
+ // door — which intercepts any upstream at the transport layer — takes over.
112
+ const customUpstream = nonDefaultPrimaryUpstream(classification, env, cwd);
113
+ if (customUpstream) {
114
+ overrides.push(customUpstream);
115
+ active = false;
116
+ }
117
+ // An EXTRA index sits alongside the primary one the origin fronts; pip/uv query
118
+ // it directly, so any package it serves is fetched un-gated. The origin cannot
119
+ // front it, so the proxy door (which intercepts all transport) takes over.
120
+ for (const extra of extraPythonIndexes(classification, env)) {
121
+ overrides.push(extra);
122
+ active = false;
123
+ }
124
+ return { active, overrides };
125
+ }
126
+ function extraPythonIndexes(classification, env) {
127
+ if (classification.ecosystem !== "python") {
128
+ return [];
129
+ }
130
+ const found = [];
131
+ for (const key of ["PIP_EXTRA_INDEX_URL", "PIP_FIND_LINKS", "UV_EXTRA_INDEX_URL", "UV_INDEX", "UV_FIND_LINKS"]) {
132
+ const value = env[key]?.trim();
133
+ if (value) {
134
+ found.push(`${key} adds a package source the origin does not front`);
135
+ }
136
+ }
137
+ return found;
138
+ }
139
+ function nonDefaultPrimaryUpstream(classification, env, cwd) {
140
+ if (classification.ecosystem === "javascript") {
141
+ const registry = env.npm_config_registry ?? env.NPM_CONFIG_REGISTRY ?? npmrcPrimaryRegistry(env, cwd);
142
+ if (registry && !isHost(registry, "registry.npmjs.org")) {
143
+ return `non-default npm registry ${registry} (origin fronts the public registry only)`;
144
+ }
145
+ }
146
+ if (classification.ecosystem === "python") {
147
+ const index = env.PIP_INDEX_URL ?? env.UV_DEFAULT_INDEX ?? env.UV_INDEX_URL;
148
+ if (index && !isHost(index, "pypi.org")) {
149
+ return `non-default python index ${index} (origin fronts the public index only)`;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ function npmrcPrimaryRegistry(env, cwd) {
155
+ for (const file of [join(cwd, ".npmrc"), join(env.HOME ?? "", ".npmrc")]) {
156
+ for (const line of readLines(file)) {
157
+ const match = /^\s*registry\s*=\s*(\S+)/.exec(line);
158
+ if (match?.[1]) {
159
+ return match[1];
160
+ }
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ function isNonDefaultPythonIndex(value) {
166
+ try {
167
+ const host = new URL(value).hostname;
168
+ return host !== "pypi.org" && host !== "files.pythonhosted.org";
169
+ }
170
+ catch {
171
+ // A bare/relative local path (`--find-links ./wheels`) is not an un-fronted
172
+ // remote index, so it does not force a fallback.
173
+ return false;
174
+ }
175
+ }
176
+ function isHost(url, host) {
177
+ try {
178
+ return new URL(url).hostname === host;
179
+ }
180
+ catch {
181
+ return false;
182
+ }
183
+ }
184
+ function scopedNpmRegistries(env, cwd) {
185
+ const scopes = new Set();
186
+ for (const file of [join(cwd, ".npmrc"), join(env.HOME ?? "", ".npmrc")]) {
187
+ for (const line of readLines(file)) {
188
+ const match = /^\s*(@[^:=\s]+):registry\s*=/.exec(line);
189
+ if (match?.[1]) {
190
+ scopes.add(match[1]);
191
+ }
192
+ }
193
+ }
194
+ // npm/pnpm also accept a scoped registry from the environment
195
+ // (npm_config_@scope:registry); that scope resolves to its own upstream, which
196
+ // the origin does not front.
197
+ for (const key of Object.keys(env)) {
198
+ const match = /^npm_config_(@[^:=\s]+):registry$/i.exec(key);
199
+ if (match?.[1] && (env[key] ?? "").trim()) {
200
+ scopes.add(match[1]);
201
+ }
202
+ }
203
+ return [...scopes];
204
+ }
205
+ // pip.conf (INI): index-url to a non-pypi host, or any extra-index-url /
206
+ // find-links, is a package source the origin does not front.
207
+ function pipConfigIndexes(env, cwd) {
208
+ const out = [];
209
+ const home = env.HOME ?? "";
210
+ const venv = env.VIRTUAL_ENV;
211
+ // pip's full config search chain — global, user (XDG + legacy), and the active
212
+ // virtualenv's site config. An extra-index-url in ANY of these is merged into
213
+ // the install, so all must be checked or a config-file index slips the gate.
214
+ const files = [
215
+ env.PIP_CONFIG_FILE,
216
+ "/etc/pip.conf",
217
+ "/etc/xdg/pip/pip.conf",
218
+ pipUserConfigPath(env),
219
+ home ? join(home, ".pip", "pip.conf") : undefined,
220
+ venv ? join(venv, "pip.conf") : undefined,
221
+ join(cwd, "pip.conf"),
222
+ ].filter((f) => Boolean(f));
223
+ for (const file of files) {
224
+ let section = "";
225
+ for (const raw of readLines(file)) {
226
+ const line = raw.trim();
227
+ if (line.startsWith("#") || line.startsWith(";")) {
228
+ continue;
229
+ }
230
+ const sec = /^\[([^\]]+)\]/.exec(line);
231
+ if (sec) {
232
+ section = sec[1] ?? "";
233
+ continue;
234
+ }
235
+ if (section !== "global" && section !== "install") {
236
+ continue;
237
+ }
238
+ const kv = /^(index-url|extra-index-url|find-links)\s*[=:]\s*(\S.*)$/.exec(line);
239
+ if (!kv) {
240
+ continue;
241
+ }
242
+ const value = (kv[2] ?? "").trim();
243
+ if (kv[1] === "index-url") {
244
+ if (!isHost(value, "pypi.org")) {
245
+ out.push(`pip.conf index-url ${value} (origin fronts the public index only)`);
246
+ }
247
+ }
248
+ else {
249
+ out.push(`pip.conf ${kv[1]} adds a package source the origin does not front`);
250
+ }
251
+ }
252
+ }
253
+ return out;
254
+ }
255
+ function normalizeTomlKey(s) {
256
+ return s
257
+ .split(".")
258
+ .map((seg) => seg.trim().replace(/^["']|["']$/g, "").trim())
259
+ .join(".");
260
+ }
261
+ // cargo source/registry redirection in a config file out-ranks the throwaway
262
+ // CARGO_HOME the origin door writes, so a `replace-with` (or a direct crates-io
263
+ // registry override) means the origin is shadowed. cargo accepts the redirection
264
+ // in several equivalent TOML spellings (header table, dotted key under a parent
265
+ // table, fully-qualified top-level key, inline table, whitespace-padded header);
266
+ // normalize each assignment to its fully-qualified key so every spelling is seen.
267
+ function cargoConfigOverride(env, cwd) {
268
+ const files = [];
269
+ let dir = cwd;
270
+ for (let depth = 0; depth < 40; depth += 1) {
271
+ files.push(join(dir, ".cargo", "config.toml"), join(dir, ".cargo", "config"));
272
+ const parent = dirname(dir);
273
+ if (parent === dir) {
274
+ break;
275
+ }
276
+ dir = parent;
277
+ }
278
+ const cargoHome = env.CARGO_HOME && env.CARGO_HOME.length > 0 ? env.CARGO_HOME : join(env.HOME ?? "", ".cargo");
279
+ files.push(join(cargoHome, "config.toml"), join(cargoHome, "config"));
280
+ for (const file of files) {
281
+ let section = "";
282
+ for (const raw of readLines(file)) {
283
+ const line = raw.trim();
284
+ if (line.startsWith("#") || line.length === 0) {
285
+ continue;
286
+ }
287
+ const sec = /^\[([^\]]+)\]/.exec(line);
288
+ if (sec) {
289
+ section = normalizeTomlKey(sec[1] ?? "");
290
+ continue;
291
+ }
292
+ const eq = line.indexOf("=");
293
+ if (eq < 0) {
294
+ continue;
295
+ }
296
+ const lhs = line.slice(0, eq);
297
+ const rhs = line.slice(eq + 1);
298
+ const fqKey = normalizeTomlKey(section ? `${section}.${lhs}` : lhs);
299
+ if (fqKey === "source.crates-io.replace-with") {
300
+ return [`cargo config ${file} replaces the crates-io source (origin door shadowed)`];
301
+ }
302
+ if (fqKey === "source.crates-io.registry") {
303
+ return [`cargo config ${file} overrides the crates-io registry`];
304
+ }
305
+ if (fqKey === "registries.crates-io.index") {
306
+ return [`cargo config ${file} overrides the crates-io index`];
307
+ }
308
+ // Inline-table assignment to the table itself: `crates-io = { replace-with = … }`.
309
+ if (fqKey === "source.crates-io" && /\breplace-with\s*=/.test(rhs)) {
310
+ return [`cargo config ${file} replaces the crates-io source (origin door shadowed)`];
311
+ }
312
+ if (fqKey === "source.crates-io" && /\bregistry\s*=/.test(rhs)) {
313
+ return [`cargo config ${file} overrides the crates-io registry`];
314
+ }
315
+ if (fqKey === "registries.crates-io" && /\bindex\s*=/.test(rhs)) {
316
+ return [`cargo config ${file} overrides the crates-io index`];
317
+ }
318
+ }
319
+ }
320
+ return [];
321
+ }
322
+ // cargo also accepts registry redirection through env vars (CARGO_REGISTRY_DEFAULT,
323
+ // CARGO_SOURCE_CRATES_IO_REPLACE_WITH, CARGO_REGISTRIES_CRATES_IO_INDEX), which the
324
+ // throwaway CARGO_HOME config cannot win against.
325
+ function cargoEnvOverride(env) {
326
+ const out = [];
327
+ if (env.CARGO_REGISTRY_DEFAULT && env.CARGO_REGISTRY_DEFAULT.trim()) {
328
+ out.push("CARGO_REGISTRY_DEFAULT overrides the default registry");
329
+ }
330
+ if (env.CARGO_SOURCE_CRATES_IO_REPLACE_WITH && env.CARGO_SOURCE_CRATES_IO_REPLACE_WITH.trim()) {
331
+ out.push("CARGO_SOURCE_CRATES_IO_REPLACE_WITH replaces the crates-io source");
332
+ }
333
+ if (env.CARGO_REGISTRIES_CRATES_IO_INDEX && env.CARGO_REGISTRIES_CRATES_IO_INDEX.trim()) {
334
+ out.push("CARGO_REGISTRIES_CRATES_IO_INDEX overrides the crates-io index");
335
+ }
336
+ return out;
337
+ }
338
+ function readLines(file) {
339
+ try {
340
+ return readFileSync(file, "utf8").split("\n");
341
+ }
342
+ catch {
343
+ return [];
344
+ }
345
+ }