@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.
Files changed (64) hide show
  1. package/README.md +5 -1
  2. package/dist/index.mjs +249 -114
  3. package/dist/packages/cli/src/alt-screen.js +36 -0
  4. package/dist/packages/cli/src/api.js +322 -0
  5. package/dist/packages/cli/src/auth.js +218 -0
  6. package/dist/packages/cli/src/bin.js +386 -0
  7. package/dist/packages/cli/src/config.js +228 -0
  8. package/dist/packages/cli/src/discover.js +126 -0
  9. package/dist/packages/cli/src/first-run.js +135 -0
  10. package/dist/packages/cli/src/hook.js +360 -0
  11. package/dist/packages/cli/src/lockfile.js +303 -0
  12. package/dist/packages/cli/src/npm-wrapper.js +218 -0
  13. package/dist/packages/cli/src/pip-wrapper.js +273 -0
  14. package/dist/packages/cli/src/sanitize.js +38 -0
  15. package/dist/packages/cli/src/scan-core.js +144 -0
  16. package/dist/packages/cli/src/setup-status.js +46 -0
  17. package/dist/packages/cli/src/static-output.js +625 -0
  18. package/dist/packages/cli/src/telemetry.js +141 -0
  19. package/dist/packages/cli/src/ui/App.js +137 -0
  20. package/dist/packages/cli/src/ui/InitApp.js +391 -0
  21. package/dist/packages/cli/src/ui/LoginApp.js +51 -0
  22. package/dist/packages/cli/src/ui/NpmWrapperApp.js +73 -0
  23. package/dist/packages/cli/src/ui/PipWrapperApp.js +72 -0
  24. package/dist/packages/cli/src/ui/components/ConfirmPrompt.js +24 -0
  25. package/dist/packages/cli/src/ui/components/DemoScanAnimation.js +26 -0
  26. package/dist/packages/cli/src/ui/components/DurationLine.js +7 -0
  27. package/dist/packages/cli/src/ui/components/ErrorView.js +30 -0
  28. package/dist/packages/cli/src/ui/components/FileSavePrompt.js +210 -0
  29. package/dist/packages/cli/src/ui/components/InteractiveResultsView.js +557 -0
  30. package/dist/packages/cli/src/ui/components/Mascot.js +33 -0
  31. package/dist/packages/cli/src/ui/components/ProgressBar.js +51 -0
  32. package/dist/packages/cli/src/ui/components/ProgressDots.js +35 -0
  33. package/dist/packages/cli/src/ui/components/ProjectSelector.js +60 -0
  34. package/dist/packages/cli/src/ui/components/ResultsView.js +105 -0
  35. package/dist/packages/cli/src/ui/components/ScanResultCard.js +54 -0
  36. package/dist/packages/cli/src/ui/components/ScoreHeader.js +142 -0
  37. package/dist/packages/cli/src/ui/components/SetupBanner.js +17 -0
  38. package/dist/packages/cli/src/ui/components/Spinner.js +11 -0
  39. package/dist/packages/cli/src/ui/hooks/useExpandAnimation.js +44 -0
  40. package/dist/packages/cli/src/ui/hooks/useInit.js +341 -0
  41. package/dist/packages/cli/src/ui/hooks/useLogin.js +121 -0
  42. package/dist/packages/cli/src/ui/hooks/useNpmWrapper.js +192 -0
  43. package/dist/packages/cli/src/ui/hooks/usePipWrapper.js +195 -0
  44. package/dist/packages/cli/src/ui/hooks/useScan.js +202 -0
  45. package/dist/packages/cli/src/ui/hooks/useTerminalSize.js +29 -0
  46. package/dist/packages/cli/src/update-check.js +152 -0
  47. package/dist/packages/cli/src/wizard-demo-data.js +63 -0
  48. package/dist/src/ecosystem.js +2 -0
  49. package/dist/src/lockfile/diff.js +38 -0
  50. package/dist/src/lockfile/parse_package_json.js +41 -0
  51. package/dist/src/lockfile/parse_package_lock.js +55 -0
  52. package/dist/src/lockfile/parse_pipfile_lock.js +69 -0
  53. package/dist/src/lockfile/parse_pnpm_lock.js +62 -0
  54. package/dist/src/lockfile/parse_poetry_lock.js +71 -0
  55. package/dist/src/lockfile/parse_requirements.js +83 -0
  56. package/dist/src/lockfile/parse_yarn_lock.js +66 -0
  57. package/dist/src/logger.js +21 -0
  58. package/dist/src/npm/h2pool.js +161 -0
  59. package/dist/src/npm/registry.js +299 -0
  60. package/dist/src/npm/tarball.js +274 -0
  61. package/dist/src/pypi/registry.js +299 -0
  62. package/dist/src/pypi/tarball.js +361 -0
  63. package/dist/src/types.js +2 -0
  64. package/package.json +6 -3
@@ -0,0 +1,391 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InitApp = 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 useInit_1 = require("./hooks/useInit");
8
+ const useTerminalSize_1 = require("./hooks/useTerminalSize");
9
+ const Spinner_1 = require("./components/Spinner");
10
+ const ProgressBar_1 = require("./components/ProgressBar");
11
+ const Mascot_1 = require("./components/Mascot");
12
+ const ScanResultCard_1 = require("./components/ScanResultCard");
13
+ const DemoScanAnimation_1 = require("./components/DemoScanAnimation");
14
+ const ProgressDots_1 = require("./components/ProgressDots");
15
+ const wizard_demo_data_1 = require("../wizard-demo-data");
16
+ const sanitize_1 = require("../sanitize");
17
+ const node_path_1 = require("node:path");
18
+ const YES_NO_PHASES = new Set([
19
+ "greet",
20
+ "confirm_hook",
21
+ "confirm_wrap",
22
+ "confirm_scan",
23
+ ]);
24
+ function defaultYesNoCursor(phase) {
25
+ return phase === "confirm_wrap" ? 1 : 0;
26
+ }
27
+ const InitApp = ({ dryRun = false, firstRun = false, returning = false, onReachedDone, onScanRan, }) => {
28
+ const { state, canGoBack, runDetect, runInstallHook, runVerifyHook, runScan, dispatch } = (0, useInit_1.useInit)({ dryRun, firstRun });
29
+ const { exit } = (0, ink_1.useApp)();
30
+ (0, useTerminalSize_1.useTerminalSize)();
31
+ const [yesNoCursor, setYesNoCursor] = (0, react_1.useState)(defaultYesNoCursor(state.phase));
32
+ // Alt-screen management lives in src/alt-screen.ts — it is entered BEFORE
33
+ // `render()` is called so Ink's first paint lands in the alt buffer and its
34
+ // internal diff state matches reality from frame 1. Doing it from a useEffect
35
+ // here caused the "blank until keypress" bug (Ink painted the greet frame
36
+ // to the main buffer, the effect flipped buffers after, diff tracker went
37
+ // stale, no repaint until user input forced one).
38
+ // Defense against the run_scan → result_first_scan transition stall: after a
39
+ // long async scan, Ink has occasionally been observed to skip the repaint
40
+ // that replaces the ProgressBar with the result card. Bumping a trivial
41
+ // state value in a microtask on that specific transition gives Ink a clear
42
+ // reason to diff-and-paint. Cheap, and local to just this transition.
43
+ const prevPhaseRef = (0, react_1.useRef)(state.phase);
44
+ const [, forceTick] = (0, react_1.useState)(0);
45
+ (0, react_1.useEffect)(() => {
46
+ if (prevPhaseRef.current === "run_scan" && state.phase === "result_first_scan") {
47
+ Promise.resolve().then(() => forceTick((n) => n + 1));
48
+ }
49
+ prevPhaseRef.current = state.phase;
50
+ }, [state.phase]);
51
+ (0, react_1.useEffect)(() => {
52
+ if (YES_NO_PHASES.has(state.phase)) {
53
+ setYesNoCursor(defaultYesNoCursor(state.phase));
54
+ }
55
+ }, [state.phase]);
56
+ (0, react_1.useEffect)(() => {
57
+ if (state.phase === "detect") {
58
+ runDetect();
59
+ }
60
+ }, [state.phase, runDetect]);
61
+ (0, react_1.useEffect)(() => {
62
+ if (state.scanRan && onScanRan)
63
+ onScanRan();
64
+ }, [state.scanRan, onScanRan]);
65
+ (0, react_1.useEffect)(() => {
66
+ if (state.phase === "install_hook") {
67
+ runInstallHook();
68
+ }
69
+ if (state.phase === "verify_hook") {
70
+ void runVerifyHook();
71
+ }
72
+ if (state.phase === "run_scan") {
73
+ void runScan();
74
+ }
75
+ if (state.phase === "done") {
76
+ if (onReachedDone)
77
+ onReachedDone();
78
+ }
79
+ }, [state.phase, runInstallHook, runVerifyHook, runScan, dispatch, exit, onReachedDone]);
80
+ (0, ink_1.useInput)((input, key) => {
81
+ const ch = input.toLowerCase();
82
+ if (ch === "q" || key.escape) {
83
+ exit();
84
+ return;
85
+ }
86
+ if (ch === "b" && canGoBack) {
87
+ dispatch({ type: "go_back" });
88
+ return;
89
+ }
90
+ if (ch === "s") {
91
+ switch (state.phase) {
92
+ case "greet":
93
+ dispatch({ type: "welcome_no" });
94
+ return;
95
+ case "value_prop":
96
+ dispatch({ type: "value_prop_continue" });
97
+ return;
98
+ case "demo_result":
99
+ dispatch({ type: "demo_acked" });
100
+ return;
101
+ case "scenario":
102
+ dispatch({ type: "scenario_acked" });
103
+ return;
104
+ case "confirm_hook":
105
+ dispatch({ type: "confirm_hook_no" });
106
+ return;
107
+ case "confirm_wrap":
108
+ dispatch({ type: "wrap_skipped" });
109
+ return;
110
+ case "confirm_scan":
111
+ dispatch({ type: "scan_skipped" });
112
+ return;
113
+ case "show_app_link":
114
+ dispatch({ type: "app_link_acked" });
115
+ return;
116
+ case "result_first_scan":
117
+ dispatch({ type: "first_scan_acked" });
118
+ return;
119
+ case "what_happens_next":
120
+ dispatch({ type: "what_happens_next_acked" });
121
+ return;
122
+ case "pitch_app":
123
+ dispatch({ type: "pitch_acked" });
124
+ return;
125
+ }
126
+ }
127
+ if (YES_NO_PHASES.has(state.phase)) {
128
+ if (key.leftArrow || key.upArrow) {
129
+ setYesNoCursor(0);
130
+ return;
131
+ }
132
+ if (key.rightArrow || key.downArrow) {
133
+ setYesNoCursor(1);
134
+ return;
135
+ }
136
+ }
137
+ switch (state.phase) {
138
+ case "greet":
139
+ if (ch === "y") {
140
+ dispatch({ type: "welcome_yes" });
141
+ }
142
+ else if (ch === "n") {
143
+ dispatch({ type: "welcome_no" });
144
+ }
145
+ else if (key.return) {
146
+ dispatch({ type: yesNoCursor === 0 ? "welcome_yes" : "welcome_no" });
147
+ }
148
+ return;
149
+ case "value_prop":
150
+ if (key.return) {
151
+ dispatch({ type: "value_prop_continue" });
152
+ }
153
+ return;
154
+ case "demo_result":
155
+ if (key.return) {
156
+ dispatch({ type: "demo_acked" });
157
+ }
158
+ return;
159
+ case "scenario":
160
+ if (key.return) {
161
+ dispatch({ type: "scenario_acked" });
162
+ }
163
+ return;
164
+ case "confirm_hook":
165
+ if (ch === "y") {
166
+ dispatch({ type: "confirm_hook_yes" });
167
+ }
168
+ else if (ch === "n") {
169
+ dispatch({ type: "confirm_hook_no" });
170
+ }
171
+ else if (key.return) {
172
+ dispatch({ type: yesNoCursor === 0 ? "confirm_hook_yes" : "confirm_hook_no" });
173
+ }
174
+ return;
175
+ case "confirm_wrap":
176
+ if (ch === "y") {
177
+ dispatch({ type: "wrap_confirmed" });
178
+ }
179
+ else if (ch === "n") {
180
+ dispatch({ type: "wrap_skipped" });
181
+ }
182
+ else if (key.return) {
183
+ dispatch({ type: yesNoCursor === 0 ? "wrap_confirmed" : "wrap_skipped" });
184
+ }
185
+ return;
186
+ case "confirm_scan":
187
+ if (ch === "y") {
188
+ dispatch({ type: "scan_confirmed" });
189
+ }
190
+ else if (ch === "n") {
191
+ dispatch({ type: "scan_skipped" });
192
+ }
193
+ else if (key.return) {
194
+ dispatch({ type: yesNoCursor === 0 ? "scan_confirmed" : "scan_skipped" });
195
+ }
196
+ return;
197
+ case "show_app_link":
198
+ if (key.return) {
199
+ dispatch({ type: "app_link_acked" });
200
+ }
201
+ return;
202
+ case "result_first_scan":
203
+ if (key.return) {
204
+ dispatch({ type: "first_scan_acked" });
205
+ }
206
+ return;
207
+ case "what_happens_next":
208
+ if (key.return) {
209
+ dispatch({ type: "what_happens_next_acked" });
210
+ }
211
+ return;
212
+ case "pitch_app":
213
+ if (key.return) {
214
+ dispatch({ type: "pitch_acked" });
215
+ }
216
+ return;
217
+ case "done":
218
+ if (key.return || ch === " " || ch === "q") {
219
+ exit();
220
+ }
221
+ return;
222
+ }
223
+ });
224
+ const yesNoRow = (yesLabel = "Yes", noLabel = "No") => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: yesNoCursor === 0, bold: yesNoCursor === 0, color: "green", children: ` ${yesLabel} ` }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: yesNoCursor === 1, bold: yesNoCursor === 1, color: "red", children: ` ${noLabel} ` })] }));
225
+ const okButton = ((0, jsx_runtime_1.jsx)(ink_1.Box, { children: (0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: true, bold: true, color: "green", children: ` OK ` }) }));
226
+ const isYesNo = YES_NO_PHASES.has(state.phase);
227
+ const stepHint = ((0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [isYesNo ? "← → choose · Enter confirm · " : "", canGoBack ? "[b] back · " : "", "[s] skip \u00B7 [q] quit"] }) }));
228
+ const currentStep = (0, ProgressDots_1.phaseToStep)(state.phase);
229
+ const Scene = (props) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", paddingTop: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginRight: 1, children: (0, jsx_runtime_1.jsx)(Mascot_1.Mascot, { mood: props.mood, color: props.color }) }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: props.color ?? "white", paddingX: 1, paddingY: 0, flexGrow: 1, children: [(0, jsx_runtime_1.jsx)(ProgressDots_1.ProgressDots, { current: currentStep }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), props.children] })] }));
230
+ const projectName = state.projects.length > 0
231
+ ? (0, sanitize_1.sanitize)((0, node_path_1.basename)(state.projects[0].path)) || "your project"
232
+ : "your project";
233
+ const hookTargetFile = state.hookInfo
234
+ ? (0, sanitize_1.sanitize)(state.hookInfo.targetFile)
235
+ : "";
236
+ const resultMood = (() => {
237
+ const r = state.scanResult;
238
+ if (!r)
239
+ return "idle";
240
+ if (r.status === "ok" && r.result) {
241
+ const action = r.result.result.action;
242
+ if (action === "block")
243
+ return "alarmed";
244
+ if (action === "warn")
245
+ return "alert";
246
+ return "happy";
247
+ }
248
+ if (r.status === "no_packages")
249
+ return "happy";
250
+ return "idle";
251
+ })();
252
+ switch (state.phase) {
253
+ case "greet":
254
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "happy", color: "green", children: [returning ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Hey, welcome back \uD83D\uDC4B I'm ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Dependency Guardian" }), "."] })) : ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Hey \uD83D\uDC4B I'm ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Dependency Guardian" }), "."] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I watch the code that gets added to your project" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "so bad stuff doesn't sneak in." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), returning ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Want a refresher? Takes about a minute." })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Looks like it's your first time here." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Want me to show you around? Takes about a minute." })] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), yesNoRow("Yes", "I know what I'm doing"), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "\u2190 \u2192 choose \u00B7 Enter confirm \u00B7 [q] quit anytime" }) })] }));
255
+ case "value_prop":
256
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "worried", color: "yellow", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["In ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "2025-2026" }), ", over ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "50,000" }), " packages on"] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "npm" }), " and ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "PyPI" }), " have been caught doing bad"] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "things \u2014 stealing passwords, mining crypto," }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "installing backdoors." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "I scan both today, with 20+ more languages planned." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Want to see me catch one in the act?" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
257
+ case "demo_scanning":
258
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: "scanning", color: "cyan", children: (0, jsx_runtime_1.jsx)(DemoScanAnimation_1.DemoScanAnimation, { onComplete: () => dispatch({ type: "demo_complete" }) }) }));
259
+ case "demo_result":
260
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "alarmed", color: "red", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "red", children: "Got one." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ScanResultCard_1.ScanResultCard, { outcome: {
261
+ kind: "demo",
262
+ pkg: wizard_demo_data_1.DEMO_MALICIOUS_PACKAGE.package,
263
+ score: wizard_demo_data_1.DEMO_MALICIOUS_PACKAGE.score,
264
+ action: wizard_demo_data_1.DEMO_MALICIOUS_PACKAGE.action,
265
+ } }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "This one's fake. The next one won't be." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
266
+ case "scenario":
267
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "explaining", color: "cyan", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Here's the situation in ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: projectName }), "."] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "npm packages can ship \"install scripts\" \u2014 code that" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["runs the moment you ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "npm install" }), " them."] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["To catch that, use ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg npm install" }), " or"] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg pip install" }), " instead of the normal command."] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I scan the package first, before any code runs" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "on your machine." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I can also add a safety net to git so nothing" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "slips through at commit time." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
268
+ case "detect":
269
+ if (!state.firstRun) {
270
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Dependency Guardian" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "Looking around..." })] }));
271
+ }
272
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: "scanning", color: "cyan", children: (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Looking around..." }) }));
273
+ case "confirm_hook":
274
+ if (!state.hookInfo)
275
+ return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "..." });
276
+ if (state.hookInfo.alreadyInstalled) {
277
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "happy", color: "green", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "Looks like you already did this step." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "The pre-commit hook is already installed in" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: hookTargetFile }), "."] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Every commit that adds a package gets scanned." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
278
+ }
279
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "curious", color: "green", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Git can run a safety check before each commit." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I'd add my scan to that check \u2014 one line of code." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "After that, every commit that adds a package" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "gets scanned first. If I find malware, the" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "commit stops before your code ships anywhere." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Want me to set it up?" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), yesNoRow("Yes", "No"), stepHint] }));
280
+ case "install_hook":
281
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: "scanning", color: "cyan", children: (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Adding the hook..." }) }));
282
+ case "verify_hook":
283
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: "scanning", color: "cyan", children: (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Testing it..." }) }));
284
+ case "confirm_wrap":
285
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: state.hookError ? "alert" : "happy", color: state.hookError ? "yellow" : "green", children: [state.hookVerifyMs !== null ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "green", children: ["\u2713 Hook works. ", (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: [state.hookVerifyMs, "ms"] }), "."] })) : null, state.hookError ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: ["\u26A0 ", state.hookError] })) : null, (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: ["I can also catch packages at ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "npm install" }), " time, before"] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "they hit your package.json. Adds one line to your shell config." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Want it?" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), yesNoRow(), stepHint] }));
286
+ case "confirm_scan":
287
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "happy", color: "green", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Can I scan ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: projectName }), " now?"] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I'll check what's already installed and show" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "you what I see." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), yesNoRow("Scan", "Skip"), stepHint] }));
288
+ case "run_scan": {
289
+ const p = state.scanProgress;
290
+ if (p && p.total > 0) {
291
+ // Free-float the progress bar (no Scene border) — it already has its
292
+ // own header + spinner and looks out of place nested in a card.
293
+ // No per-package label: scans arrive batch-at-a-time so any single
294
+ // package name shown is just an arbitrary pick from the last batch,
295
+ // not "what's scanning right now" — misleading rather than useful.
296
+ return (0, jsx_runtime_1.jsx)(ProgressBar_1.ProgressBar, { value: p.done, total: p.total });
297
+ }
298
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: "scanning", color: "cyan", children: (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Scanning..." }) }));
299
+ }
300
+ case "result_first_scan": {
301
+ const r = state.scanResult;
302
+ const okScan = r?.status === "ok" && r.result;
303
+ const empty = r?.status === "no_packages";
304
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: resultMood, color: resultMood === "alarmed" ? "red" : resultMood === "alert" ? "yellow" : "green", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Done." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okScan ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["I scanned ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: projectName }), "."] })) : empty ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["I didn't find any packages to scan in ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: projectName }), "."] })) : ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["I tried to scan ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: projectName }), " but ran into a snag."] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), state.scanResult ? ((0, jsx_runtime_1.jsx)(ScanResultCard_1.ScanResultCard, { outcome: { kind: "real", outcome: state.scanResult } })) : null, (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okScan && resultMood === "happy" ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "You're already in good shape." })) : null, (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
305
+ }
306
+ case "what_happens_next":
307
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "explaining", color: "cyan", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "What happens from here." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "You commit normally. If I find nothing, you" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "won't even see me \u2014 the commit just goes through." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "If I find something bad, you'll see a card like" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "the demo I showed you. Read it. If it's a real" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "threat, investigate." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["If it's a false alarm, just add ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "--no-verify" }), ":"] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "git commit -m \"your message\" --no-verify" })] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I won't be offended." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
308
+ case "pitch_app":
309
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "idle", color: "green", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "One last thing." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "I'm watching your laptop. For your team," }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "there's a GitHub App that watches every pull" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "request the same way. Optional \u2014 install it" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "whenever:" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "https://github.com/apps/dependency-guardian" })] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton, stepHint] }));
310
+ case "show_app_link": {
311
+ const scan = state.scanResult;
312
+ const scanLine = [];
313
+ if (scan) {
314
+ if (scan.status === "ok" && scan.result) {
315
+ const r = scan.result.result;
316
+ const verdict = r.action === "block" ? "BLOCKED" : r.action === "warn" ? "WARNING" : "PASSED";
317
+ const color = r.action === "block" ? "red" : r.action === "warn" ? "yellow" : "green";
318
+ scanLine.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: color, children: ["\u2713 Scanned ", scan.result.scannedCount, " package", scan.result.scannedCount === 1 ? "" : "s", " in ", scan.result.durationMs, "ms \u2014 ", verdict, " (score ", r.score, "/100)"] }, "scan-summary"));
319
+ }
320
+ else if (scan.status === "no_packages") {
321
+ scanLine.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: ["\u2713 Scan ran \u2014 nothing to check yet (", scan.result?.durationMs ?? 0, "ms)"] }, "scan-empty"));
322
+ }
323
+ else if (scan.status === "trial_exhausted") {
324
+ scanLine.push((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "\u26A0 Free trial scans used up \u2014 `dg login` to continue" }, "scan-trial"));
325
+ }
326
+ else if (scan.status === "error") {
327
+ scanLine.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: ["\u26A0 Scan didn't finish: ", scan.message] }, "scan-err"));
328
+ }
329
+ scanLine.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp"));
330
+ }
331
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Server-side coverage (optional)" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), scanLine, (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "The CLI protects you on this laptop. Install the GitHub App" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "too for server-side scanning on every PR." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", children: "https://github.com/apps/dependency-guardian/installations/new" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "Press Enter to continue." }), stepHint] }));
332
+ }
333
+ case "done": {
334
+ const lines = [];
335
+ // User said N at greet — skip straight to goodbye.
336
+ if (state.firstRun && state.skipped.has("scenario") && state.engaged) {
337
+ return ((0, jsx_runtime_1.jsxs)(Scene, { mood: "wink", color: "cyan", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "No worries." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), returning ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Catch you later. ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg kitty" }), " any time."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Running your command now. If you change your" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["mind later, ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg kitty" }), " brings me back."] })] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), okButton] }));
338
+ }
339
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "You're set." }, "header"));
340
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp1"));
341
+ if (state.hookInstalled && !state.skipped.has("install_hook")) {
342
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "\u2713" }), " I'm running on every commit"] }, "hook"));
343
+ }
344
+ else {
345
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " ○ Hook (skipped)" }, "hook"));
346
+ }
347
+ if (state.scanRan && state.scanResult) {
348
+ const scan = state.scanResult;
349
+ if (scan.status === "ok" && scan.result) {
350
+ const r = scan.result.result;
351
+ const tail = r.action === "block"
352
+ ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "red", bold: true, children: "found something to block" })
353
+ : r.action === "warn"
354
+ ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "flagged a few things" })
355
+ : (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "all clean" });
356
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "\u2713" }), " Scanned ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: scan.result.scannedCount }), " packages \u2014 ", tail] }, "scan"));
357
+ }
358
+ else if (scan.status === "no_packages") {
359
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " ○ Nothing to scan yet" }, "scan"));
360
+ }
361
+ else {
362
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: [" ⚠ ", scan.message ?? "scan didn't finish"] }, "scan"));
363
+ }
364
+ }
365
+ else if (state.skipped.has("run_scan")) {
366
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " ○ First scan (skipped)" }, "scan"));
367
+ }
368
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "\u2713" }), " You know how to bypass me if I'm wrong"] }, "bypass"));
369
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp2"));
370
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Forget anything? ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg kitty" }), " brings me back."] }, "kitty"));
371
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Full command list: ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "dg help" })] }, "help"));
372
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Curious about more? ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "https://westbayberry.com/docs" })] }, "docs"));
373
+ if (state.hookError) {
374
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp3"));
375
+ lines.push((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: ["Note: ", state.hookError] }, "err"));
376
+ }
377
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp4"));
378
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Build fast, don't get hacked." }, "signoff"));
379
+ if (state.dryRun) {
380
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "(dry run \u2014 no files were modified)" }, "dry"));
381
+ }
382
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }, "sp5"));
383
+ lines.push((0, jsx_runtime_1.jsx)(ink_1.Box, { children: (0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: true, bold: true, color: "green", children: ` OK ` }) }, "ok-btn"));
384
+ if (!state.firstRun) {
385
+ return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: lines }));
386
+ }
387
+ return ((0, jsx_runtime_1.jsx)(Scene, { mood: state.hookError ? "worried" : "proud", color: state.hookError ? "yellow" : "green", children: lines }));
388
+ }
389
+ }
390
+ };
391
+ exports.InitApp = InitApp;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LoginApp = 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 useLogin_1 = require("./hooks/useLogin");
8
+ const Spinner_1 = require("./components/Spinner");
9
+ const LoginApp = () => {
10
+ const { state, openAndPoll } = (0, useLogin_1.useLogin)();
11
+ const { exit } = (0, ink_1.useApp)();
12
+ (0, react_1.useEffect)(() => {
13
+ if (state.phase === "success") {
14
+ process.exitCode = 0;
15
+ const timer = setTimeout(() => exit(), 0);
16
+ return () => clearTimeout(timer);
17
+ }
18
+ if (state.phase === "already_logged_in") {
19
+ process.exitCode = 0;
20
+ const timer = setTimeout(() => exit(), 0);
21
+ return () => clearTimeout(timer);
22
+ }
23
+ if (state.phase === "expired" || state.phase === "error") {
24
+ process.exitCode = 1;
25
+ const timer = setTimeout(() => exit(), 0);
26
+ return () => clearTimeout(timer);
27
+ }
28
+ }, [state, exit]);
29
+ (0, ink_1.useInput)((_input, key) => {
30
+ if (state.phase === "ready" && key.return) {
31
+ openAndPoll();
32
+ }
33
+ });
34
+ switch (state.phase) {
35
+ case "creating":
36
+ return (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Creating login session..." });
37
+ case "already_logged_in":
38
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "Already authenticated." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: ["Run ", "`dg logout`", " first to re-authenticate."] })] }));
39
+ case "ready":
40
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Authenticate your account at:" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", children: state.verifyUrl }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "Press ENTER to open in the browser..." })] }));
41
+ case "waiting":
42
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Authenticate your account at:" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", children: state.verifyUrl }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Waiting for authorization..." })] }));
43
+ case "success":
44
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "green", bold: true, children: ["Logged in as ", state.email] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "Credentials saved." }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Run ", "`dg scan`", " to scan your dependencies."] })] }));
45
+ case "expired":
46
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "Session expired." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: ["Run ", "`dg login`", " to try again."] })] }));
47
+ case "error":
48
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "red", bold: true, children: "Error: " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "red", children: state.message })] }));
49
+ }
50
+ };
51
+ exports.LoginApp = LoginApp;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NpmWrapperApp = 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 useNpmWrapper_1 = require("./hooks/useNpmWrapper");
9
+ const Spinner_1 = require("./components/Spinner");
10
+ const ResultsView_1 = require("./components/ResultsView");
11
+ const ConfirmPrompt_1 = require("./components/ConfirmPrompt");
12
+ const ErrorView_1 = require("./components/ErrorView");
13
+ const NpmWrapperApp = ({ npmArgs, config, }) => {
14
+ const { exit } = (0, ink_1.useApp)();
15
+ const { state, confirmInstall, rejectInstall } = (0, useNpmWrapper_1.useNpmWrapper)(npmArgs, config, exit);
16
+ (0, react_1.useEffect)(() => {
17
+ if (state.phase === "done") {
18
+ process.exitCode = state.exitCode;
19
+ const timer = setTimeout(() => exit(), 0);
20
+ return () => clearTimeout(timer);
21
+ }
22
+ if (state.phase === "trial_exhausted") {
23
+ process.exitCode = 1;
24
+ const timer = setTimeout(() => exit(), 0);
25
+ return () => clearTimeout(timer);
26
+ }
27
+ if (state.phase === "passthrough") {
28
+ // Nothing to render -- runNpm handles stdio directly
29
+ }
30
+ }, [state, exit]);
31
+ const handleConfirm = (0, react_1.useCallback)(() => {
32
+ confirmInstall();
33
+ }, [confirmInstall]);
34
+ const handleReject = (0, react_1.useCallback)(() => {
35
+ rejectInstall();
36
+ }, [rejectInstall]);
37
+ switch (state.phase) {
38
+ case "resolving":
39
+ return ((0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Resolving ${state.count} package${state.count !== 1 ? "s" : ""}...` }));
40
+ case "scanning":
41
+ return ((0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Scanning ${state.count} package${state.count !== 1 ? "s" : ""}...` }));
42
+ case "pass":
43
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "green", children: ["\u2713", " ", state.count, " package", state.count !== 1 ? "s" : "", " scanned", " \u2014 ", "all clear"] }));
44
+ case "warn":
45
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "Warnings detected. Proceeding with install..." })] }));
46
+ case "blocked":
47
+ if (state.dgForce) {
48
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "--dg-force: Bypassing block. Install at your own risk." })] }));
49
+ }
50
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "red", bold: true, children: ["BLOCKED:", " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "red", children: "High-risk packages detected." })] }), (0, jsx_runtime_1.jsx)(ConfirmPrompt_1.ConfirmPrompt, { message: "Proceed with install anyway?", onConfirm: handleConfirm, onReject: handleReject })] }));
51
+ case "installing":
52
+ return (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Installing..." });
53
+ case "done":
54
+ // Will exit via useEffect
55
+ return null;
56
+ case "error":
57
+ if (state.proceed) {
58
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: ["Warning: Scan failed (", state.message, "). Proceeding with install."] }));
59
+ }
60
+ return (0, jsx_runtime_1.jsx)(ErrorView_1.ErrorView, { error: new Error(state.message) });
61
+ case "passthrough":
62
+ return null;
63
+ case "trial_exhausted": {
64
+ let hasKey = false;
65
+ try {
66
+ hasKey = !!(0, auth_1.getStoredApiKey)();
67
+ }
68
+ catch { /* ignore — best-effort */ }
69
+ 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."] })] })) }));
70
+ }
71
+ }
72
+ };
73
+ exports.NpmWrapperApp = NpmWrapperApp;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PipWrapperApp = 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 usePipWrapper_1 = require("./hooks/usePipWrapper");
9
+ const Spinner_1 = require("./components/Spinner");
10
+ const ResultsView_1 = require("./components/ResultsView");
11
+ const ConfirmPrompt_1 = require("./components/ConfirmPrompt");
12
+ const ErrorView_1 = require("./components/ErrorView");
13
+ const PipWrapperApp = ({ pipArgs, config, }) => {
14
+ const { exit } = (0, ink_1.useApp)();
15
+ const { state, confirmInstall, rejectInstall } = (0, usePipWrapper_1.usePipWrapper)(pipArgs, config, exit);
16
+ (0, react_1.useEffect)(() => {
17
+ if (state.phase === "done") {
18
+ process.exitCode = state.exitCode;
19
+ const timer = setTimeout(() => exit(), 0);
20
+ return () => clearTimeout(timer);
21
+ }
22
+ if (state.phase === "trial_exhausted") {
23
+ process.exitCode = 1;
24
+ const timer = setTimeout(() => exit(), 0);
25
+ return () => clearTimeout(timer);
26
+ }
27
+ if (state.phase === "passthrough") {
28
+ // Nothing to render -- runPip handles stdio directly
29
+ }
30
+ }, [state, exit]);
31
+ const handleConfirm = (0, react_1.useCallback)(() => {
32
+ confirmInstall();
33
+ }, [confirmInstall]);
34
+ const handleReject = (0, react_1.useCallback)(() => {
35
+ rejectInstall();
36
+ }, [rejectInstall]);
37
+ switch (state.phase) {
38
+ case "resolving":
39
+ return ((0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Resolving ${state.count} package${state.count !== 1 ? "s" : ""} from PyPI...` }));
40
+ case "scanning":
41
+ return ((0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Scanning ${state.count} Python package${state.count !== 1 ? "s" : ""}...` }));
42
+ case "pass":
43
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "green", children: ["\u2713", " ", state.count, " package", state.count !== 1 ? "s" : "", " scanned", " \u2014 ", "all clear"] }));
44
+ case "warn":
45
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "Warnings detected. Proceeding with install..." })] }));
46
+ case "blocked":
47
+ if (state.dgForce) {
48
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "--dg-force: Bypassing block. Install at your own risk." })] }));
49
+ }
50
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ResultsView_1.ResultsView, { result: state.result, config: config, durationMs: state.result.durationMs }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "red", bold: true, children: ["BLOCKED:", " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "red", children: "High-risk packages detected." })] }), (0, jsx_runtime_1.jsx)(ConfirmPrompt_1.ConfirmPrompt, { message: "Proceed with install anyway?", onConfirm: handleConfirm, onReject: handleReject })] }));
51
+ case "installing":
52
+ return (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: "Installing with pip..." });
53
+ case "done":
54
+ return null;
55
+ case "error":
56
+ if (state.proceed) {
57
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", children: ["Warning: Scan failed (", state.message, "). Proceeding with install."] }));
58
+ }
59
+ return (0, jsx_runtime_1.jsx)(ErrorView_1.ErrorView, { error: new Error(state.message) });
60
+ case "passthrough":
61
+ return null;
62
+ case "trial_exhausted": {
63
+ let hasKey = false;
64
+ try {
65
+ hasKey = !!(0, auth_1.getStoredApiKey)();
66
+ }
67
+ catch { /* ignore — best-effort */ }
68
+ 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."] })] })) }));
69
+ }
70
+ }
71
+ };
72
+ exports.PipWrapperApp = PipWrapperApp;