@westbayberry/dg 1.0.53 → 1.0.56
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.
- package/README.md +5 -1
- package/dist/index.mjs +249 -114
- package/dist/packages/cli/src/alt-screen.js +36 -0
- package/dist/packages/cli/src/api.js +322 -0
- package/dist/packages/cli/src/auth.js +218 -0
- package/dist/packages/cli/src/bin.js +386 -0
- package/dist/packages/cli/src/config.js +228 -0
- package/dist/packages/cli/src/discover.js +126 -0
- package/dist/packages/cli/src/first-run.js +135 -0
- package/dist/packages/cli/src/hook.js +360 -0
- package/dist/packages/cli/src/lockfile.js +303 -0
- package/dist/packages/cli/src/npm-wrapper.js +218 -0
- package/dist/packages/cli/src/pip-wrapper.js +273 -0
- package/dist/packages/cli/src/sanitize.js +38 -0
- package/dist/packages/cli/src/scan-core.js +144 -0
- package/dist/packages/cli/src/setup-status.js +46 -0
- package/dist/packages/cli/src/static-output.js +625 -0
- package/dist/packages/cli/src/telemetry.js +141 -0
- package/dist/packages/cli/src/ui/App.js +137 -0
- package/dist/packages/cli/src/ui/InitApp.js +391 -0
- package/dist/packages/cli/src/ui/LoginApp.js +51 -0
- package/dist/packages/cli/src/ui/NpmWrapperApp.js +73 -0
- package/dist/packages/cli/src/ui/PipWrapperApp.js +72 -0
- package/dist/packages/cli/src/ui/components/ConfirmPrompt.js +24 -0
- package/dist/packages/cli/src/ui/components/DemoScanAnimation.js +26 -0
- package/dist/packages/cli/src/ui/components/DurationLine.js +7 -0
- package/dist/packages/cli/src/ui/components/ErrorView.js +30 -0
- package/dist/packages/cli/src/ui/components/FileSavePrompt.js +210 -0
- package/dist/packages/cli/src/ui/components/InteractiveResultsView.js +557 -0
- package/dist/packages/cli/src/ui/components/Mascot.js +33 -0
- package/dist/packages/cli/src/ui/components/ProgressBar.js +51 -0
- package/dist/packages/cli/src/ui/components/ProgressDots.js +35 -0
- package/dist/packages/cli/src/ui/components/ProjectSelector.js +60 -0
- package/dist/packages/cli/src/ui/components/ResultsView.js +105 -0
- package/dist/packages/cli/src/ui/components/ScanResultCard.js +54 -0
- package/dist/packages/cli/src/ui/components/ScoreHeader.js +142 -0
- package/dist/packages/cli/src/ui/components/SetupBanner.js +17 -0
- package/dist/packages/cli/src/ui/components/Spinner.js +11 -0
- package/dist/packages/cli/src/ui/hooks/useExpandAnimation.js +44 -0
- package/dist/packages/cli/src/ui/hooks/useInit.js +341 -0
- package/dist/packages/cli/src/ui/hooks/useLogin.js +121 -0
- package/dist/packages/cli/src/ui/hooks/useNpmWrapper.js +192 -0
- package/dist/packages/cli/src/ui/hooks/usePipWrapper.js +195 -0
- package/dist/packages/cli/src/ui/hooks/useScan.js +202 -0
- package/dist/packages/cli/src/ui/hooks/useTerminalSize.js +29 -0
- package/dist/packages/cli/src/update-check.js +152 -0
- package/dist/packages/cli/src/wizard-demo-data.js +63 -0
- package/dist/src/ecosystem.js +2 -0
- package/dist/src/lockfile/diff.js +38 -0
- package/dist/src/lockfile/parse_package_json.js +41 -0
- package/dist/src/lockfile/parse_package_lock.js +55 -0
- package/dist/src/lockfile/parse_pipfile_lock.js +69 -0
- package/dist/src/lockfile/parse_pnpm_lock.js +62 -0
- package/dist/src/lockfile/parse_poetry_lock.js +71 -0
- package/dist/src/lockfile/parse_requirements.js +83 -0
- package/dist/src/lockfile/parse_yarn_lock.js +66 -0
- package/dist/src/logger.js +21 -0
- package/dist/src/npm/h2pool.js +161 -0
- package/dist/src/npm/registry.js +299 -0
- package/dist/src/npm/tarball.js +274 -0
- package/dist/src/pypi/registry.js +299 -0
- package/dist/src/pypi/tarball.js +361 -0
- package/dist/src/types.js +2 -0
- package/package.json +6 -3
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.initTelemetry = initTelemetry;
|
|
37
|
+
exports.redact = redact;
|
|
38
|
+
exports.captureError = captureError;
|
|
39
|
+
exports.flush = flush;
|
|
40
|
+
/**
|
|
41
|
+
* Anonymous crash reporting via Sentry.
|
|
42
|
+
*
|
|
43
|
+
* Sends: error message, stack trace, Node version, OS, CLI version, command name.
|
|
44
|
+
* Never sends: package names, file paths, API keys, lockfile contents.
|
|
45
|
+
* Disable: DG_TELEMETRY=0 or DO_NOT_TRACK=1
|
|
46
|
+
*/
|
|
47
|
+
const Sentry = __importStar(require("@sentry/node"));
|
|
48
|
+
// Sentry DSNs are public ingest endpoints, not secrets.
|
|
49
|
+
const CLI_DSN = "https://0b48c008cee0061b41bd45029fac2342@o4510904601935872.ingest.us.sentry.io/4511197701931008";
|
|
50
|
+
let initialized = false;
|
|
51
|
+
function initTelemetry(version) {
|
|
52
|
+
if (process.env.DG_TELEMETRY === "0" || process.env.DO_NOT_TRACK === "1")
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
Sentry.init({
|
|
56
|
+
dsn: CLI_DSN,
|
|
57
|
+
release: `dg-cli@${version}`,
|
|
58
|
+
environment: process.env.CI ? "ci" : "local",
|
|
59
|
+
// TODO: reduce sample rate when CLI usage exceeds ~10k DAU
|
|
60
|
+
sampleRate: 1.0,
|
|
61
|
+
sendDefaultPii: false,
|
|
62
|
+
beforeSend(event) {
|
|
63
|
+
// Drop errors hitting RFC 2606 reserved hostnames (negative-path tests)
|
|
64
|
+
if (event.exception?.values?.some(ex => ex.value && /\.(invalid|test|example|localhost)\b/i.test(ex.value))) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (event.request) {
|
|
68
|
+
delete event.request.headers;
|
|
69
|
+
delete event.request.data;
|
|
70
|
+
delete event.request.cookies;
|
|
71
|
+
}
|
|
72
|
+
if (event.breadcrumbs) {
|
|
73
|
+
event.breadcrumbs = event.breadcrumbs.map(b => {
|
|
74
|
+
if (b.message)
|
|
75
|
+
b.message = redact(b.message);
|
|
76
|
+
if (b.data) {
|
|
77
|
+
for (const key of Object.keys(b.data)) {
|
|
78
|
+
if (typeof b.data[key] === "string") {
|
|
79
|
+
b.data[key] = redact(b.data[key]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return b;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (event.exception?.values) {
|
|
87
|
+
for (const ex of event.exception.values) {
|
|
88
|
+
if (ex.value)
|
|
89
|
+
ex.value = redact(ex.value);
|
|
90
|
+
if (ex.stacktrace?.frames) {
|
|
91
|
+
for (const frame of ex.stacktrace.frames) {
|
|
92
|
+
if (frame.filename) {
|
|
93
|
+
frame.filename = frame.filename
|
|
94
|
+
.replace(/^\/Users\/[^/]+/, "[HOME]")
|
|
95
|
+
.replace(/^\/home\/[^/]+/, "[HOME]")
|
|
96
|
+
.replace(/^C:\\Users\\[^\\]+/, "[HOME]");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return event;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
initialized = true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Sentry init failure should never block the CLI
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Redact secrets before sending to Sentry. Intentionally broad on API keys —
|
|
112
|
+
// tighter bounds like {8,} would miss short dev/test tokens and leak them.
|
|
113
|
+
// Being greedy here is the right default.
|
|
114
|
+
function redact(s) {
|
|
115
|
+
return s
|
|
116
|
+
.replace(/dg_(?:live|test)_[A-Za-z0-9_-]{3,}/gi, "[DG_KEY_REDACTED]")
|
|
117
|
+
.replace(/\/Users\/[^\s:]+/g, "[USER_PATH]")
|
|
118
|
+
.replace(/\/home\/[^\s:]+/g, "[USER_PATH]")
|
|
119
|
+
.replace(/C:\\Users\\[^\s:]+/g, "[USER_PATH]")
|
|
120
|
+
.replace(/Bearer [^\s]+/g, "Bearer [REDACTED]");
|
|
121
|
+
}
|
|
122
|
+
function captureError(error, context) {
|
|
123
|
+
if (!initialized)
|
|
124
|
+
return;
|
|
125
|
+
try {
|
|
126
|
+
if (context)
|
|
127
|
+
Sentry.setContext("scan", context);
|
|
128
|
+
Sentry.captureException(error);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Silent
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function flush() {
|
|
135
|
+
if (!initialized)
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
await Sentry.flush(2000);
|
|
139
|
+
}
|
|
140
|
+
catch { /* ignore — best-effort */ }
|
|
141
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.App = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ink_1 = require("ink");
|
|
7
|
+
const auth_1 = require("../auth");
|
|
8
|
+
const useScan_1 = require("./hooks/useScan");
|
|
9
|
+
const Spinner_1 = require("./components/Spinner");
|
|
10
|
+
const ProgressBar_1 = require("./components/ProgressBar");
|
|
11
|
+
const InteractiveResultsView_1 = require("./components/InteractiveResultsView");
|
|
12
|
+
const ErrorView_1 = require("./components/ErrorView");
|
|
13
|
+
const ProjectSelector_1 = require("./components/ProjectSelector");
|
|
14
|
+
const SetupBanner_1 = require("./components/SetupBanner");
|
|
15
|
+
const useTerminalSize_1 = require("./hooks/useTerminalSize");
|
|
16
|
+
const App = ({ config, userStatus, scanUsage, setupIssues = [] }) => {
|
|
17
|
+
const { state, scanSelectedProjects, restartSelection } = (0, useScan_1.useScan)(config);
|
|
18
|
+
const { exit } = (0, ink_1.useApp)();
|
|
19
|
+
(0, useTerminalSize_1.useTerminalSize)(); // keep hook active for resize-triggered re-renders
|
|
20
|
+
const prevPhaseRef = (0, react_1.useRef)(state.phase);
|
|
21
|
+
const altScreenActiveRef = (0, react_1.useRef)(false);
|
|
22
|
+
// Enter alternate screen buffer for the entire app lifecycle
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
24
|
+
if (!process.stdout.isTTY)
|
|
25
|
+
return;
|
|
26
|
+
process.stdout.write("\x1b[?1049h"); // enter alternate screen
|
|
27
|
+
process.stdout.write("\x1b[2J\x1b[H"); // clear + home
|
|
28
|
+
process.stdout.write("\x1b[?1003l"); // disable mouse all-events (prevents scroll → arrow key)
|
|
29
|
+
process.stdout.write("\x1b[?1000l"); // disable mouse click tracking
|
|
30
|
+
altScreenActiveRef.current = true;
|
|
31
|
+
return () => {
|
|
32
|
+
if (altScreenActiveRef.current) {
|
|
33
|
+
process.stdout.write("\x1b[?1049l"); // exit alternate screen
|
|
34
|
+
altScreenActiveRef.current = false;
|
|
35
|
+
}
|
|
36
|
+
process.stdout.write("\x1b[?25h"); // restore cursor
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
// Track phase transitions (Ink handles repainting automatically)
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
prevPhaseRef.current = state.phase;
|
|
42
|
+
}, [state.phase]);
|
|
43
|
+
const leaveAltScreen = (0, react_1.useCallback)(() => {
|
|
44
|
+
if (altScreenActiveRef.current && process.stdout.isTTY) {
|
|
45
|
+
process.stdout.write("\x1b[?1049l");
|
|
46
|
+
altScreenActiveRef.current = false;
|
|
47
|
+
}
|
|
48
|
+
process.stdout.write("\x1b[?25h");
|
|
49
|
+
}, []);
|
|
50
|
+
const handleResultsExit = (0, react_1.useCallback)(() => {
|
|
51
|
+
if (state.phase === "results") {
|
|
52
|
+
const { result } = state;
|
|
53
|
+
if (result.action === "block" && config.mode === "block") {
|
|
54
|
+
process.exitCode = 2;
|
|
55
|
+
}
|
|
56
|
+
else if (result.action === "block" || result.action === "warn") {
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
process.exitCode = 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
leaveAltScreen();
|
|
64
|
+
exit();
|
|
65
|
+
}, [state, config, exit, leaveAltScreen]);
|
|
66
|
+
// Exit alt screen BEFORE writing messages so they appear on the main screen
|
|
67
|
+
const exitWithMessage = (0, react_1.useCallback)((message, exitCode) => {
|
|
68
|
+
process.exitCode = exitCode;
|
|
69
|
+
leaveAltScreen();
|
|
70
|
+
process.stderr.write(message);
|
|
71
|
+
return setTimeout(() => exit(), 0);
|
|
72
|
+
}, [exit, leaveAltScreen]);
|
|
73
|
+
(0, react_1.useEffect)(() => {
|
|
74
|
+
if (state.phase === "empty") {
|
|
75
|
+
const timer = exitWithMessage(`${state.message}\n`, 0);
|
|
76
|
+
return () => clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
if (state.phase === "error") {
|
|
79
|
+
const timer = exitWithMessage(`Error: ${state.error.message}\n`, 3);
|
|
80
|
+
return () => clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
if (state.phase === "trial_exhausted") {
|
|
83
|
+
let msg = "Free trial scans used up. Run `dg login` to create a free account and continue scanning.\n";
|
|
84
|
+
try {
|
|
85
|
+
if ((0, auth_1.getStoredApiKey)()) {
|
|
86
|
+
msg = "Your API key may be invalid or expired. Run `dg logout` then `dg login` to re-authenticate.\n";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch { /* ignore — best-effort */ }
|
|
90
|
+
const timer = exitWithMessage(msg, 1);
|
|
91
|
+
return () => clearTimeout(timer);
|
|
92
|
+
}
|
|
93
|
+
}, [state, exitWithMessage]);
|
|
94
|
+
// Global q/ESC handler for phases that lack their own input handling
|
|
95
|
+
(0, ink_1.useInput)((input, key) => {
|
|
96
|
+
if (state.phase === "discovering" || state.phase === "scanning") {
|
|
97
|
+
if (input === "q" || key.escape) {
|
|
98
|
+
process.exitCode = 0;
|
|
99
|
+
leaveAltScreen();
|
|
100
|
+
exit();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const content = (() => {
|
|
105
|
+
switch (state.phase) {
|
|
106
|
+
case "discovering":
|
|
107
|
+
return (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Searching for dependencies in ${process.cwd()} ...` });
|
|
108
|
+
case "selecting":
|
|
109
|
+
return ((0, jsx_runtime_1.jsx)(ProjectSelector_1.ProjectSelector, { projects: state.projects, onConfirm: scanSelectedProjects, onCancel: () => { process.exitCode = 0; leaveAltScreen(); exit(); }, userStatus: userStatus }));
|
|
110
|
+
case "scanning":
|
|
111
|
+
return ((0, jsx_runtime_1.jsx)(ProgressBar_1.ProgressBar, { value: state.done, total: state.total, label: state.currentBatch.length > 0
|
|
112
|
+
? state.currentBatch[state.currentBatch.length - 1]
|
|
113
|
+
: undefined }));
|
|
114
|
+
case "results":
|
|
115
|
+
return ((0, jsx_runtime_1.jsx)(InteractiveResultsView_1.InteractiveResultsView, { result: state.result, config: config, durationMs: state.durationMs, onExit: handleResultsExit, onBack: restartSelection ?? undefined, discoveredTotal: state.discoveredTotal, userStatus: userStatus, scanUsage: scanUsage }));
|
|
116
|
+
case "empty":
|
|
117
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: state.message });
|
|
118
|
+
case "error":
|
|
119
|
+
return (0, jsx_runtime_1.jsx)(ErrorView_1.ErrorView, { error: state.error });
|
|
120
|
+
case "trial_exhausted": {
|
|
121
|
+
let hasKey = false;
|
|
122
|
+
try {
|
|
123
|
+
hasKey = !!(0, auth_1.getStoredApiKey)();
|
|
124
|
+
}
|
|
125
|
+
catch { /* ignore — best-effort */ }
|
|
126
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", paddingLeft: 2, children: hasKey ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "Your API key may be invalid or expired." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Run ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg logout" }), " then ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg login" }), " to re-authenticate."] })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "Free trial scans used up." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Run ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg login" }), " to create a free account and continue scanning."] })] })) }));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
// Show the setup banner above the results — but not during empty/error/
|
|
131
|
+
// trial_exhausted phases where it would compete with the message the user
|
|
132
|
+
// actually needs to read.
|
|
133
|
+
const showBanner = state.phase === "selecting" &&
|
|
134
|
+
setupIssues.length > 0;
|
|
135
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [showBanner ? (0, jsx_runtime_1.jsx)(SetupBanner_1.SetupBanner, { issues: setupIssues }) : null, content] }));
|
|
136
|
+
};
|
|
137
|
+
exports.App = App;
|