@rainy-updates/cli 0.5.3 → 0.5.6
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/CHANGELOG.md +102 -0
- package/README.md +8 -0
- package/dist/bin/cli.js +38 -2
- package/dist/commands/audit/runner.js +43 -26
- package/dist/commands/dashboard/parser.d.ts +2 -0
- package/dist/commands/dashboard/parser.js +59 -0
- package/dist/commands/dashboard/runner.d.ts +2 -0
- package/dist/commands/dashboard/runner.js +47 -0
- package/dist/commands/doctor/parser.js +6 -0
- package/dist/commands/ga/parser.d.ts +2 -0
- package/dist/commands/ga/parser.js +50 -0
- package/dist/commands/ga/runner.d.ts +2 -0
- package/dist/commands/ga/runner.js +129 -0
- package/dist/commands/resolve/runner.js +7 -3
- package/dist/commands/review/parser.js +6 -0
- package/dist/commands/review/runner.js +4 -3
- package/dist/core/analysis-bundle.d.ts +4 -0
- package/dist/core/analysis-bundle.js +241 -0
- package/dist/core/artifacts.d.ts +3 -0
- package/dist/core/artifacts.js +48 -0
- package/dist/core/check.js +6 -1
- package/dist/core/options.d.ts +7 -1
- package/dist/core/options.js +14 -0
- package/dist/core/review-model.js +51 -177
- package/dist/core/summary.js +13 -0
- package/dist/output/format.js +15 -0
- package/dist/output/github.js +8 -0
- package/dist/output/sarif.js +12 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/ui/dashboard/DashboardTUI.d.ts +6 -0
- package/dist/ui/dashboard/DashboardTUI.js +34 -0
- package/dist/ui/dashboard/components/DetailPanel.d.ts +4 -0
- package/dist/ui/dashboard/components/DetailPanel.js +30 -0
- package/dist/ui/dashboard/components/Footer.d.ts +4 -0
- package/dist/ui/dashboard/components/Footer.js +9 -0
- package/dist/ui/dashboard/components/Header.d.ts +4 -0
- package/dist/ui/dashboard/components/Header.js +12 -0
- package/dist/ui/dashboard/components/Sidebar.d.ts +4 -0
- package/dist/ui/dashboard/components/Sidebar.js +23 -0
- package/dist/ui/dashboard/store.d.ts +34 -0
- package/dist/ui/dashboard/store.js +148 -0
- package/dist/ui/tui.d.ts +2 -2
- package/dist/ui/tui.js +310 -79
- package/package.json +1 -1
|
@@ -1,82 +1,51 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import process from "node:process";
|
|
3
|
-
import { check } from "./check.js";
|
|
4
2
|
import { createSummary, finalizeSummary } from "./summary.js";
|
|
5
|
-
import {
|
|
6
|
-
import { applyRiskAssessments } from "../risk/index.js";
|
|
3
|
+
import { buildAnalysisBundle } from "./analysis-bundle.js";
|
|
7
4
|
export async function buildReviewResult(options) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
interactive
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const checkResult = await check(baseCheckOptions);
|
|
15
|
-
const [auditResult, resolveResult, healthResult, licenseResult, unusedResult] = await Promise.all([
|
|
16
|
-
runSilenced(() => import("../commands/audit/runner.js").then((mod) => mod.runAudit(toAuditOptions(options)))),
|
|
17
|
-
runSilenced(() => import("../commands/resolve/runner.js").then((mod) => mod.runResolve(toResolveOptions(options)))),
|
|
18
|
-
runSilenced(() => import("../commands/health/runner.js").then((mod) => mod.runHealth(toHealthOptions(options)))),
|
|
19
|
-
runSilenced(() => import("../commands/licenses/runner.js").then((mod) => mod.runLicenses(toLicenseOptions(options)))),
|
|
20
|
-
runSilenced(() => import("../commands/unused/runner.js").then((mod) => mod.runUnused(toUnusedOptions(options)))),
|
|
21
|
-
]);
|
|
22
|
-
const advisoryPackages = new Set(auditResult.packages.map((pkg) => pkg.packageName));
|
|
23
|
-
const impactedUpdates = applyImpactScores(checkResult.updates, {
|
|
24
|
-
advisoryPackages,
|
|
25
|
-
workspaceDependentCount: (name) => checkResult.updates.filter((item) => item.name === name).length,
|
|
26
|
-
});
|
|
27
|
-
const healthByName = new Map(healthResult.metrics.map((metric) => [metric.name, metric]));
|
|
28
|
-
const advisoriesByName = new Map();
|
|
29
|
-
const conflictsByName = new Map();
|
|
30
|
-
const licenseByName = new Map(licenseResult.packages.map((pkg) => [pkg.name, pkg]));
|
|
31
|
-
const licenseViolationNames = new Set(licenseResult.violations.map((pkg) => pkg.name));
|
|
32
|
-
const unusedByName = new Map();
|
|
33
|
-
for (const advisory of auditResult.advisories) {
|
|
34
|
-
const list = advisoriesByName.get(advisory.packageName) ?? [];
|
|
35
|
-
list.push(advisory);
|
|
36
|
-
advisoriesByName.set(advisory.packageName, list);
|
|
37
|
-
}
|
|
38
|
-
for (const conflict of resolveResult.conflicts) {
|
|
39
|
-
const list = conflictsByName.get(conflict.requester) ?? [];
|
|
40
|
-
list.push(conflict);
|
|
41
|
-
conflictsByName.set(conflict.requester, list);
|
|
42
|
-
const peerList = conflictsByName.get(conflict.peer) ?? [];
|
|
43
|
-
peerList.push(conflict);
|
|
44
|
-
conflictsByName.set(conflict.peer, peerList);
|
|
45
|
-
}
|
|
46
|
-
for (const issue of [...unusedResult.unused, ...unusedResult.missing]) {
|
|
47
|
-
const list = unusedByName.get(issue.name) ?? [];
|
|
48
|
-
list.push(issue);
|
|
49
|
-
unusedByName.set(issue.name, list);
|
|
50
|
-
}
|
|
51
|
-
const baseItems = impactedUpdates
|
|
52
|
-
.map((update) => enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName))
|
|
53
|
-
.filter((item) => matchesReviewFilters(item, options));
|
|
54
|
-
const items = applyRiskAssessments(baseItems, {
|
|
55
|
-
knownPackageNames: new Set(checkResult.updates.map((item) => item.name)),
|
|
56
|
-
}).filter((item) => matchesReviewFilters(item, options));
|
|
5
|
+
const includeChangelog = ("showChangelog" in options && options.showChangelog === true) ||
|
|
6
|
+
("includeChangelog" in options && options.includeChangelog === true) ||
|
|
7
|
+
options.interactive === true;
|
|
8
|
+
const analysis = await buildAnalysisBundle(options, { includeChangelog });
|
|
9
|
+
const items = analysis.items.filter((item) => matchesReviewFilters(item, options));
|
|
10
|
+
const checkResult = analysis.check;
|
|
57
11
|
const summary = createReviewSummary(checkResult.summary, items, [
|
|
58
12
|
...checkResult.errors,
|
|
59
|
-
...
|
|
60
|
-
...
|
|
61
|
-
...
|
|
62
|
-
...
|
|
63
|
-
...
|
|
13
|
+
...analysis.audit.errors,
|
|
14
|
+
...analysis.resolve.errors,
|
|
15
|
+
...analysis.health.errors,
|
|
16
|
+
...analysis.licenses.errors,
|
|
17
|
+
...analysis.unused.errors,
|
|
64
18
|
], [
|
|
65
19
|
...checkResult.warnings,
|
|
66
|
-
...
|
|
67
|
-
...
|
|
68
|
-
...
|
|
69
|
-
...
|
|
70
|
-
...
|
|
71
|
-
], options.interactive === true);
|
|
20
|
+
...analysis.audit.warnings,
|
|
21
|
+
...analysis.resolve.warnings,
|
|
22
|
+
...analysis.health.warnings,
|
|
23
|
+
...analysis.licenses.warnings,
|
|
24
|
+
...analysis.unused.warnings,
|
|
25
|
+
], options.interactive === true, analysis.degradedSources);
|
|
72
26
|
return {
|
|
73
27
|
projectPath: checkResult.projectPath,
|
|
74
28
|
target: checkResult.target,
|
|
75
29
|
summary,
|
|
30
|
+
analysis,
|
|
76
31
|
items,
|
|
77
32
|
updates: items.map((item) => item.update),
|
|
78
|
-
errors: [
|
|
79
|
-
|
|
33
|
+
errors: [
|
|
34
|
+
...checkResult.errors,
|
|
35
|
+
...analysis.audit.errors,
|
|
36
|
+
...analysis.resolve.errors,
|
|
37
|
+
...analysis.health.errors,
|
|
38
|
+
...analysis.licenses.errors,
|
|
39
|
+
...analysis.unused.errors,
|
|
40
|
+
],
|
|
41
|
+
warnings: [
|
|
42
|
+
...checkResult.warnings,
|
|
43
|
+
...analysis.audit.warnings,
|
|
44
|
+
...analysis.resolve.warnings,
|
|
45
|
+
...analysis.health.warnings,
|
|
46
|
+
...analysis.licenses.warnings,
|
|
47
|
+
...analysis.unused.warnings,
|
|
48
|
+
],
|
|
80
49
|
};
|
|
81
50
|
}
|
|
82
51
|
export function createDoctorResult(review) {
|
|
@@ -115,6 +84,7 @@ export function renderReviewResult(review) {
|
|
|
115
84
|
item.update.licenseStatus && item.update.licenseStatus !== "allowed"
|
|
116
85
|
? `license=${item.update.licenseStatus}`
|
|
117
86
|
: undefined,
|
|
87
|
+
item.update.policyAction ? `policy=${item.update.policyAction}` : undefined,
|
|
118
88
|
].filter(Boolean);
|
|
119
89
|
lines.push(`- ${path.basename(item.update.packagePath)} :: ${item.update.name} ${item.update.fromRange} -> ${item.update.toRange} (${notes.join(", ")})`);
|
|
120
90
|
if (item.update.riskReasons && item.update.riskReasons.length > 0) {
|
|
@@ -123,6 +93,9 @@ export function renderReviewResult(review) {
|
|
|
123
93
|
if (item.update.recommendedAction) {
|
|
124
94
|
lines.push(` action: ${item.update.recommendedAction}`);
|
|
125
95
|
}
|
|
96
|
+
if (item.update.releaseNotesSummary) {
|
|
97
|
+
lines.push(` notes: ${item.update.releaseNotesSummary.title} - ${item.update.releaseNotesSummary.excerpt}`);
|
|
98
|
+
}
|
|
126
99
|
if (item.update.homepage) {
|
|
127
100
|
lines.push(` homepage: ${item.update.homepage}`);
|
|
128
101
|
}
|
|
@@ -157,36 +130,6 @@ export function renderDoctorResult(result, verdictOnly = false) {
|
|
|
157
130
|
}
|
|
158
131
|
return lines.join("\n");
|
|
159
132
|
}
|
|
160
|
-
function enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName) {
|
|
161
|
-
const advisories = advisoriesByName.get(update.name) ?? [];
|
|
162
|
-
const peerConflicts = conflictsByName.get(update.name) ?? [];
|
|
163
|
-
const health = healthByName.get(update.name);
|
|
164
|
-
const license = licenseByName.get(update.name);
|
|
165
|
-
const unusedIssues = unusedByName.get(update.name) ?? [];
|
|
166
|
-
return {
|
|
167
|
-
update: {
|
|
168
|
-
...update,
|
|
169
|
-
advisoryCount: advisories.length,
|
|
170
|
-
peerConflictSeverity: peerConflicts.some((item) => item.severity === "error")
|
|
171
|
-
? "error"
|
|
172
|
-
: peerConflicts.length > 0
|
|
173
|
-
? "warning"
|
|
174
|
-
: "none",
|
|
175
|
-
licenseStatus: licenseViolationNames.has(update.name)
|
|
176
|
-
? "denied"
|
|
177
|
-
: license
|
|
178
|
-
? "allowed"
|
|
179
|
-
: "review",
|
|
180
|
-
healthStatus: health?.flags[0] ?? "healthy",
|
|
181
|
-
},
|
|
182
|
-
advisories,
|
|
183
|
-
health,
|
|
184
|
-
peerConflicts,
|
|
185
|
-
license,
|
|
186
|
-
unusedIssues,
|
|
187
|
-
selected: true,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
133
|
function matchesReviewFilters(item, options) {
|
|
191
134
|
if ("securityOnly" in options && options.securityOnly && item.advisories.length === 0) {
|
|
192
135
|
return false;
|
|
@@ -208,7 +151,7 @@ function riskMatches(current, threshold) {
|
|
|
208
151
|
};
|
|
209
152
|
return order[current ?? "low"] >= order[threshold];
|
|
210
153
|
}
|
|
211
|
-
function createReviewSummary(base, items, errors, warnings, interactiveSession) {
|
|
154
|
+
function createReviewSummary(base, items, errors, warnings, interactiveSession, degradedSources) {
|
|
212
155
|
const summary = finalizeSummary(createSummary({
|
|
213
156
|
scannedPackages: base.scannedPackages,
|
|
214
157
|
totalDependencies: base.totalDependencies,
|
|
@@ -242,6 +185,17 @@ function createReviewSummary(base, items, errors, warnings, interactiveSession)
|
|
|
242
185
|
summary.riskPackages = items.filter((item) => item.update.riskLevel === "critical" || item.update.riskLevel === "high").length;
|
|
243
186
|
summary.peerConflictPackages = items.filter((item) => item.update.peerConflictSeverity && item.update.peerConflictSeverity !== "none").length;
|
|
244
187
|
summary.licenseViolationPackages = items.filter((item) => item.update.licenseStatus === "denied").length;
|
|
188
|
+
summary.policyActionCounts = {
|
|
189
|
+
allow: items.filter((item) => item.update.policyAction === "allow").length,
|
|
190
|
+
review: items.filter((item) => item.update.policyAction === "review").length,
|
|
191
|
+
block: items.filter((item) => item.update.policyAction === "block").length,
|
|
192
|
+
monitor: items.filter((item) => item.update.policyAction === "monitor").length,
|
|
193
|
+
};
|
|
194
|
+
summary.blockedPackages = items.filter((item) => item.update.decisionState === "blocked").length;
|
|
195
|
+
summary.reviewPackages = items.filter((item) => item.update.decisionState === "review").length;
|
|
196
|
+
summary.monitorPackages = items.filter((item) => item.update.policyAction === "monitor").length;
|
|
197
|
+
summary.decisionPackages = items.length;
|
|
198
|
+
summary.degradedSources = degradedSources;
|
|
245
199
|
summary.verdict = deriveVerdict(items, errors);
|
|
246
200
|
return summary;
|
|
247
201
|
}
|
|
@@ -290,83 +244,3 @@ function recommendCommand(review, verdict) {
|
|
|
290
244
|
return "rup review --interactive";
|
|
291
245
|
return "rup check";
|
|
292
246
|
}
|
|
293
|
-
async function runSilenced(fn) {
|
|
294
|
-
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
295
|
-
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
296
|
-
process.stdout.write = (() => true);
|
|
297
|
-
process.stderr.write = (() => true);
|
|
298
|
-
try {
|
|
299
|
-
return await fn();
|
|
300
|
-
}
|
|
301
|
-
finally {
|
|
302
|
-
process.stdout.write = stdoutWrite;
|
|
303
|
-
process.stderr.write = stderrWrite;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
function toAuditOptions(options) {
|
|
307
|
-
return {
|
|
308
|
-
cwd: options.cwd,
|
|
309
|
-
workspace: options.workspace,
|
|
310
|
-
severity: undefined,
|
|
311
|
-
fix: false,
|
|
312
|
-
dryRun: true,
|
|
313
|
-
commit: false,
|
|
314
|
-
packageManager: "auto",
|
|
315
|
-
reportFormat: "json",
|
|
316
|
-
sourceMode: "auto",
|
|
317
|
-
jsonFile: undefined,
|
|
318
|
-
concurrency: options.concurrency,
|
|
319
|
-
registryTimeoutMs: options.registryTimeoutMs,
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
function toResolveOptions(options) {
|
|
323
|
-
return {
|
|
324
|
-
cwd: options.cwd,
|
|
325
|
-
workspace: options.workspace,
|
|
326
|
-
afterUpdate: true,
|
|
327
|
-
safe: false,
|
|
328
|
-
jsonFile: undefined,
|
|
329
|
-
concurrency: options.concurrency,
|
|
330
|
-
registryTimeoutMs: options.registryTimeoutMs,
|
|
331
|
-
cacheTtlSeconds: options.cacheTtlSeconds,
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
function toHealthOptions(options) {
|
|
335
|
-
return {
|
|
336
|
-
cwd: options.cwd,
|
|
337
|
-
workspace: options.workspace,
|
|
338
|
-
staleDays: 365,
|
|
339
|
-
includeDeprecated: true,
|
|
340
|
-
includeAlternatives: false,
|
|
341
|
-
reportFormat: "json",
|
|
342
|
-
jsonFile: undefined,
|
|
343
|
-
concurrency: options.concurrency,
|
|
344
|
-
registryTimeoutMs: options.registryTimeoutMs,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
function toLicenseOptions(options) {
|
|
348
|
-
return {
|
|
349
|
-
cwd: options.cwd,
|
|
350
|
-
workspace: options.workspace,
|
|
351
|
-
allow: undefined,
|
|
352
|
-
deny: undefined,
|
|
353
|
-
sbomFile: undefined,
|
|
354
|
-
jsonFile: undefined,
|
|
355
|
-
diffMode: false,
|
|
356
|
-
concurrency: options.concurrency,
|
|
357
|
-
registryTimeoutMs: options.registryTimeoutMs,
|
|
358
|
-
cacheTtlSeconds: options.cacheTtlSeconds,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
function toUnusedOptions(options) {
|
|
362
|
-
return {
|
|
363
|
-
cwd: options.cwd,
|
|
364
|
-
workspace: options.workspace,
|
|
365
|
-
srcDirs: ["src", "."],
|
|
366
|
-
includeDevDependencies: true,
|
|
367
|
-
fix: false,
|
|
368
|
-
dryRun: true,
|
|
369
|
-
jsonFile: undefined,
|
|
370
|
-
concurrency: options.concurrency,
|
|
371
|
-
};
|
|
372
|
-
}
|
package/dist/core/summary.js
CHANGED
|
@@ -50,6 +50,19 @@ export function createSummary(input) {
|
|
|
50
50
|
peerConflictPackages: 0,
|
|
51
51
|
licenseViolationPackages: 0,
|
|
52
52
|
privateRegistryPackages: 0,
|
|
53
|
+
runId: undefined,
|
|
54
|
+
artifactManifest: undefined,
|
|
55
|
+
policyActionCounts: undefined,
|
|
56
|
+
blockedPackages: 0,
|
|
57
|
+
reviewPackages: 0,
|
|
58
|
+
monitorPackages: 0,
|
|
59
|
+
decisionPackages: 0,
|
|
60
|
+
releaseVolatilityPackages: 0,
|
|
61
|
+
engineConflictPackages: 0,
|
|
62
|
+
degradedSources: [],
|
|
63
|
+
cacheBackend: undefined,
|
|
64
|
+
binaryRecommended: false,
|
|
65
|
+
gaReady: undefined,
|
|
53
66
|
};
|
|
54
67
|
}
|
|
55
68
|
export function finalizeSummary(summary) {
|
package/dist/output/format.js
CHANGED
|
@@ -62,6 +62,14 @@ export function renderResult(result, format, display = {}) {
|
|
|
62
62
|
`security_packages=${result.summary.securityPackages ?? 0}`,
|
|
63
63
|
`peer_conflict_packages=${result.summary.peerConflictPackages ?? 0}`,
|
|
64
64
|
`license_violation_packages=${result.summary.licenseViolationPackages ?? 0}`,
|
|
65
|
+
`run_id=${result.summary.runId ?? ""}`,
|
|
66
|
+
`artifact_manifest=${result.summary.artifactManifest ?? ""}`,
|
|
67
|
+
`blocked_packages=${result.summary.blockedPackages ?? 0}`,
|
|
68
|
+
`review_packages=${result.summary.reviewPackages ?? 0}`,
|
|
69
|
+
`monitor_packages=${result.summary.monitorPackages ?? 0}`,
|
|
70
|
+
`cache_backend=${result.summary.cacheBackend ?? ""}`,
|
|
71
|
+
`degraded_sources=${(result.summary.degradedSources ?? []).join(",")}`,
|
|
72
|
+
`ga_ready=${result.summary.gaReady === true ? "1" : "0"}`,
|
|
65
73
|
].join("\n");
|
|
66
74
|
}
|
|
67
75
|
const lines = [];
|
|
@@ -89,12 +97,16 @@ export function renderResult(result, format, display = {}) {
|
|
|
89
97
|
display.showHomepage && update.homepage ? update.homepage : undefined,
|
|
90
98
|
update.riskLevel ? `risk=${update.riskLevel}` : undefined,
|
|
91
99
|
typeof update.riskScore === "number" ? `score=${update.riskScore}` : undefined,
|
|
100
|
+
update.policyAction ? `policy=${update.policyAction}` : undefined,
|
|
92
101
|
]
|
|
93
102
|
.filter(Boolean)
|
|
94
103
|
.join(", ")})`);
|
|
95
104
|
if (update.recommendedAction) {
|
|
96
105
|
lines.push(` action: ${update.recommendedAction}`);
|
|
97
106
|
}
|
|
107
|
+
if (update.releaseNotesSummary) {
|
|
108
|
+
lines.push(` notes: ${update.releaseNotesSummary.title} — ${update.releaseNotesSummary.excerpt}`);
|
|
109
|
+
}
|
|
98
110
|
}
|
|
99
111
|
}
|
|
100
112
|
if (result.errors.length > 0) {
|
|
@@ -118,6 +130,9 @@ export function renderResult(result, format, display = {}) {
|
|
|
118
130
|
if (result.summary.verdict) {
|
|
119
131
|
lines.push(`Verdict=${result.summary.verdict}, riskPackages=${result.summary.riskPackages ?? 0}, securityPackages=${result.summary.securityPackages ?? 0}, peerConflictPackages=${result.summary.peerConflictPackages ?? 0}, licenseViolationPackages=${result.summary.licenseViolationPackages ?? 0}`);
|
|
120
132
|
}
|
|
133
|
+
if (result.summary.runId) {
|
|
134
|
+
lines.push(`RunId=${result.summary.runId}, artifactManifest=${result.summary.artifactManifest ?? "none"}, blockedPackages=${result.summary.blockedPackages ?? 0}, reviewPackages=${result.summary.reviewPackages ?? 0}, monitorPackages=${result.summary.monitorPackages ?? 0}`);
|
|
135
|
+
}
|
|
121
136
|
lines.push(`Contract v${result.summary.contractVersion}, failReason=${result.summary.failReason}, duration=${result.summary.durationMs.total}ms`);
|
|
122
137
|
if (result.summary.fixPrApplied) {
|
|
123
138
|
lines.push(`Fix PR: applied on branch ${result.summary.fixBranchName ?? "unknown"} (${result.summary.fixCommitSha ?? "no-commit"})`);
|
package/dist/output/github.js
CHANGED
|
@@ -26,6 +26,14 @@ export async function writeGitHubOutput(filePath, result) {
|
|
|
26
26
|
`security_packages=${result.summary.securityPackages ?? 0}`,
|
|
27
27
|
`peer_conflict_packages=${result.summary.peerConflictPackages ?? 0}`,
|
|
28
28
|
`license_violation_packages=${result.summary.licenseViolationPackages ?? 0}`,
|
|
29
|
+
`run_id=${result.summary.runId ?? ""}`,
|
|
30
|
+
`artifact_manifest=${result.summary.artifactManifest ?? ""}`,
|
|
31
|
+
`blocked_packages=${result.summary.blockedPackages ?? 0}`,
|
|
32
|
+
`review_packages=${result.summary.reviewPackages ?? 0}`,
|
|
33
|
+
`monitor_packages=${result.summary.monitorPackages ?? 0}`,
|
|
34
|
+
`cache_backend=${result.summary.cacheBackend ?? ""}`,
|
|
35
|
+
`degraded_sources=${(result.summary.degradedSources ?? []).join(",")}`,
|
|
36
|
+
`ga_ready=${result.summary.gaReady === true ? "1" : "0"}`,
|
|
29
37
|
`fix_pr_applied=${result.summary.fixPrApplied === true ? "1" : "0"}`,
|
|
30
38
|
`fix_pr_branches_created=${result.summary.fixPrBranchesCreated}`,
|
|
31
39
|
`fix_pr_branch=${result.summary.fixBranchName ?? ""}`,
|
package/dist/output/sarif.js
CHANGED
|
@@ -39,6 +39,11 @@ export function createSarifReport(result) {
|
|
|
39
39
|
advisoryCount: update.advisoryCount ?? 0,
|
|
40
40
|
peerConflictSeverity: update.peerConflictSeverity ?? "none",
|
|
41
41
|
licenseStatus: update.licenseStatus ?? "allowed",
|
|
42
|
+
policyAction: update.policyAction,
|
|
43
|
+
decisionState: update.decisionState,
|
|
44
|
+
releaseNotesSummary: update.releaseNotesSummary?.excerpt,
|
|
45
|
+
engineStatus: update.engineStatus?.state,
|
|
46
|
+
runId: result.summary.runId,
|
|
42
47
|
},
|
|
43
48
|
}));
|
|
44
49
|
const errorResults = [...result.errors].sort((a, b) => a.localeCompare(b)).map((error) => ({
|
|
@@ -89,6 +94,13 @@ export function createSarifReport(result) {
|
|
|
89
94
|
securityPackages: result.summary.securityPackages ?? 0,
|
|
90
95
|
peerConflictPackages: result.summary.peerConflictPackages ?? 0,
|
|
91
96
|
licenseViolationPackages: result.summary.licenseViolationPackages ?? 0,
|
|
97
|
+
runId: result.summary.runId,
|
|
98
|
+
artifactManifest: result.summary.artifactManifest,
|
|
99
|
+
blockedPackages: result.summary.blockedPackages ?? 0,
|
|
100
|
+
reviewPackages: result.summary.reviewPackages ?? 0,
|
|
101
|
+
monitorPackages: result.summary.monitorPackages ?? 0,
|
|
102
|
+
degradedSources: result.summary.degradedSources ?? [],
|
|
103
|
+
cacheBackend: result.summary.cacheBackend,
|
|
92
104
|
},
|
|
93
105
|
},
|
|
94
106
|
],
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type Verdict = "safe" | "review" | "blocked" | "actionable";
|
|
|
7
7
|
export type RiskLevel = "critical" | "high" | "medium" | "low";
|
|
8
8
|
export type RiskCategory = "known-vulnerability" | "behavioral-risk" | "operational-health";
|
|
9
9
|
export type MaintainerChurnStatus = "unknown" | "stable" | "elevated-change";
|
|
10
|
+
export type PolicyAction = "allow" | "review" | "block" | "monitor";
|
|
11
|
+
export type DecisionState = "safe" | "review" | "blocked" | "actionable";
|
|
10
12
|
export type OutputFormat = "table" | "json" | "minimal" | "github" | "metrics";
|
|
11
13
|
export type FailOnLevel = "none" | "patch" | "minor" | "major" | "any";
|
|
12
14
|
export type LogLevel = "error" | "warn" | "info" | "debug";
|
|
@@ -108,6 +110,40 @@ export interface PackageUpdate {
|
|
|
108
110
|
peerConflictSeverity?: "none" | PeerConflictSeverity;
|
|
109
111
|
licenseStatus?: "allowed" | "review" | "denied";
|
|
110
112
|
healthStatus?: "healthy" | HealthFlag;
|
|
113
|
+
policyAction?: PolicyAction;
|
|
114
|
+
decisionState?: DecisionState;
|
|
115
|
+
releaseNotesSummary?: ReleaseNotesSummary;
|
|
116
|
+
engineStatus?: EngineStatus;
|
|
117
|
+
workspaceGroup?: string;
|
|
118
|
+
groupKey?: string;
|
|
119
|
+
selectedByDefault?: boolean;
|
|
120
|
+
blockedReason?: string;
|
|
121
|
+
monitorReason?: string;
|
|
122
|
+
}
|
|
123
|
+
export interface ReleaseNotesSummary {
|
|
124
|
+
source: "github-release" | "changelog-file" | "none";
|
|
125
|
+
title: string;
|
|
126
|
+
excerpt: string;
|
|
127
|
+
}
|
|
128
|
+
export interface EngineStatus {
|
|
129
|
+
state: "compatible" | "review" | "blocked" | "unknown";
|
|
130
|
+
required?: string;
|
|
131
|
+
current?: string;
|
|
132
|
+
reason?: string;
|
|
133
|
+
}
|
|
134
|
+
export interface ArtifactManifest {
|
|
135
|
+
runId: string;
|
|
136
|
+
createdAt: string;
|
|
137
|
+
command: string;
|
|
138
|
+
projectPath: string;
|
|
139
|
+
ciProfile: CiProfile;
|
|
140
|
+
artifactManifestPath: string;
|
|
141
|
+
outputs: {
|
|
142
|
+
jsonFile?: string;
|
|
143
|
+
githubOutputFile?: string;
|
|
144
|
+
sarifFile?: string;
|
|
145
|
+
prReportFile?: string;
|
|
146
|
+
};
|
|
111
147
|
}
|
|
112
148
|
export interface Summary {
|
|
113
149
|
contractVersion: "2";
|
|
@@ -155,6 +191,19 @@ export interface Summary {
|
|
|
155
191
|
peerConflictPackages?: number;
|
|
156
192
|
licenseViolationPackages?: number;
|
|
157
193
|
privateRegistryPackages?: number;
|
|
194
|
+
runId?: string;
|
|
195
|
+
artifactManifest?: string;
|
|
196
|
+
policyActionCounts?: Record<PolicyAction, number>;
|
|
197
|
+
blockedPackages?: number;
|
|
198
|
+
reviewPackages?: number;
|
|
199
|
+
monitorPackages?: number;
|
|
200
|
+
decisionPackages?: number;
|
|
201
|
+
releaseVolatilityPackages?: number;
|
|
202
|
+
engineConflictPackages?: number;
|
|
203
|
+
degradedSources?: string[];
|
|
204
|
+
cacheBackend?: "sqlite" | "file";
|
|
205
|
+
binaryRecommended?: boolean;
|
|
206
|
+
gaReady?: boolean;
|
|
158
207
|
}
|
|
159
208
|
export interface CheckResult {
|
|
160
209
|
projectPath: string;
|
|
@@ -208,6 +257,7 @@ export interface AuditOptions {
|
|
|
208
257
|
jsonFile?: string;
|
|
209
258
|
concurrency: number;
|
|
210
259
|
registryTimeoutMs: number;
|
|
260
|
+
silent?: boolean;
|
|
211
261
|
}
|
|
212
262
|
export interface CveAdvisory {
|
|
213
263
|
cveId: string;
|
|
@@ -324,6 +374,7 @@ export interface ResolveOptions {
|
|
|
324
374
|
concurrency: number;
|
|
325
375
|
registryTimeoutMs: number;
|
|
326
376
|
cacheTtlSeconds: number;
|
|
377
|
+
silent?: boolean;
|
|
327
378
|
}
|
|
328
379
|
export interface ResolveResult {
|
|
329
380
|
conflicts: PeerConflict[];
|
|
@@ -367,6 +418,7 @@ export interface ReviewResult {
|
|
|
367
418
|
projectPath: string;
|
|
368
419
|
target: TargetLevel;
|
|
369
420
|
summary: Summary;
|
|
421
|
+
analysis: AnalysisBundle;
|
|
370
422
|
items: ReviewItem[];
|
|
371
423
|
updates: PackageUpdate[];
|
|
372
424
|
errors: string[];
|
|
@@ -377,9 +429,11 @@ export interface ReviewOptions extends CheckOptions {
|
|
|
377
429
|
risk?: RiskLevel;
|
|
378
430
|
diff?: TargetLevel;
|
|
379
431
|
applySelected: boolean;
|
|
432
|
+
showChangelog?: boolean;
|
|
380
433
|
}
|
|
381
434
|
export interface DoctorOptions extends CheckOptions {
|
|
382
435
|
verdictOnly: boolean;
|
|
436
|
+
includeChangelog?: boolean;
|
|
383
437
|
}
|
|
384
438
|
export interface DoctorResult {
|
|
385
439
|
verdict: Verdict;
|
|
@@ -388,6 +442,24 @@ export interface DoctorResult {
|
|
|
388
442
|
primaryFindings: string[];
|
|
389
443
|
recommendedCommand: string;
|
|
390
444
|
}
|
|
445
|
+
export interface AnalysisBundle {
|
|
446
|
+
check: CheckResult;
|
|
447
|
+
audit: AuditResult;
|
|
448
|
+
resolve: ResolveResult;
|
|
449
|
+
health: HealthResult;
|
|
450
|
+
licenses: LicenseResult;
|
|
451
|
+
unused: UnusedResult;
|
|
452
|
+
items: ReviewItem[];
|
|
453
|
+
degradedSources: string[];
|
|
454
|
+
}
|
|
455
|
+
export interface DashboardOptions extends CheckOptions {
|
|
456
|
+
view?: "dependencies" | "security" | "health";
|
|
457
|
+
}
|
|
458
|
+
export interface DashboardResult {
|
|
459
|
+
completed: boolean;
|
|
460
|
+
errors: string[];
|
|
461
|
+
warnings: string[];
|
|
462
|
+
}
|
|
391
463
|
export type UnusedKind = "declared-not-imported" | "imported-not-declared";
|
|
392
464
|
export interface UnusedDependency {
|
|
393
465
|
name: string;
|
|
@@ -495,3 +567,23 @@ export interface SnapshotResult {
|
|
|
495
567
|
errors: string[];
|
|
496
568
|
warnings: string[];
|
|
497
569
|
}
|
|
570
|
+
export interface GaOptions {
|
|
571
|
+
cwd: string;
|
|
572
|
+
workspace: boolean;
|
|
573
|
+
jsonFile?: string;
|
|
574
|
+
}
|
|
575
|
+
export interface GaCheck {
|
|
576
|
+
name: "package-manager" | "workspace-discovery" | "lockfile" | "cache-backend" | "dist-build" | "benchmark-gates" | "docs-contract";
|
|
577
|
+
status: "pass" | "warn" | "fail";
|
|
578
|
+
detail: string;
|
|
579
|
+
}
|
|
580
|
+
export interface GaResult {
|
|
581
|
+
ready: boolean;
|
|
582
|
+
projectPath: string;
|
|
583
|
+
packageManager: "npm" | "pnpm" | "unknown";
|
|
584
|
+
workspacePackages: number;
|
|
585
|
+
cacheBackend: "sqlite" | "file";
|
|
586
|
+
checks: GaCheck[];
|
|
587
|
+
warnings: string[];
|
|
588
|
+
errors: string[];
|
|
589
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DashboardOptions, CheckResult } from "../../types/index.js";
|
|
2
|
+
export interface DashboardTUIProps {
|
|
3
|
+
options: DashboardOptions;
|
|
4
|
+
initialResult: CheckResult;
|
|
5
|
+
}
|
|
6
|
+
export declare function DashboardTUI({ options, initialResult }: DashboardTUIProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, useInput, useApp } from "ink";
|
|
3
|
+
import { initStore, dashboardActions } from "./store.js";
|
|
4
|
+
import { Header } from "./components/Header.js";
|
|
5
|
+
import { Sidebar } from "./components/Sidebar.js";
|
|
6
|
+
import { DetailPanel } from "./components/DetailPanel.js";
|
|
7
|
+
import { Footer } from "./components/Footer.js";
|
|
8
|
+
export function DashboardTUI({ options, initialResult }) {
|
|
9
|
+
const { exit } = useApp();
|
|
10
|
+
// Initialize the singleton store synchronously before rendering children
|
|
11
|
+
// so that components can access it on the first render pass natively.
|
|
12
|
+
initStore(options, initialResult);
|
|
13
|
+
// Handle global keyboard input (doesn't trigger React re-renders unless store state affects this component)
|
|
14
|
+
// Our static layout theoretically will not re-render off this hook alone.
|
|
15
|
+
useInput((input, key) => {
|
|
16
|
+
if (key.upArrow) {
|
|
17
|
+
dashboardActions.moveCursorUp();
|
|
18
|
+
}
|
|
19
|
+
if (key.downArrow) {
|
|
20
|
+
dashboardActions.moveCursorDown();
|
|
21
|
+
}
|
|
22
|
+
if (key.return) {
|
|
23
|
+
dashboardActions.setShouldApply(true);
|
|
24
|
+
exit();
|
|
25
|
+
}
|
|
26
|
+
if (input === "r") {
|
|
27
|
+
void dashboardActions.runResolveAction();
|
|
28
|
+
}
|
|
29
|
+
if (input === "a") {
|
|
30
|
+
void dashboardActions.runAuditAction();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", minHeight: 25, children: [_jsx(Header, {}), _jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Sidebar, {}), _jsx(DetailPanel, {})] }), _jsx(Footer, {})] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { useDashboardStore } from "../store.js";
|
|
5
|
+
function getRiskColor(risk) {
|
|
6
|
+
switch (risk) {
|
|
7
|
+
case "critical":
|
|
8
|
+
return "red";
|
|
9
|
+
case "high":
|
|
10
|
+
return "red";
|
|
11
|
+
case "medium":
|
|
12
|
+
return "yellow";
|
|
13
|
+
case "low":
|
|
14
|
+
return "blue";
|
|
15
|
+
default:
|
|
16
|
+
return "gray";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function DetailPanelComponent() {
|
|
20
|
+
const selectedIndex = useDashboardStore((s) => s.selectedIndex);
|
|
21
|
+
const updates = useDashboardStore((s) => s.updates);
|
|
22
|
+
// Fallback if list is empty
|
|
23
|
+
const update = updates[selectedIndex];
|
|
24
|
+
if (!update) {
|
|
25
|
+
return (_jsx(Box, { width: "50%", height: 22, padding: 1, borderStyle: "single", borderColor: "gray", children: _jsx(Text, { dimColor: true, children: "No package selected." }) }));
|
|
26
|
+
}
|
|
27
|
+
const { name, publishedAt, publishAgeDays, riskLevel, homepage, advisoryCount, peerConflictSeverity, } = update;
|
|
28
|
+
return (_jsxs(Box, { width: "50%", height: 22, flexDirection: "column", paddingX: 2, borderStyle: "single", borderColor: "gray", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: name }) }), _jsxs(Box, { marginBottom: 2, flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Risk: " }), _jsx(Text, { color: getRiskColor(riskLevel), children: riskLevel || "unknown" })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Advisories: " }), _jsx(Text, { color: advisoryCount ? "red" : "green", children: advisoryCount || 0 })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Peer Conflicts: " }), _jsx(Text, { color: peerConflictSeverity ? "red" : "green", children: peerConflictSeverity || "none" })] })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "gray", children: homepage || "No homepage provided" }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Published ", publishAgeDays, " days ago", " ", publishedAt ? `(${publishedAt})` : ""] }) })] }));
|
|
29
|
+
}
|
|
30
|
+
export const DetailPanel = React.memo(DetailPanelComponent);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { useDashboardStore } from "../store.js";
|
|
5
|
+
function FooterComponent() {
|
|
6
|
+
const modal = useDashboardStore((s) => s.modal);
|
|
7
|
+
return (_jsxs(Box, { width: "100%", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [_jsx(Text, { bold: true, color: "white", children: "\u2191\u2193" }), " ", "Navigate |", _jsxs(Text, { bold: true, color: "white", children: [" ", "Enter"] }), " ", "Apply |", _jsxs(Text, { bold: true, color: "white", children: [" ", "r"] }), "esolve |", _jsxs(Text, { bold: true, color: "white", children: [" ", "a"] }), "udit |", _jsxs(Text, { bold: true, color: "white", children: [" ", "i"] }), "gnore"] }) }), _jsx(Box, { children: _jsx(Text, { color: "yellow", children: modal !== "none" ? `Status: ${modal}...` : "Status: Idle" }) })] }));
|
|
8
|
+
}
|
|
9
|
+
export const Footer = React.memo(FooterComponent);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { useDashboardStore } from "../store.js";
|
|
5
|
+
function HeaderComponent() {
|
|
6
|
+
const scanned = useDashboardStore((s) => s.summary.scannedPackages);
|
|
7
|
+
const totalUpdates = useDashboardStore((s) => s.updates.length);
|
|
8
|
+
const view = useDashboardStore((s) => s.view);
|
|
9
|
+
return (_jsxs(Box, { width: "100%", paddingX: 1, borderStyle: "single", borderColor: "blue", flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "\uD83C\uDF27\uFE0F Rainy Updates Dashboard" }) }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: view === "dependencies" ? "green" : "gray", children: "[Dependencies]" }), " ", _jsx(Text, { color: view === "security" ? "green" : "gray", children: "[Security]" }), " ", _jsx(Text, { color: view === "health" ? "green" : "gray", children: "[Health]" })] }) }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["Packages: ", scanned, " | Found: ", totalUpdates] }) })] }));
|
|
10
|
+
}
|
|
11
|
+
// Memoize to ensure Header only renders when deeply affected (which is rare)
|
|
12
|
+
export const Header = React.memo(HeaderComponent);
|