@westbayberry/dg 2.1.0 → 2.3.0
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 +216 -226
- package/dist/agents/claude-code.js +123 -0
- package/dist/agents/codex.js +65 -0
- package/dist/agents/copilot-cli.js +115 -0
- package/dist/agents/cursor.js +118 -0
- package/dist/agents/gate-posture.js +21 -0
- package/dist/agents/gemini.js +107 -0
- package/dist/agents/persistence.js +351 -0
- package/dist/agents/registry.js +128 -0
- package/dist/agents/routing.js +118 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/windsurf.js +93 -0
- package/dist/api/analyze.js +1 -1
- package/dist/audit/detectors.js +0 -11
- package/dist/audit/events.js +5 -21
- package/dist/audit/rules.js +6 -2
- package/dist/audit-ui/AuditApp.js +2 -0
- package/dist/audit-ui/components/AuditResultsView.js +55 -92
- package/dist/audit-ui/export.js +0 -4
- package/dist/audit-ui/format.js +0 -3
- package/dist/audit-ui/launch.js +10 -1
- package/dist/auth/device-login.js +4 -5
- package/dist/auth/login-app.js +7 -7
- package/dist/auth/store.js +17 -5
- package/dist/bin/dg.js +59 -52
- package/dist/commands/agents.js +275 -0
- package/dist/commands/audit.js +28 -3
- package/dist/commands/config.js +15 -7
- package/dist/commands/cooldown.js +389 -0
- package/dist/commands/decisions.js +23 -9
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/help.js +1 -1
- package/dist/commands/licenses.js +10 -22
- package/dist/commands/logout.js +4 -11
- package/dist/commands/router.js +6 -4
- package/dist/commands/sbom.js +206 -0
- package/dist/commands/scan.js +1 -1
- package/dist/commands/service.js +32 -13
- package/dist/commands/setup.js +196 -27
- package/dist/commands/types.js +1 -0
- package/dist/commands/uninstall.js +2 -1
- package/dist/commands/update.js +17 -8
- package/dist/commands/verify.js +8 -5
- package/dist/config/settings.js +49 -48
- package/dist/decisions/remember-prompt.js +5 -10
- package/dist/export-ui/ExportDialog.js +198 -0
- package/dist/install-ui/LiveInstall.js +5 -4
- package/dist/install-ui/block-render.js +18 -14
- package/dist/install-ui/prep-spinner.js +32 -0
- package/dist/launcher/agent-check.js +896 -0
- package/dist/launcher/agent-hook-exec.js +70 -0
- package/dist/launcher/agent-hook-io.js +39 -0
- package/dist/launcher/cargo-cache.js +40 -0
- package/dist/launcher/classify.js +44 -7
- package/dist/launcher/env.js +105 -26
- package/dist/launcher/install-preflight.js +64 -18
- package/dist/launcher/live-install.js +29 -8
- package/dist/launcher/manifest-screen.js +171 -0
- package/dist/launcher/output-redaction.js +10 -4
- package/dist/launcher/preflight-prompt.js +18 -4
- package/dist/launcher/run.js +178 -103
- package/dist/launcher/spawn-invocation.js +21 -0
- package/dist/policy/cooldown.js +18 -5
- package/dist/policy/evaluate.js +5 -6
- package/dist/policy/pypi-name.js +17 -0
- package/dist/presentation/mode.js +3 -2
- package/dist/presentation/package-page.js +9 -0
- package/dist/presentation/theme.js +7 -7
- package/dist/project/dgfile.js +146 -6
- package/dist/project/override-trust.js +0 -0
- package/dist/proxy/auth.js +42 -0
- package/dist/proxy/ca.js +29 -9
- package/dist/proxy/cooldown-exemptions-file.js +33 -0
- package/dist/proxy/enforcement.js +55 -16
- package/dist/proxy/metadata-map.js +66 -12
- package/dist/proxy/preverified.js +55 -0
- package/dist/proxy/server.js +568 -53
- package/dist/proxy/worker.js +16 -1
- package/dist/publish-set/collect.js +1 -4
- package/dist/publish-set/npm.js +8 -5
- package/dist/publish-set/pack.js +9 -3
- package/dist/runtime/cli.js +0 -4
- package/dist/runtime/fatal.js +31 -0
- package/dist/runtime/first-run.js +14 -12
- package/dist/runtime/node-version.js +43 -6
- package/dist/runtime/nudges.js +35 -2
- package/dist/sbom/cyclonedx.js +211 -0
- package/dist/sbom-ui/SbomApp.js +158 -0
- package/dist/sbom-ui/components/SbomHeader.js +32 -0
- package/dist/sbom-ui/components/SbomList.js +52 -0
- package/dist/sbom-ui/inventory.js +132 -0
- package/dist/sbom-ui/launch.js +51 -0
- package/dist/sbom-ui/run.js +55 -0
- package/dist/sbom-ui/store.js +26 -0
- package/dist/scan/collect.js +10 -6
- package/dist/scan/command.js +17 -10
- package/dist/scan/discovery.js +11 -2
- package/dist/scan/render.js +28 -4
- package/dist/scan/scanner-report.js +15 -9
- package/dist/scan/staged.js +33 -9
- package/dist/scan-ui/LegacyApp.js +9 -13
- package/dist/scan-ui/alt-screen.js +5 -8
- package/dist/scan-ui/components/InteractiveResultsView.js +133 -120
- package/dist/scan-ui/components/ProgressBar.js +3 -14
- package/dist/scan-ui/components/ProjectSelector.js +1 -1
- package/dist/scan-ui/components/ScoreHeader.js +2 -3
- package/dist/scan-ui/components/SetupBanner.js +0 -6
- package/dist/scan-ui/format-helpers.js +61 -5
- package/dist/scan-ui/hooks/useResizeRepaint.js +25 -0
- package/dist/scan-ui/hooks/useScan.js +19 -2
- package/dist/scan-ui/launch.js +7 -4
- package/dist/scan-ui/shims.js +11 -4
- package/dist/scripts/detect.js +7 -2
- package/dist/scripts/gate.js +1 -1
- package/dist/security/sanitize.js +8 -4
- package/dist/service/state.js +28 -9
- package/dist/service/trust-refresh.js +92 -0
- package/dist/service/trust-store.js +5 -9
- package/dist/service/worker.js +23 -1
- package/dist/setup/activate-shell.js +28 -0
- package/dist/setup/git-hook.js +49 -4
- package/dist/setup/plan.js +227 -49
- package/dist/setup/uninstall-standalone.js +25 -0
- package/dist/setup-ui/gate.js +39 -0
- package/dist/setup-ui/offer.js +42 -0
- package/dist/setup-ui/selector.js +27 -0
- package/dist/setup-ui/tasks.js +56 -0
- package/dist/setup-ui/wizard.js +233 -0
- package/dist/standalone/uninstall.mjs +2123 -0
- package/dist/state/cooldown-held.js +66 -0
- package/dist/state/index.js +1 -0
- package/dist/state/locks.js +4 -2
- package/dist/state/store.js +2 -1
- package/dist/util/external-tool.js +25 -0
- package/dist/util/git.js +10 -3
- package/dist/util/report-writer.js +57 -0
- package/dist/verify/local.js +242 -44
- package/dist/verify/package-check.js +87 -29
- package/dist/verify/preflight.js +242 -49
- package/dist/verify/render.js +15 -1
- package/npm-shrinkwrap.json +2383 -0
- package/package.json +7 -8
- package/NOTICE +0 -5
- package/dist/commands/completion.js +0 -117
- package/dist/commands/explain.js +0 -236
- package/dist/commands/unavailable.js +0 -11
- package/dist/scripts/rebuild.js +0 -28
- package/dist/telemetry/events.js +0 -40
package/dist/api/analyze.js
CHANGED
|
@@ -328,7 +328,7 @@ function normalizeAction(action) {
|
|
|
328
328
|
if (action === "block" || action === "warn" || action === "analysis_incomplete" || action === "pass") {
|
|
329
329
|
return action;
|
|
330
330
|
}
|
|
331
|
-
return "
|
|
331
|
+
return "analysis_incomplete";
|
|
332
332
|
}
|
|
333
333
|
export function mergeAnalyzeResponses(responses) {
|
|
334
334
|
const first = responses[0];
|
package/dist/audit/detectors.js
CHANGED
|
@@ -133,17 +133,6 @@ function contentChecks(file, push) {
|
|
|
133
133
|
}
|
|
134
134
|
function structuralProjectChecks(context, push) {
|
|
135
135
|
if (context.ecosystem === "npm" && context.packageJson) {
|
|
136
|
-
if (context.packageJson.private === true) {
|
|
137
|
-
push({
|
|
138
|
-
id: "publishing-private",
|
|
139
|
-
category: "structural",
|
|
140
|
-
severity: 4,
|
|
141
|
-
title: "Package is marked private but is being audited for publish",
|
|
142
|
-
recommendation: "A private package being published is almost always a mistake — remove \"private\" or do not publish.",
|
|
143
|
-
location: "package.json",
|
|
144
|
-
evidence: "\"private\": true"
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
136
|
if (!context.hasFilesAllowlist) {
|
|
148
137
|
push({
|
|
149
138
|
id: "no-files-allowlist",
|
package/dist/audit/events.js
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { authStatus } from "../auth/store.js";
|
|
4
|
-
import { loadUserConfig } from "../config/settings.js";
|
|
5
3
|
import { resolveDgPaths } from "../state/index.js";
|
|
6
4
|
export function auditLogPath(paths) {
|
|
7
5
|
return join(paths.stateDir, "audit.jsonl");
|
|
8
6
|
}
|
|
9
|
-
export function webhookOutboxPath(paths) {
|
|
10
|
-
return join(paths.stateDir, "webhooks.jsonl");
|
|
11
|
-
}
|
|
12
7
|
export function recordAuditEvent(event, env = process.env) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const status = authStatus(env);
|
|
19
|
-
if (!config.webhooks.enabled || !status.authenticated) {
|
|
8
|
+
try {
|
|
9
|
+
appendJsonLine(auditLogPath(resolveDgPaths(env)), event);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
20
13
|
return false;
|
|
21
14
|
}
|
|
22
|
-
const paths = resolveDgPaths(env);
|
|
23
|
-
appendJsonLine(webhookOutboxPath(paths), {
|
|
24
|
-
...event,
|
|
25
|
-
auth: {
|
|
26
|
-
source: status.source,
|
|
27
|
-
token: status.tokenPreview
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
return true;
|
|
31
15
|
}
|
|
32
16
|
function appendJsonLine(path, value) {
|
|
33
17
|
mkdirSync(dirname(path), {
|
package/dist/audit/rules.js
CHANGED
|
@@ -384,7 +384,7 @@ export const CONTENT_RULES = [
|
|
|
384
384
|
{
|
|
385
385
|
id: "db-connection-string",
|
|
386
386
|
re: /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:@\s/]+:[^@\s/]+@[^/\s]+/u,
|
|
387
|
-
allow: /:\/\/[^:@/]+:(password|pass|user|example|changeme|xxx+|\*+)@|@(localhost|127\.0\.0\.1|example\.|host)/iu,
|
|
387
|
+
allow: /:\/\/[^:@/]+:(password|pass|user|example|changeme|xxx+|\*+)@|@(localhost|127\.0\.0\.1|example\.|host\b)/iu,
|
|
388
388
|
category: "bundled-secret",
|
|
389
389
|
severity: 4,
|
|
390
390
|
title: "Database connection string with password embedded in a published file",
|
|
@@ -421,6 +421,10 @@ export const RISKY_SCRIPT_NAMES = [
|
|
|
421
421
|
"preuninstall",
|
|
422
422
|
"uninstall"
|
|
423
423
|
];
|
|
424
|
-
|
|
424
|
+
// Best-effort heuristic on the LOCAL audit only; a public regex is evadable, so
|
|
425
|
+
// the authoritative verdict is the behavioral scanner. Covers pipe-to-shell, eval,
|
|
426
|
+
// node -e, python -c, base64 -d, curl|sh, AND download-then-execute where the fetch
|
|
427
|
+
// is chained (&& / ;) to a shell or a freshly-downloaded file.
|
|
428
|
+
export const DANGEROUS_SCRIPT_RE = /\|\s*(?:sh|bash|zsh)\b|\beval\s|node\s+-e\b|python[0-9.]*\s+-c\b|base64\s+-d|(?:curl|wget|fetch)\b[^\n]*\|\s*\w*sh\b|(?:curl|wget|fetch)\b[^\n]*(?:&&|;)\s*(?:sh|bash|zsh|source|\.\/)/iu;
|
|
425
429
|
export const INVISIBLE_UNICODE_RE = /[\u200B-\u200F\u2060-\u2064\u202A-\u202E\u2066-\u2069\uFEFF]/u;
|
|
426
430
|
export const BIDI_OVERRIDE_RE = /[\u202A-\u202E\u2066-\u2069]/u;
|
|
@@ -3,8 +3,10 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
3
3
|
import { Box, useApp } from "ink";
|
|
4
4
|
import { combineAction, auditExitCode, displayTarget } from "../commands/audit.js";
|
|
5
5
|
import { AuditResultsView } from "./components/AuditResultsView.js";
|
|
6
|
+
import { useResizeRepaint } from "../scan-ui/hooks/useResizeRepaint.js";
|
|
6
7
|
export const AuditApp = ({ gathered, initialDeep, deepPromise, onExitCode }) => {
|
|
7
8
|
const { exit } = useApp();
|
|
9
|
+
useResizeRepaint();
|
|
8
10
|
const [deep, setDeep] = useState(initialDeep);
|
|
9
11
|
useEffect(() => {
|
|
10
12
|
if (!deepPromise)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useMemo, useRef, useState } from "react";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
-
import { writeFileSync } from "node:fs";
|
|
6
|
-
import { resolve as resolvePath } from "node:path";
|
|
7
5
|
import { findingLocation } from "../../audit/detectors.js";
|
|
8
6
|
import { isLoggedIn } from "../../scan-ui/shims.js";
|
|
9
7
|
import { clearScreen } from "../../scan-ui/alt-screen.js";
|
|
@@ -12,7 +10,10 @@ import { pad, truncate } from "../../scan-ui/format-helpers.js";
|
|
|
12
10
|
import { AuditHeader } from "./AuditHeader.js";
|
|
13
11
|
import { DeepStatusRow } from "./DeepStatusRow.js";
|
|
14
12
|
import { countSummary, severityGlyph, severityRole } from "../format.js";
|
|
15
|
-
import { buildExport
|
|
13
|
+
import { buildExport } from "../export.js";
|
|
14
|
+
import { ExportDialog, loginRequiredToast } from "../../export-ui/ExportDialog.js";
|
|
15
|
+
import { resolvePresentation } from "../../presentation/mode.js";
|
|
16
|
+
import { createTheme } from "../../presentation/theme.js";
|
|
16
17
|
const ROLE_PAINT = {
|
|
17
18
|
block: chalk.red,
|
|
18
19
|
warn: chalk.yellow,
|
|
@@ -23,6 +24,11 @@ function glyphPaint(severity) {
|
|
|
23
24
|
}
|
|
24
25
|
const FIXED_CHROME = 12;
|
|
25
26
|
const EXPAND_CHROME = 10;
|
|
27
|
+
const EXPORT_FORMATS = [
|
|
28
|
+
{ format: "json", label: "JSON" },
|
|
29
|
+
{ format: "md", label: "Markdown" },
|
|
30
|
+
{ format: "txt", label: "Plain text" }
|
|
31
|
+
];
|
|
26
32
|
export const AuditResultsView = ({ findings, action, artifact, ecosystem, target, fileCount, publishSetSource, deep, onExit }) => {
|
|
27
33
|
const { rows: termRows, cols: termCols } = useTerminalSize();
|
|
28
34
|
const [cursor, setCursor] = useState(0);
|
|
@@ -36,17 +42,22 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
36
42
|
const [showHelp, setShowHelp] = useState(false);
|
|
37
43
|
const showHelpRef = useRef(showHelp);
|
|
38
44
|
showHelpRef.current = showHelp;
|
|
39
|
-
const [
|
|
40
|
-
const
|
|
41
|
-
|
|
45
|
+
const [exportDialog, setExportDialog] = useState(null);
|
|
46
|
+
const exportDialogRef = useRef(exportDialog);
|
|
47
|
+
exportDialogRef.current = exportDialog;
|
|
48
|
+
const theme = useMemo(() => createTheme(resolvePresentation().color), []);
|
|
42
49
|
const [exportMsg, setExportMsg] = useState(null);
|
|
43
50
|
const exportTimer = useRef(null);
|
|
44
|
-
const showExportMsg = (
|
|
45
|
-
setExportMsg(
|
|
51
|
+
const showExportMsg = (text, tone = "ok") => {
|
|
52
|
+
setExportMsg({ text, tone });
|
|
46
53
|
if (exportTimer.current)
|
|
47
54
|
clearTimeout(exportTimer.current);
|
|
48
55
|
exportTimer.current = setTimeout(() => setExportMsg(null), 4000);
|
|
49
56
|
};
|
|
57
|
+
useEffect(() => () => {
|
|
58
|
+
if (exportTimer.current)
|
|
59
|
+
clearTimeout(exportTimer.current);
|
|
60
|
+
}, []);
|
|
50
61
|
const filtered = useMemo(() => {
|
|
51
62
|
if (!searchQuery)
|
|
52
63
|
return findings;
|
|
@@ -88,70 +99,40 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
88
99
|
setViewport(clamped - listRows + 1);
|
|
89
100
|
}
|
|
90
101
|
};
|
|
91
|
-
const
|
|
102
|
+
const openExport = () => {
|
|
92
103
|
if (!isLoggedIn()) {
|
|
93
|
-
showExportMsg(
|
|
104
|
+
showExportMsg(loginRequiredToast(), "nudge");
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
const input = {
|
|
108
|
+
target,
|
|
109
|
+
artifact,
|
|
110
|
+
ecosystem,
|
|
111
|
+
action,
|
|
112
|
+
fileCount,
|
|
113
|
+
publishSetSource,
|
|
114
|
+
findings,
|
|
115
|
+
deep: deep ?? { ran: false, reason: "deep behavioral scan did not run" }
|
|
116
|
+
};
|
|
117
|
+
setExportDialog(EXPORT_FORMATS.map(({ format, label }) => ({
|
|
118
|
+
label,
|
|
119
|
+
defaultName: `dg-audit.${format}`,
|
|
120
|
+
render: () => buildExport(input, format).body
|
|
121
|
+
})));
|
|
122
|
+
};
|
|
123
|
+
const handleExportDone = (result) => {
|
|
124
|
+
setExportDialog(null);
|
|
125
|
+
clearScreen();
|
|
126
|
+
if (result === null)
|
|
127
|
+
return;
|
|
128
|
+
if ("path" in result)
|
|
129
|
+
showExportMsg(`✓ Exported to ${result.path}`);
|
|
130
|
+
else
|
|
131
|
+
showExportMsg(`Export failed: ${result.error}`, "error");
|
|
115
132
|
};
|
|
116
133
|
useInput((input, key) => {
|
|
117
|
-
if (
|
|
118
|
-
const menu = exportMenuRef.current;
|
|
119
|
-
const FORMATS = ["json", "md", "txt"];
|
|
120
|
-
const move = (cur, dir) => {
|
|
121
|
-
const i = FORMATS.indexOf(cur);
|
|
122
|
-
return FORMATS[Math.max(0, Math.min(FORMATS.length - 1, i + dir))] ?? cur;
|
|
123
|
-
};
|
|
124
|
-
if (key.escape) {
|
|
125
|
-
setExportMenu(null);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (input === "q") {
|
|
129
|
-
setExportMenu(null);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (key.return) {
|
|
133
|
-
const format = menu.format;
|
|
134
|
-
setExportMenu(null);
|
|
135
|
-
clearScreen();
|
|
136
|
-
runExport(format);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (key.tab || key.leftArrow || key.rightArrow) {
|
|
140
|
-
setExportMenu({ ...menu, activeRow: menu.activeRow === "scope" ? "format" : "scope" });
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (key.upArrow || input === "k") {
|
|
144
|
-
if (menu.activeRow === "format")
|
|
145
|
-
setExportMenu({ ...menu, format: move(menu.format, -1) });
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (key.downArrow || input === "j") {
|
|
149
|
-
if (menu.activeRow === "format")
|
|
150
|
-
setExportMenu({ ...menu, format: move(menu.format, 1) });
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
134
|
+
if (exportDialogRef.current)
|
|
153
135
|
return;
|
|
154
|
-
}
|
|
155
136
|
if (showHelpRef.current) {
|
|
156
137
|
if (input === "?" || key.escape)
|
|
157
138
|
setShowHelp(false);
|
|
@@ -219,11 +200,7 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
219
200
|
return;
|
|
220
201
|
}
|
|
221
202
|
if (input === "e") {
|
|
222
|
-
|
|
223
|
-
showExportMsg("Export requires `dg login` (free account)");
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
setExportMenu({ format: "json", activeRow: "scope" });
|
|
203
|
+
openExport();
|
|
227
204
|
return;
|
|
228
205
|
}
|
|
229
206
|
return;
|
|
@@ -233,11 +210,7 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
233
210
|
return;
|
|
234
211
|
}
|
|
235
212
|
if (input === "e") {
|
|
236
|
-
|
|
237
|
-
showExportMsg("Export requires `dg login` (free account)");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
setExportMenu({ format: "json", activeRow: "scope" });
|
|
213
|
+
openExport();
|
|
241
214
|
return;
|
|
242
215
|
}
|
|
243
216
|
if (input === "q") {
|
|
@@ -269,19 +242,9 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
269
242
|
}
|
|
270
243
|
else if (input === "/")
|
|
271
244
|
setSearchMode(true);
|
|
272
|
-
});
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
{ value: "json", label: "JSON" },
|
|
276
|
-
{ value: "md", label: "Markdown" },
|
|
277
|
-
{ value: "txt", label: "Plain text" }
|
|
278
|
-
];
|
|
279
|
-
const renderColumn = (title, isActive, rows) => (_jsxs(Box, { flexDirection: "column", marginRight: 4, children: [_jsxs(Text, { children: [isActive ? chalk.cyan("▌ ") : " ", isActive ? chalk.bold(title) : chalk.dim(title)] }), rows] }));
|
|
280
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(AuditHeader, { action: action, artifact: artifact, ecosystem: ecosystem, countSummary: countSummary(findings), fileCount: fileCount, fallback: publishSetSource === "fallback" }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, children: [_jsx(Text, { bold: true, children: "Export" }), _jsx(Text, { children: "" }), _jsxs(Box, { flexDirection: "row", children: [renderColumn("What", exportMenu.activeRow === "scope", (_jsxs(Box, { children: [_jsx(Box, { width: 5, flexShrink: 0, children: _jsxs(Text, { children: [" ", exportMenu.activeRow === "scope" ? chalk.cyan("●") : chalk.green("●")] }) }), _jsx(Text, { children: chalk.bold("Findings") })] }))), renderColumn("Format", exportMenu.activeRow === "format", (_jsx(_Fragment, { children: FORMATS_RENDER.map((r) => {
|
|
281
|
-
const selected = r.value === exportMenu.format;
|
|
282
|
-
const bullet = selected ? (exportMenu.activeRow === "format" ? chalk.cyan("●") : chalk.green("●")) : chalk.dim("○");
|
|
283
|
-
return (_jsxs(Box, { children: [_jsx(Box, { width: 5, flexShrink: 0, children: _jsxs(Text, { children: [" ", bullet] }) }), _jsx(Text, { children: selected ? chalk.bold(r.label) : r.label })] }, r.value));
|
|
284
|
-
}) })))] })] }), _jsx(Text, { dimColor: true, children: chalk.dim("─".repeat(Math.max(20, termCols - 4))) }), _jsxs(Text, { children: [" ", chalk.bold.cyan("↑↓"), " ", chalk.dim("scroll"), " ", chalk.bold.cyan("←→/Tab"), " ", chalk.dim("switch row"), " ", chalk.bold.cyan("⏎"), " ", chalk.dim("export"), " ", chalk.bold.cyan("Esc"), " ", chalk.dim("cancel")] })] }));
|
|
245
|
+
}, { isActive: exportDialog === null });
|
|
246
|
+
if (exportDialog) {
|
|
247
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(AuditHeader, { action: action, artifact: artifact, ecosystem: ecosystem, countSummary: countSummary(findings), fileCount: fileCount, fallback: publishSetSource === "fallback" }), _jsx(ExportDialog, { options: exportDialog, theme: theme, cwd: process.cwd(), onDone: handleExportDone })] }));
|
|
285
248
|
}
|
|
286
249
|
if (showHelp) {
|
|
287
250
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(AuditHeader, { action: action, artifact: artifact, ecosystem: ecosystem, countSummary: countSummary(findings), fileCount: fileCount, fallback: publishSetSource === "fallback" }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Text, { bold: true, children: "Keyboard Shortcuts" }), _jsx(Text, { children: "" }), _jsx(Text, { bold: true, children: " Navigation" }), _jsxs(Text, { children: [" ", chalk.cyan("↑ k"), " ", chalk.dim("Move up")] }), _jsxs(Text, { children: [" ", chalk.cyan("↓ j"), " ", chalk.dim("Move down")] }), _jsxs(Text, { children: [" ", chalk.cyan("g"), " ", chalk.dim("Jump to top")] }), _jsxs(Text, { children: [" ", chalk.cyan("G"), " ", chalk.dim("Jump to bottom")] }), _jsxs(Text, { children: [" ", chalk.cyan("PgUp"), " ", chalk.dim("Page up")] }), _jsxs(Text, { children: [" ", chalk.cyan("PgDn"), " ", chalk.dim("Page down")] }), _jsx(Text, { children: "" }), _jsx(Text, { bold: true, children: " Actions" }), _jsxs(Text, { children: [" ", chalk.cyan("⏎"), " ", chalk.dim("Expand / collapse finding (evidence + recommendation)")] }), _jsxs(Text, { children: [" ", chalk.cyan("/"), " ", chalk.dim("Search findings")] }), _jsxs(Text, { children: [" ", chalk.cyan("e"), " ", chalk.dim("Export menu — Findings as JSON / Markdown / text")] }), _jsxs(Text, { children: [" ", chalk.cyan("q"), " ", chalk.dim("Quit")] }), _jsx(Text, { children: "" }), _jsxs(Text, { dimColor: true, children: [" Press ", chalk.bold.cyan("?"), " or ", chalk.bold.cyan("Esc"), " to close"] })] })] }));
|
|
@@ -303,5 +266,5 @@ export const AuditResultsView = ({ findings, action, artifact, ecosystem, target
|
|
|
303
266
|
return (_jsxs(Text, { backgroundColor: "#1a1a2e", children: [chalk.cyan("▌"), " ", paint(glyph), " ", chalk.bold(loc), chalk.dim(title)] }, `${finding.id}|${finding.location}`));
|
|
304
267
|
}
|
|
305
268
|
return (_jsxs(Text, { children: [" ", paint(glyph), " ", loc, chalk.dim(title)] }, `${finding.id}|${finding.location}`));
|
|
306
|
-
}), belowCount > 0 && _jsxs(Text, { dimColor: true, children: [chalk.cyan(" ↓"), " ", belowCount, " more below"] }), expanded && focused && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 4, children: [_jsxs(Text, { bold: true, children: [findingLocation(focused), chalk.dim(" — "), focused.title] }), expandLines.slice(expandScroll, expandScroll + expandViewportRows).map((line, i) => (_jsx(Text, { children: line }, i))), expandLines.length > expandViewportRows && (_jsx(Text, { dimColor: true, children: ` ${expandScroll + 1}-${Math.min(expandScroll + expandViewportRows, expandLines.length)} / ${expandLines.length} (Esc collapse)` }))] }))] }), _jsx(Text, { dimColor: true, children: chalk.dim("─".repeat(Math.max(20, termCols - 4))) }), _jsx(DeepStatusRow, { deep: deep }), _jsx(Text, { dimColor: true, children: chalk.dim("─".repeat(Math.max(20, termCols - 4))) }), searchMode ? (_jsxs(Text, { children: [" ", chalk.bold.cyan("/"), " ", searchQuery, chalk.cyan("█"), " ", chalk.dim("Esc clear · Enter confirm")] })) : (_jsxs(Text, { children: [" ", filtered.length > 0 && (_jsxs(_Fragment, { children: [chalk.bold.cyan("↑↓"), " ", chalk.dim("navigate"), " ", chalk.bold.cyan("⏎"), " ", chalk.dim(expanded ? "collapse" : "expand"), " ", chalk.bold.cyan("/"), " ", chalk.dim("search"), " "] })), chalk.bold.cyan("e"), " ", chalk.dim("export"), " ", chalk.bold.cyan("?"), " ", chalk.dim("help"), " ", chalk.bold.cyan("q"), " ", chalk.dim("quit"), exportMsg && _jsxs(_Fragment, { children: [" ", chalk.green(exportMsg)] })] }))] }));
|
|
269
|
+
}), belowCount > 0 && _jsxs(Text, { dimColor: true, children: [chalk.cyan(" ↓"), " ", belowCount, " more below"] }), expanded && focused && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 4, children: [_jsxs(Text, { bold: true, children: [findingLocation(focused), chalk.dim(" — "), focused.title] }), expandLines.slice(expandScroll, expandScroll + expandViewportRows).map((line, i) => (_jsx(Text, { children: line }, i))), expandLines.length > expandViewportRows && (_jsx(Text, { dimColor: true, children: ` ${expandScroll + 1}-${Math.min(expandScroll + expandViewportRows, expandLines.length)} / ${expandLines.length} (Esc collapse)` }))] }))] }), _jsx(Text, { dimColor: true, children: chalk.dim("─".repeat(Math.max(20, termCols - 4))) }), _jsx(DeepStatusRow, { deep: deep }), _jsx(Text, { dimColor: true, children: chalk.dim("─".repeat(Math.max(20, termCols - 4))) }), searchMode ? (_jsxs(Text, { children: [" ", chalk.bold.cyan("/"), " ", searchQuery, chalk.cyan("█"), " ", chalk.dim("Esc clear · Enter confirm")] })) : (_jsxs(Text, { children: [" ", filtered.length > 0 && (_jsxs(_Fragment, { children: [chalk.bold.cyan("↑↓"), " ", chalk.dim("navigate"), " ", chalk.bold.cyan("⏎"), " ", chalk.dim(expanded ? "collapse" : "expand"), " ", chalk.bold.cyan("/"), " ", chalk.dim("search"), " "] })), chalk.bold.cyan("e"), " ", chalk.dim("export"), " ", chalk.bold.cyan("?"), " ", chalk.dim("help"), " ", chalk.bold.cyan("q"), " ", chalk.dim("quit"), exportMsg && _jsxs(_Fragment, { children: [" ", exportMsg.tone === "nudge" ? exportMsg.text : (exportMsg.tone === "error" ? chalk.red : chalk.green)(exportMsg.text)] })] }))] }));
|
|
307
270
|
};
|
package/dist/audit-ui/export.js
CHANGED
|
@@ -78,7 +78,3 @@ export function buildExport(input, format) {
|
|
|
78
78
|
return { body: buildMd(input), ext: "md" };
|
|
79
79
|
return { body: buildTxt(input), ext: "txt" };
|
|
80
80
|
}
|
|
81
|
-
export function exportFilename(format, now = new Date()) {
|
|
82
|
-
const ts = now.toISOString().replace(/[:T]/g, "-").replace(/\..*$/, "");
|
|
83
|
-
return `dg-audit-${ts}-findings.${format}`;
|
|
84
|
-
}
|
package/dist/audit-ui/format.js
CHANGED
|
@@ -17,9 +17,6 @@ export function severityRole(severity) {
|
|
|
17
17
|
export function verdictGlyph(action) {
|
|
18
18
|
return action === "block" ? "✘" : action === "warn" ? WARN_GLYPH : "✓";
|
|
19
19
|
}
|
|
20
|
-
export function verdictRole(action) {
|
|
21
|
-
return action;
|
|
22
|
-
}
|
|
23
20
|
export function countSummary(findings) {
|
|
24
21
|
const blocking = findings.filter((finding) => finding.severity >= 4).length;
|
|
25
22
|
const warnings = findings.filter((finding) => finding.severity === 3).length;
|
package/dist/audit-ui/launch.js
CHANGED
|
@@ -3,11 +3,18 @@ export function shouldLaunchAuditTui(options) {
|
|
|
3
3
|
if (options.format !== "text" || options.outputPath) {
|
|
4
4
|
return false;
|
|
5
5
|
}
|
|
6
|
+
if (process.env.TERM === "dumb") {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
if (!process.stdin.isTTY) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
6
12
|
return resolvePresentation().mode === "rich";
|
|
7
13
|
}
|
|
8
14
|
export async function launchAuditTui(props) {
|
|
9
15
|
const ci = process.env.CI;
|
|
10
|
-
|
|
16
|
+
const ciCleared = ci === "" || ci === "0" || ci === "false";
|
|
17
|
+
if (ciCleared) {
|
|
11
18
|
delete process.env.CI;
|
|
12
19
|
}
|
|
13
20
|
const [{ render }, react, app, altScreen] = await Promise.all([
|
|
@@ -28,6 +35,8 @@ export async function launchAuditTui(props) {
|
|
|
28
35
|
await instance.waitUntilExit();
|
|
29
36
|
}
|
|
30
37
|
finally {
|
|
38
|
+
if (ciCleared)
|
|
39
|
+
process.env.CI = ci;
|
|
31
40
|
altScreen.leaveTui();
|
|
32
41
|
}
|
|
33
42
|
return exitCode;
|
|
@@ -191,6 +191,7 @@ export async function fetchAccountStatus(token, env, fetchImpl, timeoutMs = 5_00
|
|
|
191
191
|
const body = (await response.json());
|
|
192
192
|
return {
|
|
193
193
|
tier: typeof body.tier === "string" && body.tier.length > 0 ? body.tier.toLowerCase() : null,
|
|
194
|
+
name: typeof body.name === "string" && body.name.trim().length > 0 ? body.name.trim() : null,
|
|
194
195
|
scansUsed: typeof body.scansUsed === "number" && Number.isFinite(body.scansUsed) ? body.scansUsed : null,
|
|
195
196
|
scansLimit: typeof body.scansLimit === "number" && Number.isFinite(body.scansLimit) ? body.scansLimit : null
|
|
196
197
|
};
|
|
@@ -202,9 +203,6 @@ export async function fetchAccountStatus(token, env, fetchImpl, timeoutMs = 5_00
|
|
|
202
203
|
clearTimeout(timer);
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
|
-
export async function fetchAccountTier(token, env, fetchImpl) {
|
|
206
|
-
return (await fetchAccountStatus(token, env, fetchImpl))?.tier ?? null;
|
|
207
|
-
}
|
|
208
206
|
export async function runDeviceLogin(io = {}) {
|
|
209
207
|
const env = io.env ?? process.env;
|
|
210
208
|
const fetchImpl = io.fetchImpl ?? fetch;
|
|
@@ -236,8 +234,9 @@ export async function runDeviceLogin(io = {}) {
|
|
|
236
234
|
for (;;) {
|
|
237
235
|
const result = await pollAuthSession(webBase, session.sessionId, fetchImpl);
|
|
238
236
|
if (result.status === "complete" && result.apiKey) {
|
|
239
|
-
const
|
|
240
|
-
|
|
237
|
+
const account = await fetchAccountStatus(result.apiKey, env, fetchImpl);
|
|
238
|
+
const tier = account?.tier ?? null;
|
|
239
|
+
writeAuthState({ token: result.apiKey, email: result.email, tier: tier ?? undefined, name: account?.name ?? undefined });
|
|
241
240
|
const who = result.email ? ` as ${result.email}` : "";
|
|
242
241
|
return {
|
|
243
242
|
exitCode: 0,
|
package/dist/auth/login-app.js
CHANGED
|
@@ -3,10 +3,10 @@ import { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { Box, Text, useApp, useInput } from "ink";
|
|
4
4
|
import { Spinner } from "../scan-ui/components/Spinner.js";
|
|
5
5
|
import { displayTier, writeAuthState } from "./store.js";
|
|
6
|
-
import { POLL_INTERVAL_MS, POLL_TIMEOUT_MS, createAuthSession,
|
|
6
|
+
import { POLL_INTERVAL_MS, POLL_TIMEOUT_MS, createAuthSession, fetchAccountStatus, openBrowser, pollAuthSession, resolveWebBase } from "./device-login.js";
|
|
7
7
|
const SUCCESS_MIN_MS = 600;
|
|
8
8
|
const SUCCESS_MAX_MS = 2500;
|
|
9
|
-
function useLogin(webBase, env) {
|
|
9
|
+
export function useLogin(webBase, env) {
|
|
10
10
|
const [state, setState] = useState({ phase: "creating", verifyUrl: "", email: "", plan: "", message: "" });
|
|
11
11
|
const sessionId = useRef("");
|
|
12
12
|
const started = useRef(false);
|
|
@@ -58,12 +58,12 @@ function useLogin(webBase, env) {
|
|
|
58
58
|
const email = result.email ?? "";
|
|
59
59
|
writeAuthState({ token: apiKey, email: result.email });
|
|
60
60
|
setState((prev) => ({ ...prev, phase: "success", email }));
|
|
61
|
-
void
|
|
62
|
-
.then((
|
|
63
|
-
if (tier) {
|
|
64
|
-
writeAuthState({ token: apiKey, email: result.email, tier });
|
|
61
|
+
void fetchAccountStatus(apiKey, env, fetch)
|
|
62
|
+
.then((account) => {
|
|
63
|
+
if (account?.tier) {
|
|
64
|
+
writeAuthState({ token: apiKey, email: result.email, tier: account.tier, name: account.name ?? undefined });
|
|
65
65
|
if (!cancelled.current) {
|
|
66
|
-
setState((prev) => ({ ...prev, plan: tier }));
|
|
66
|
+
setState((prev) => ({ ...prev, plan: account.tier ?? "" }));
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
})
|
package/dist/auth/store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { loadUserConfig, saveUserConfig, withUserConfigLock } from "../config/settings.js";
|
|
4
|
+
import { loadUserConfig, parseUrl, saveUserConfig, withUserConfigLock } from "../config/settings.js";
|
|
5
5
|
import { resolveDgPaths } from "../state/index.js";
|
|
6
6
|
import { envAuthToken } from "./env-token.js";
|
|
7
7
|
export class AuthError extends Error {
|
|
@@ -24,17 +24,26 @@ export function readAuthState(env = process.env) {
|
|
|
24
24
|
if (parsed.version !== 1 || !parsed.token || !parsed.apiBaseUrl || parsed.loggedInAt === undefined) {
|
|
25
25
|
throw new AuthError("unsupported auth state");
|
|
26
26
|
}
|
|
27
|
+
let apiBaseUrl;
|
|
28
|
+
try {
|
|
29
|
+
apiBaseUrl = parseUrl(parsed.apiBaseUrl);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new AuthError("auth state has an invalid api base URL; run 'dg login' again");
|
|
33
|
+
}
|
|
27
34
|
const email = typeof parsed.email === "string" && parsed.email.length > 0 ? parsed.email : undefined;
|
|
28
35
|
const tier = typeof parsed.tier === "string" && parsed.tier.length > 0 ? parsed.tier : undefined;
|
|
36
|
+
const name = typeof parsed.name === "string" && parsed.name.length > 0 ? parsed.name : undefined;
|
|
29
37
|
return {
|
|
30
38
|
version: 1,
|
|
31
39
|
token: parsed.token,
|
|
32
40
|
tokenPreview: parsed.tokenPreview ?? redactToken(parsed.token),
|
|
33
|
-
apiBaseUrl
|
|
41
|
+
apiBaseUrl,
|
|
34
42
|
orgId: parsed.orgId ?? "",
|
|
35
43
|
loggedInAt: parsed.loggedInAt,
|
|
36
44
|
...(email ? { email } : {}),
|
|
37
|
-
...(tier ? { tier } : {})
|
|
45
|
+
...(tier ? { tier } : {}),
|
|
46
|
+
...(name ? { name } : {})
|
|
38
47
|
};
|
|
39
48
|
}
|
|
40
49
|
catch (error) {
|
|
@@ -63,6 +72,7 @@ export function writeAuthState(options, env = process.env) {
|
|
|
63
72
|
}
|
|
64
73
|
const email = typeof options.email === "string" && options.email.length > 0 ? options.email : undefined;
|
|
65
74
|
const tier = typeof options.tier === "string" && options.tier.length > 0 ? options.tier : undefined;
|
|
75
|
+
const name = typeof options.name === "string" && options.name.length > 0 ? options.name : undefined;
|
|
66
76
|
return withUserConfigLock(env, () => {
|
|
67
77
|
const config = loadUserConfig(env);
|
|
68
78
|
const apiBaseUrl = options.apiBaseUrl ?? config.api.baseUrl;
|
|
@@ -75,7 +85,8 @@ export function writeAuthState(options, env = process.env) {
|
|
|
75
85
|
orgId,
|
|
76
86
|
loggedInAt: (options.now ?? new Date()).toISOString(),
|
|
77
87
|
...(email ? { email } : {}),
|
|
78
|
-
...(tier ? { tier } : {})
|
|
88
|
+
...(tier ? { tier } : {}),
|
|
89
|
+
...(name ? { name } : {})
|
|
79
90
|
};
|
|
80
91
|
const paths = resolveDgPaths(env);
|
|
81
92
|
writeJsonAtomic(authPath(paths), state);
|
|
@@ -120,7 +131,8 @@ export function authStatus(env = process.env) {
|
|
|
120
131
|
apiBaseUrl: state.apiBaseUrl,
|
|
121
132
|
orgId: state.orgId,
|
|
122
133
|
...(state.email ? { email: state.email } : {}),
|
|
123
|
-
...(state.tier ? { tier: state.tier } : {})
|
|
134
|
+
...(state.tier ? { tier: state.tier } : {}),
|
|
135
|
+
...(state.name ? { name: state.name } : {})
|
|
124
136
|
};
|
|
125
137
|
}
|
|
126
138
|
return {
|
package/dist/bin/dg.js
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { assertCurrentNode } from "../runtime/node-version.js";
|
|
3
|
-
assertCurrentNode();
|
|
4
|
-
const
|
|
5
|
-
function exitOnFatal(error) {
|
|
6
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
-
try {
|
|
8
|
-
process.stderr.write(`dg: unexpected error — ${message}\nRun 'dg doctor' to check your installation; remove ~/.dg/config.json if it is corrupted.\n`);
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
// stderr is gone; nothing left to report to.
|
|
12
|
-
}
|
|
13
|
-
process.exit(EXIT_TOOL_ERROR);
|
|
14
|
-
}
|
|
3
|
+
await assertCurrentNode();
|
|
4
|
+
const { exitOnFatal } = await import("../runtime/fatal.js");
|
|
15
5
|
process.on("uncaughtException", (error) => {
|
|
16
6
|
if (error.code === "EPIPE") {
|
|
17
7
|
process.exit(process.exitCode ?? 0);
|
|
@@ -30,52 +20,69 @@ const { runCli, writeCliResult } = await import("../runtime/cli.js");
|
|
|
30
20
|
const { maybeShowFirstRun } = await import("../runtime/first-run.js");
|
|
31
21
|
const { maybePreflightInstallPrompt } = await import("../launcher/preflight-prompt.js");
|
|
32
22
|
const args = process.argv.slice(2);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})
|
|
38
|
-
.catch(() => { });
|
|
39
|
-
const { maybeDeviceLogin } = await import("../auth/device-login.js");
|
|
40
|
-
const { maybeVerifyPackage } = await import("../verify/package-check.js");
|
|
41
|
-
const { maybeAudit } = await import("../commands/audit.js");
|
|
42
|
-
const notHandled = { handled: false };
|
|
43
|
-
const deviceLogin = await maybeDeviceLogin(args);
|
|
44
|
-
const verifyPackage = deviceLogin.handled ? notHandled : await maybeVerifyPackage(args);
|
|
45
|
-
const audit = deviceLogin.handled || verifyPackage.handled ? notHandled : await maybeAudit(args);
|
|
46
|
-
if (deviceLogin.handled) {
|
|
47
|
-
writeCliResult(deviceLogin.result);
|
|
48
|
-
}
|
|
49
|
-
else if (verifyPackage.handled) {
|
|
50
|
-
writeCliResult(verifyPackage.result);
|
|
51
|
-
}
|
|
52
|
-
else if (audit.handled) {
|
|
53
|
-
writeCliResult(audit.result);
|
|
23
|
+
const { maybeAgentHookExec } = await import("../launcher/agent-hook-exec.js");
|
|
24
|
+
const agentHookExec = await maybeAgentHookExec(args);
|
|
25
|
+
if (agentHookExec.handled) {
|
|
26
|
+
writeCliResult(agentHookExec.result);
|
|
54
27
|
}
|
|
55
28
|
else {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
29
|
+
const { maybeOfferSetupWizard } = await import("../setup-ui/offer.js");
|
|
30
|
+
const wizardOffer = await maybeOfferSetupWizard(args);
|
|
31
|
+
if (wizardOffer.handled) {
|
|
32
|
+
writeCliResult(wizardOffer.result);
|
|
59
33
|
}
|
|
60
34
|
else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
35
|
+
if (wizardOffer.result.stderr) {
|
|
36
|
+
process.stderr.write(wizardOffer.result.stderr);
|
|
37
|
+
}
|
|
38
|
+
maybeShowFirstRun(args);
|
|
39
|
+
import("../state/index.js")
|
|
40
|
+
.then(({ pruneDeadSessionsSync, resolveDgPaths }) => {
|
|
41
|
+
pruneDeadSessionsSync(resolveDgPaths());
|
|
42
|
+
})
|
|
43
|
+
.catch(() => { });
|
|
44
|
+
const { maybeDeviceLogin } = await import("../auth/device-login.js");
|
|
45
|
+
const { maybeVerifyPackage } = await import("../verify/package-check.js");
|
|
46
|
+
const { maybeAudit } = await import("../commands/audit.js");
|
|
47
|
+
const notHandled = { handled: false };
|
|
48
|
+
const deviceLogin = await maybeDeviceLogin(args);
|
|
49
|
+
const verifyPackage = deviceLogin.handled ? notHandled : await maybeVerifyPackage(args);
|
|
50
|
+
const audit = deviceLogin.handled || verifyPackage.handled ? notHandled : await maybeAudit(args);
|
|
51
|
+
if (deviceLogin.handled) {
|
|
52
|
+
writeCliResult(deviceLogin.result);
|
|
53
|
+
}
|
|
54
|
+
else if (verifyPackage.handled) {
|
|
55
|
+
writeCliResult(verifyPackage.result);
|
|
56
|
+
}
|
|
57
|
+
else if (audit.handled) {
|
|
58
|
+
writeCliResult(audit.result);
|
|
65
59
|
}
|
|
66
60
|
else {
|
|
67
|
-
|
|
61
|
+
const preflight = await maybePreflightInstallPrompt(args, { decisionsCwd: process.cwd() });
|
|
62
|
+
if (preflight.handled) {
|
|
63
|
+
writeCliResult(preflight.result);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const { maybeRunLiveInstall } = await import("../launcher/live-install.js");
|
|
67
|
+
const liveInstall = await maybeRunLiveInstall(args);
|
|
68
|
+
if (liveInstall.handled) {
|
|
69
|
+
writeCliResult(liveInstall.result);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
writeCliResult(await runCli(args));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// The auth flows (browser login, paid-verify gate, deep audit upload) already
|
|
77
|
+
// tell the user exactly what to do; the throttled nudges would just be noise.
|
|
78
|
+
if (!deviceLogin.handled && !verifyPackage.handled && !audit.handled) {
|
|
79
|
+
try {
|
|
80
|
+
const { maybeShowNudges } = await import("../runtime/nudges.js");
|
|
81
|
+
maybeShowNudges(args);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// dg deleted its own files mid-run (uninstall of itself); nudges are cosmetic.
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// The auth flows (browser login, paid-verify gate, deep audit upload) already
|
|
72
|
-
// tell the user exactly what to do; the throttled nudges would just be noise.
|
|
73
|
-
if (!deviceLogin.handled && !verifyPackage.handled && !audit.handled) {
|
|
74
|
-
try {
|
|
75
|
-
const { maybeShowNudges } = await import("../runtime/nudges.js");
|
|
76
|
-
maybeShowNudges(args);
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// dg deleted its own files mid-run (uninstall of itself); nudges are cosmetic.
|
|
80
87
|
}
|
|
81
88
|
}
|