@rainy-updates/cli 0.6.0 → 0.6.1
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 +53 -0
- package/dist/bin/cli.js +12 -127
- package/dist/bin/dispatch.js +6 -0
- package/dist/bin/help.js +47 -0
- package/dist/bin/main.d.ts +1 -0
- package/dist/bin/main.js +126 -0
- package/dist/commands/audit/parser.js +36 -0
- package/dist/commands/audit/runner.js +17 -18
- package/dist/commands/bisect/oracle.js +18 -15
- package/dist/commands/bisect/runner.js +4 -3
- package/dist/commands/dashboard/parser.js +41 -0
- package/dist/commands/doctor/parser.js +44 -0
- package/dist/commands/ga/parser.js +39 -0
- package/dist/commands/ga/runner.js +10 -7
- package/dist/commands/health/parser.js +36 -0
- package/dist/commands/health/runner.js +5 -1
- package/dist/commands/hook/parser.d.ts +2 -0
- package/dist/commands/hook/parser.js +40 -0
- package/dist/commands/hook/runner.d.ts +2 -0
- package/dist/commands/hook/runner.js +174 -0
- package/dist/commands/licenses/parser.js +39 -0
- package/dist/commands/licenses/runner.js +5 -1
- package/dist/commands/resolve/graph/builder.js +5 -1
- package/dist/commands/resolve/parser.js +39 -0
- package/dist/commands/resolve/runner.js +5 -0
- package/dist/commands/review/parser.js +44 -0
- package/dist/commands/snapshot/parser.js +39 -0
- package/dist/commands/snapshot/runner.js +4 -1
- package/dist/commands/unused/parser.js +39 -0
- package/dist/commands/unused/runner.js +4 -1
- package/dist/commands/unused/scanner.d.ts +2 -1
- package/dist/commands/unused/scanner.js +60 -44
- package/dist/core/check.js +5 -1
- package/dist/core/init-ci.js +28 -26
- package/dist/core/options.d.ts +4 -1
- package/dist/core/options.js +57 -0
- package/dist/core/verification.js +8 -6
- package/dist/core/warm-cache.js +5 -1
- package/dist/generated/version.d.ts +1 -0
- package/dist/generated/version.js +2 -0
- package/dist/git/scope.d.ts +19 -0
- package/dist/git/scope.js +167 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/sarif.js +2 -8
- package/dist/pm/detect.d.ts +37 -0
- package/dist/pm/detect.js +133 -2
- package/dist/pm/install.d.ts +2 -1
- package/dist/pm/install.js +7 -5
- package/dist/rup +0 -0
- package/dist/types/index.d.ts +58 -0
- package/dist/ui/tui.js +152 -64
- package/dist/workspace/discover.d.ts +7 -1
- package/dist/workspace/discover.js +12 -3
- package/package.json +10 -5
package/dist/ui/tui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import React from "react";
|
|
3
3
|
import { Box, render, Text, useInput } from "ink";
|
|
4
4
|
const FILTER_ORDER = [
|
|
5
5
|
"all",
|
|
@@ -22,20 +22,86 @@ const DETAIL_TABS = [
|
|
|
22
22
|
"health",
|
|
23
23
|
"changelog",
|
|
24
24
|
];
|
|
25
|
+
function tuiReducer(state, action) {
|
|
26
|
+
switch (action.type) {
|
|
27
|
+
case "SET_SEARCH_MODE":
|
|
28
|
+
return {
|
|
29
|
+
...state,
|
|
30
|
+
searchMode: action.active,
|
|
31
|
+
...(action.active ? {} : { search: "", cursorIndex: 0 }),
|
|
32
|
+
};
|
|
33
|
+
case "SET_SEARCH":
|
|
34
|
+
return { ...state, search: action.value, cursorIndex: 0 };
|
|
35
|
+
case "APPEND_SEARCH":
|
|
36
|
+
return { ...state, search: state.search + action.value, cursorIndex: 0 };
|
|
37
|
+
case "BACKSPACE_SEARCH":
|
|
38
|
+
return { ...state, search: state.search.slice(0, -1), cursorIndex: 0 };
|
|
39
|
+
case "TOGGLE_HELP":
|
|
40
|
+
return { ...state, showHelp: !state.showHelp };
|
|
41
|
+
case "SET_HELP":
|
|
42
|
+
return { ...state, showHelp: action.active };
|
|
43
|
+
case "MOVE_FILTER":
|
|
44
|
+
return {
|
|
45
|
+
...state,
|
|
46
|
+
filterIndex: Math.min(action.max, Math.max(0, state.filterIndex + action.direction)),
|
|
47
|
+
cursorIndex: 0,
|
|
48
|
+
};
|
|
49
|
+
case "MOVE_CURSOR":
|
|
50
|
+
return {
|
|
51
|
+
...state,
|
|
52
|
+
cursorIndex: Math.min(action.max, Math.max(0, state.cursorIndex + action.direction)),
|
|
53
|
+
};
|
|
54
|
+
case "RESET_CURSOR":
|
|
55
|
+
return { ...state, cursorIndex: 0 };
|
|
56
|
+
case "CYCLE_SORT":
|
|
57
|
+
return {
|
|
58
|
+
...state,
|
|
59
|
+
sortIndex: (state.sortIndex + 1) % action.max,
|
|
60
|
+
cursorIndex: 0,
|
|
61
|
+
};
|
|
62
|
+
case "CYCLE_GROUP":
|
|
63
|
+
return {
|
|
64
|
+
...state,
|
|
65
|
+
groupIndex: (state.groupIndex + 1) % action.max,
|
|
66
|
+
cursorIndex: 0,
|
|
67
|
+
};
|
|
68
|
+
case "CYCLE_TAB":
|
|
69
|
+
return { ...state, tabIndex: (state.tabIndex + 1) % action.max };
|
|
70
|
+
case "SET_SELECTED":
|
|
71
|
+
return { ...state, selectedIndices: action.indices };
|
|
72
|
+
case "TOGGLE_SELECTED": {
|
|
73
|
+
const next = new Set(state.selectedIndices);
|
|
74
|
+
if (next.has(action.index))
|
|
75
|
+
next.delete(action.index);
|
|
76
|
+
else
|
|
77
|
+
next.add(action.index);
|
|
78
|
+
return { ...state, selectedIndices: next };
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
return state;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
25
84
|
function TuiApp({ items, title, subtitle, onComplete }) {
|
|
26
|
-
const [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
85
|
+
const [state, dispatch] = React.useReducer(tuiReducer, undefined, () => ({
|
|
86
|
+
cursorIndex: 0,
|
|
87
|
+
filterIndex: 0,
|
|
88
|
+
sortIndex: 0,
|
|
89
|
+
groupIndex: 0,
|
|
90
|
+
tabIndex: 0,
|
|
91
|
+
showHelp: false,
|
|
92
|
+
searchMode: false,
|
|
93
|
+
search: "",
|
|
94
|
+
selectedIndices: new Set(items.flatMap((item, index) => item.update.selectedByDefault === false ? [] : [index])),
|
|
95
|
+
}));
|
|
96
|
+
const activeFilter = FILTER_ORDER[state.filterIndex] ?? "all";
|
|
97
|
+
const activeSort = SORT_ORDER[state.sortIndex] ?? "risk";
|
|
98
|
+
const activeGroup = GROUP_ORDER[state.groupIndex] ?? "none";
|
|
99
|
+
const activeTab = DETAIL_TABS[state.tabIndex] ?? "overview";
|
|
100
|
+
const searchMode = state.searchMode;
|
|
101
|
+
const search = state.search;
|
|
102
|
+
const showHelp = state.showHelp;
|
|
103
|
+
const selectedIndices = state.selectedIndices;
|
|
104
|
+
const filterIndex = state.filterIndex;
|
|
39
105
|
const visibleRows = buildVisibleRows(items, {
|
|
40
106
|
filter: activeFilter,
|
|
41
107
|
sort: activeSort,
|
|
@@ -43,91 +109,100 @@ function TuiApp({ items, title, subtitle, onComplete }) {
|
|
|
43
109
|
search,
|
|
44
110
|
});
|
|
45
111
|
const itemRows = visibleRows.filter((row) => row.kind === "item" && typeof row.index === "number");
|
|
46
|
-
const boundedCursor = Math.min(cursorIndex, Math.max(0, itemRows.length - 1));
|
|
112
|
+
const boundedCursor = Math.min(state.cursorIndex, Math.max(0, itemRows.length - 1));
|
|
47
113
|
const focusedIndex = itemRows[boundedCursor]?.index ?? 0;
|
|
48
114
|
const focusedItem = items[focusedIndex];
|
|
49
115
|
useInput((input, key) => {
|
|
50
116
|
if (searchMode) {
|
|
51
117
|
if (key.escape) {
|
|
52
|
-
|
|
53
|
-
setSearch("");
|
|
54
|
-
setCursorIndex(0);
|
|
118
|
+
dispatch({ type: "SET_SEARCH_MODE", active: false });
|
|
55
119
|
return;
|
|
56
120
|
}
|
|
57
121
|
if (key.return) {
|
|
58
|
-
|
|
59
|
-
|
|
122
|
+
dispatch({ type: "SET_SEARCH_MODE", active: false });
|
|
123
|
+
dispatch({ type: "SET_SEARCH", value: search }); // keeps search but exits mode
|
|
60
124
|
return;
|
|
61
125
|
}
|
|
62
126
|
if (key.backspace || key.delete) {
|
|
63
|
-
|
|
64
|
-
setCursorIndex(0);
|
|
127
|
+
dispatch({ type: "BACKSPACE_SEARCH" });
|
|
65
128
|
return;
|
|
66
129
|
}
|
|
67
130
|
if (input && !key.ctrl && !key.meta) {
|
|
68
|
-
|
|
69
|
-
setCursorIndex(0);
|
|
131
|
+
dispatch({ type: "APPEND_SEARCH", value: input });
|
|
70
132
|
}
|
|
71
133
|
return;
|
|
72
134
|
}
|
|
73
135
|
if (input === "/") {
|
|
74
|
-
|
|
136
|
+
dispatch({ type: "SET_SEARCH_MODE", active: true });
|
|
75
137
|
return;
|
|
76
138
|
}
|
|
77
139
|
if (input === "?") {
|
|
78
|
-
|
|
140
|
+
dispatch({ type: "TOGGLE_HELP" });
|
|
79
141
|
return;
|
|
80
142
|
}
|
|
81
143
|
if (key.escape && showHelp) {
|
|
82
|
-
|
|
144
|
+
dispatch({ type: "SET_HELP", active: false });
|
|
83
145
|
return;
|
|
84
146
|
}
|
|
85
147
|
if (key.leftArrow) {
|
|
86
|
-
|
|
87
|
-
|
|
148
|
+
dispatch({
|
|
149
|
+
type: "MOVE_FILTER",
|
|
150
|
+
direction: -1,
|
|
151
|
+
max: FILTER_ORDER.length - 1,
|
|
152
|
+
});
|
|
88
153
|
}
|
|
89
154
|
if (key.rightArrow) {
|
|
90
|
-
|
|
91
|
-
|
|
155
|
+
dispatch({
|
|
156
|
+
type: "MOVE_FILTER",
|
|
157
|
+
direction: 1,
|
|
158
|
+
max: FILTER_ORDER.length - 1,
|
|
159
|
+
});
|
|
92
160
|
}
|
|
93
161
|
if (key.upArrow) {
|
|
94
|
-
|
|
162
|
+
dispatch({
|
|
163
|
+
type: "MOVE_CURSOR",
|
|
164
|
+
direction: -1,
|
|
165
|
+
max: itemRows.length - 1,
|
|
166
|
+
});
|
|
95
167
|
}
|
|
96
168
|
if (key.downArrow) {
|
|
97
|
-
|
|
169
|
+
dispatch({ type: "MOVE_CURSOR", direction: 1, max: itemRows.length - 1 });
|
|
98
170
|
}
|
|
99
171
|
if (input === "o") {
|
|
100
|
-
|
|
101
|
-
setCursorIndex(0);
|
|
172
|
+
dispatch({ type: "CYCLE_SORT", max: SORT_ORDER.length });
|
|
102
173
|
}
|
|
103
174
|
if (input === "g") {
|
|
104
|
-
|
|
105
|
-
setCursorIndex(0);
|
|
175
|
+
dispatch({ type: "CYCLE_GROUP", max: GROUP_ORDER.length });
|
|
106
176
|
}
|
|
107
177
|
if (key.tab) {
|
|
108
|
-
|
|
178
|
+
dispatch({ type: "CYCLE_TAB", max: DETAIL_TABS.length });
|
|
109
179
|
}
|
|
110
180
|
if (input === "a") {
|
|
111
|
-
|
|
181
|
+
dispatch({
|
|
182
|
+
type: "SET_SELECTED",
|
|
183
|
+
indices: addVisible(selectedIndices, itemRows),
|
|
184
|
+
});
|
|
112
185
|
}
|
|
113
186
|
if (input === "n") {
|
|
114
|
-
|
|
187
|
+
dispatch({
|
|
188
|
+
type: "SET_SELECTED",
|
|
189
|
+
indices: removeVisible(selectedIndices, itemRows),
|
|
190
|
+
});
|
|
115
191
|
}
|
|
116
192
|
if (input === "s") {
|
|
117
|
-
|
|
193
|
+
dispatch({
|
|
194
|
+
type: "SET_SELECTED",
|
|
195
|
+
indices: selectSafe(selectedIndices, itemRows, items),
|
|
196
|
+
});
|
|
118
197
|
}
|
|
119
198
|
if (input === "b") {
|
|
120
|
-
|
|
199
|
+
dispatch({
|
|
200
|
+
type: "SET_SELECTED",
|
|
201
|
+
indices: clearBlocked(selectedIndices, itemRows, items),
|
|
202
|
+
});
|
|
121
203
|
}
|
|
122
204
|
if (input === " ") {
|
|
123
|
-
|
|
124
|
-
const next = new Set(prev);
|
|
125
|
-
if (next.has(focusedIndex))
|
|
126
|
-
next.delete(focusedIndex);
|
|
127
|
-
else
|
|
128
|
-
next.add(focusedIndex);
|
|
129
|
-
return next;
|
|
130
|
-
});
|
|
205
|
+
dispatch({ type: "TOGGLE_SELECTED", index: focusedIndex });
|
|
131
206
|
}
|
|
132
207
|
if (input === "q" || key.escape) {
|
|
133
208
|
onComplete(items.filter((_, index) => selectedIndices.has(index)));
|
|
@@ -138,7 +213,7 @@ function TuiApp({ items, title, subtitle, onComplete }) {
|
|
|
138
213
|
}
|
|
139
214
|
});
|
|
140
215
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: title ?? "Rainy Dashboard" }), _jsx(Text, { color: "gray", children: subtitle ??
|
|
141
|
-
"Check detects, doctor summarizes, dashboard decides, upgrade applies." }), _jsx(Text, { color: "gray", children: "Filters: \u2190/\u2192
|
|
216
|
+
"Check detects, doctor summarizes, dashboard decides, upgrade applies." }), _jsx(Text, { color: "gray", children: "Filters: \u2190/\u2192 Sort: o Group: g Tabs: Tab Search: / Help: ? Space: toggle Enter: confirm" }), _jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsxs(Box, { width: 24, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Filter Rail" }), FILTER_ORDER.map((filter, index) => (_jsxs(Text, { color: index === filterIndex ? "cyan" : "gray", children: [index === filterIndex ? ">" : " ", " ", filter] }, filter))), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Search" }), _jsx(Text, { color: searchMode ? "cyan" : "gray", children: searchMode ? `/${search}` : search ? `/${search}` : "inactive" })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Modes" }), _jsxs(Text, { color: "gray", children: ["sort: ", activeSort] }), _jsxs(Text, { color: "gray", children: ["group: ", activeGroup] }), _jsxs(Text, { color: "gray", children: ["tab: ", activeTab] })] })] }), _jsxs(Box, { marginLeft: 1, width: 82, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Review Queue" }), itemRows.length === 0 ? (_jsx(Text, { color: "gray", children: "No review candidates match this view." })) : (visibleRows.map((row, visibleIndex) => {
|
|
142
217
|
if (row.kind === "group") {
|
|
143
218
|
return (_jsx(Text, { bold: true, color: "gray", children: row.label }, `group:${row.label}`));
|
|
144
219
|
}
|
|
@@ -150,18 +225,18 @@ function TuiApp({ items, title, subtitle, onComplete }) {
|
|
|
150
225
|
const isFocused = itemPosition === boundedCursor;
|
|
151
226
|
const isSelected = selectedIndices.has(index);
|
|
152
227
|
return (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: isFocused ? "cyan" : "gray", children: [isFocused ? ">" : " ", " ", isSelected ? "[x]" : "[ ]", " "] }), _jsx(Box, { width: 22, children: _jsx(Text, { bold: isFocused, children: update.name }) }), _jsx(Box, { width: 14, children: _jsx(Text, { color: diffColor(update.diffType), children: update.diffType }) }), _jsx(Box, { width: 14, children: _jsx(Text, { color: riskColor(update.riskLevel), children: update.riskLevel ?? "low" }) }), _jsx(Box, { width: 14, children: _jsx(Text, { color: decisionColor(decision), children: decision }) }), _jsx(Box, { width: 10, children: _jsx(Text, { color: decisionColor(decision), children: update.riskScore ?? "--" }) }), _jsx(Text, { color: "gray", children: update.fromRange }), _jsx(Text, { color: "gray", children: " \u2192 " }), _jsx(Text, { color: "green", children: update.toVersionResolved })] }, `${update.packagePath}:${update.name}`));
|
|
153
|
-
}))] }), _jsxs(Box, { marginLeft: 1, width: 54, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Decision Panel" }), _jsxs(Text, { color: "gray", children: ["tab: ", activeTab] }), focusedItem ? renderTab(focusedItem, activeTab) : _jsx(Text, { color: "gray", children: "No review candidate selected." })] })] }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsxs(Text, { color: "gray", children: [selectedIndices.size, " selected of ", items.length, ". view=", activeFilter, " sort=", activeSort, " group=", activeGroup] }) }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsx(Text, { color: "gray", children: "A select visible
|
|
228
|
+
}))] }), _jsxs(Box, { marginLeft: 1, width: 54, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Decision Panel" }), _jsxs(Text, { color: "gray", children: ["tab: ", activeTab] }), focusedItem ? (renderTab(focusedItem, activeTab)) : (_jsx(Text, { color: "gray", children: "No review candidate selected." }))] })] }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsxs(Text, { color: "gray", children: [selectedIndices.size, " selected of ", items.length, ". view=", activeFilter, " ", "sort=", activeSort, " group=", activeGroup] }) }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: _jsx(Text, { color: "gray", children: "A select visible N clear visible S select safe B clear blocked Q finish Esc clears search/help" }) }), showHelp ? (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Help" }), _jsx(Text, { color: "gray", children: "Use review as the decision center. Search packages with / and inspect details with Tab." }), _jsx(Text, { color: "gray", children: "Blocked items default to deselected. Safe items can be bulk-selected with S." })] })) : null] }));
|
|
154
229
|
}
|
|
155
230
|
function renderTab(item, tab) {
|
|
156
231
|
const update = item.update;
|
|
157
232
|
if (tab === "risk") {
|
|
158
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["state: ", _jsx(Text, { color: decisionColor(update.decisionState ?? deriveDecision(item)), children: update.decisionState ?? deriveDecision(item) })] }), _jsxs(Text, { children: ["policy: ", _jsx(Text, { color: decisionColor(policyToDecision(update.policyAction)), children: update.policyAction ?? "allow" })] }), _jsxs(Text, { children: ["risk score: ", update.riskScore ?? 0] }), _jsxs(Text, { children: ["impact score: ", update.impactScore?.score ?? 0] }), _jsxs(Text, { children: ["recommended action: ", update.recommendedAction ?? "Safe to keep in the selected set."] }), update.riskReasons && update.riskReasons.length > 0 ? update.riskReasons.slice(0, 5).map((reason) => (_jsxs(Text, { color: "gray", children: ["- ", reason] }, reason))) : _jsx(Text, { color: "gray", children: "No elevated risk reasons." })] }));
|
|
233
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["state:", " ", _jsx(Text, { color: decisionColor(update.decisionState ?? deriveDecision(item)), children: update.decisionState ?? deriveDecision(item) })] }), _jsxs(Text, { children: ["policy:", " ", _jsx(Text, { color: decisionColor(policyToDecision(update.policyAction)), children: update.policyAction ?? "allow" })] }), _jsxs(Text, { children: ["risk score: ", update.riskScore ?? 0] }), _jsxs(Text, { children: ["impact score: ", update.impactScore?.score ?? 0] }), _jsxs(Text, { children: ["recommended action:", " ", update.recommendedAction ?? "Safe to keep in the selected set."] }), update.riskReasons && update.riskReasons.length > 0 ? (update.riskReasons.slice(0, 5).map((reason) => (_jsxs(Text, { color: "gray", children: ["- ", reason] }, reason)))) : (_jsx(Text, { color: "gray", children: "No elevated risk reasons." }))] }));
|
|
159
234
|
}
|
|
160
235
|
if (tab === "security") {
|
|
161
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["advisories: ", item.advisories.length] }), item.advisories.length > 0 ? item.advisories.slice(0, 4).map((advisory) => (_jsxs(Text, { color: "gray", children: ["- ", advisory.severity, " ", advisory.cveId, ": ", advisory.title] }, `${advisory.packageName}:${advisory.cveId}`))) : _jsx(Text, { color: "gray", children: "No security advisories detected." })] }));
|
|
236
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["advisories: ", item.advisories.length] }), item.advisories.length > 0 ? (item.advisories.slice(0, 4).map((advisory) => (_jsxs(Text, { color: "gray", children: ["- ", advisory.severity, " ", advisory.cveId, ": ", advisory.title] }, `${advisory.packageName}:${advisory.cveId}`)))) : (_jsx(Text, { color: "gray", children: "No security advisories detected." }))] }));
|
|
162
237
|
}
|
|
163
238
|
if (tab === "peer") {
|
|
164
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["peer status: ", update.peerConflictSeverity ?? "none"] }), item.peerConflicts.length > 0 ? item.peerConflicts.slice(0, 4).map((conflict) => (_jsxs(Text, { color: "gray", children: ["- ", conflict.requester, " requires ", conflict.peer, " ", conflict.requiredRange] }, `${conflict.requester}:${conflict.peer}`))) : _jsx(Text, { color: "gray", children: "No peer conflicts detected." })] }));
|
|
239
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["peer status: ", update.peerConflictSeverity ?? "none"] }), item.peerConflicts.length > 0 ? (item.peerConflicts.slice(0, 4).map((conflict) => (_jsxs(Text, { color: "gray", children: ["- ", conflict.requester, " requires ", conflict.peer, " ", conflict.requiredRange] }, `${conflict.requester}:${conflict.peer}`)))) : (_jsx(Text, { color: "gray", children: "No peer conflicts detected." }))] }));
|
|
165
240
|
}
|
|
166
241
|
if (tab === "license") {
|
|
167
242
|
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["license status: ", update.licenseStatus ?? "allowed"] }), _jsxs(Text, { children: ["repository: ", update.repository ?? "unavailable"] }), _jsxs(Text, { children: ["homepage: ", update.homepage ?? "unavailable"] })] }));
|
|
@@ -170,9 +245,10 @@ function renderTab(item, tab) {
|
|
|
170
245
|
return (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["health: ", update.healthStatus ?? "healthy"] }), _jsxs(Text, { children: ["maintainers: ", update.maintainerCount ?? "unknown"] }), _jsxs(Text, { children: ["publish age days: ", update.publishAgeDays ?? "unknown"] }), _jsxs(Text, { children: ["maintainer churn: ", update.maintainerChurn ?? "unknown"] })] }));
|
|
171
246
|
}
|
|
172
247
|
if (tab === "changelog") {
|
|
173
|
-
return (_jsxs(_Fragment, { children: [_jsx(Text, { children: update.releaseNotesSummary?.title ?? "Release notes unavailable" }), _jsx(Text, { color: "gray", children: update.releaseNotesSummary?.excerpt ??
|
|
248
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { children: update.releaseNotesSummary?.title ?? "Release notes unavailable" }), _jsx(Text, { color: "gray", children: update.releaseNotesSummary?.excerpt ??
|
|
249
|
+
"Run review with changelog support or inspect the repository manually." })] }));
|
|
174
250
|
}
|
|
175
|
-
return (_jsxs(_Fragment, { children: [_jsx(Text, { children: update.name }), _jsxs(Text, { color: "gray", children: ["package: ", update.packagePath] }), _jsxs(Text, { children: ["state: ", _jsx(Text, { color: decisionColor(update.decisionState ?? deriveDecision(item)), children: update.decisionState ?? deriveDecision(item) })] }), _jsxs(Text, { children: ["diff: ", _jsx(Text, { color: diffColor(update.diffType), children: update.diffType })] }), _jsxs(Text, { children: ["risk: ", _jsx(Text, { color: riskColor(update.riskLevel), children: update.riskLevel ?? "low" })] }), _jsxs(Text, { children: ["policy: ", update.policyAction ?? "allow"] }), _jsxs(Text, { children: ["workspace: ", update.workspaceGroup ?? "root"] }), _jsxs(Text, { children: ["group: ", update.groupKey ?? "none"] }), _jsxs(Text, { children: ["action: ", update.recommendedAction ?? "Safe to keep in the selected set."] })] }));
|
|
251
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { children: update.name }), _jsxs(Text, { color: "gray", children: ["package: ", update.packagePath] }), _jsxs(Text, { children: ["state:", " ", _jsx(Text, { color: decisionColor(update.decisionState ?? deriveDecision(item)), children: update.decisionState ?? deriveDecision(item) })] }), _jsxs(Text, { children: ["diff: ", _jsx(Text, { color: diffColor(update.diffType), children: update.diffType })] }), _jsxs(Text, { children: ["risk:", " ", _jsx(Text, { color: riskColor(update.riskLevel), children: update.riskLevel ?? "low" })] }), _jsxs(Text, { children: ["policy: ", update.policyAction ?? "allow"] }), _jsxs(Text, { children: ["workspace: ", update.workspaceGroup ?? "root"] }), _jsxs(Text, { children: ["group: ", update.groupKey ?? "none"] }), _jsxs(Text, { children: ["action:", " ", update.recommendedAction ?? "Safe to keep in the selected set."] })] }));
|
|
176
252
|
}
|
|
177
253
|
function buildVisibleRows(items, config) {
|
|
178
254
|
const filtered = items
|
|
@@ -181,7 +257,11 @@ function buildVisibleRows(items, config) {
|
|
|
181
257
|
.filter(({ item }) => matchesSearch(item, config.search))
|
|
182
258
|
.sort((left, right) => compareItems(left.item, right.item, config.sort));
|
|
183
259
|
if (config.group === "none") {
|
|
184
|
-
return filtered.map(({ item, index }) => ({
|
|
260
|
+
return filtered.map(({ item, index }) => ({
|
|
261
|
+
kind: "item",
|
|
262
|
+
label: item.update.name,
|
|
263
|
+
index,
|
|
264
|
+
}));
|
|
185
265
|
}
|
|
186
266
|
const rows = [];
|
|
187
267
|
let currentGroup = "";
|
|
@@ -191,7 +271,11 @@ function buildVisibleRows(items, config) {
|
|
|
191
271
|
currentGroup = nextGroup;
|
|
192
272
|
rows.push({ kind: "group", label: nextGroup });
|
|
193
273
|
}
|
|
194
|
-
rows.push({
|
|
274
|
+
rows.push({
|
|
275
|
+
kind: "item",
|
|
276
|
+
label: entry.item.update.name,
|
|
277
|
+
index: entry.index,
|
|
278
|
+
});
|
|
195
279
|
}
|
|
196
280
|
return rows;
|
|
197
281
|
}
|
|
@@ -199,7 +283,7 @@ function matchesFilter(item, filter) {
|
|
|
199
283
|
if (filter === "security")
|
|
200
284
|
return item.advisories.length > 0;
|
|
201
285
|
if (filter === "risky")
|
|
202
|
-
return item.update.riskLevel === "critical" || item.update.riskLevel === "high";
|
|
286
|
+
return (item.update.riskLevel === "critical" || item.update.riskLevel === "high");
|
|
203
287
|
if (filter === "major")
|
|
204
288
|
return item.update.diffType === "major";
|
|
205
289
|
if (filter === "peer-conflict")
|
|
@@ -275,7 +359,8 @@ function removeVisible(selected, rows) {
|
|
|
275
359
|
function selectSafe(selected, rows, items) {
|
|
276
360
|
const next = new Set(selected);
|
|
277
361
|
for (const row of rows) {
|
|
278
|
-
if ((items[row.index]?.update.decisionState ??
|
|
362
|
+
if ((items[row.index]?.update.decisionState ??
|
|
363
|
+
deriveDecision(items[row.index])) === "safe") {
|
|
279
364
|
next.add(row.index);
|
|
280
365
|
}
|
|
281
366
|
}
|
|
@@ -284,17 +369,20 @@ function selectSafe(selected, rows, items) {
|
|
|
284
369
|
function clearBlocked(selected, rows, items) {
|
|
285
370
|
const next = new Set(selected);
|
|
286
371
|
for (const row of rows) {
|
|
287
|
-
if ((items[row.index]?.update.decisionState ??
|
|
372
|
+
if ((items[row.index]?.update.decisionState ??
|
|
373
|
+
deriveDecision(items[row.index])) === "blocked") {
|
|
288
374
|
next.delete(row.index);
|
|
289
375
|
}
|
|
290
376
|
}
|
|
291
377
|
return next;
|
|
292
378
|
}
|
|
293
379
|
function deriveDecision(item) {
|
|
294
|
-
if (item.update.peerConflictSeverity === "error" ||
|
|
380
|
+
if (item.update.peerConflictSeverity === "error" ||
|
|
381
|
+
item.update.licenseStatus === "denied") {
|
|
295
382
|
return "blocked";
|
|
296
383
|
}
|
|
297
|
-
if ((item.update.advisoryCount ?? 0) > 0 ||
|
|
384
|
+
if ((item.update.advisoryCount ?? 0) > 0 ||
|
|
385
|
+
item.update.riskLevel === "critical") {
|
|
298
386
|
return "actionable";
|
|
299
387
|
}
|
|
300
388
|
if (item.update.riskLevel === "high" || item.update.diffType === "major") {
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DependencyKind } from "../types/index.js";
|
|
2
|
+
import { type GitScopeOptions } from "../git/scope.js";
|
|
3
|
+
export declare function discoverPackageDirs(cwd: string, workspaceMode: boolean, options?: {
|
|
4
|
+
git?: GitScopeOptions;
|
|
5
|
+
includeKinds?: DependencyKind[];
|
|
6
|
+
includeDependents?: boolean;
|
|
7
|
+
}): Promise<string[]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { scopePackageDirsByGit } from "../git/scope.js";
|
|
2
3
|
const HARD_IGNORE_DIRS = new Set([
|
|
3
4
|
"node_modules",
|
|
4
5
|
".git",
|
|
@@ -8,9 +9,13 @@ const HARD_IGNORE_DIRS = new Set([
|
|
|
8
9
|
"coverage",
|
|
9
10
|
]);
|
|
10
11
|
const MAX_DISCOVERED_DIRS = 20000;
|
|
11
|
-
export async function discoverPackageDirs(cwd, workspaceMode) {
|
|
12
|
+
export async function discoverPackageDirs(cwd, workspaceMode, options = {}) {
|
|
12
13
|
if (!workspaceMode) {
|
|
13
|
-
|
|
14
|
+
const scoped = await scopePackageDirsByGit(cwd, [cwd], options.git ?? {}, {
|
|
15
|
+
includeKinds: options.includeKinds,
|
|
16
|
+
includeDependents: options.includeDependents,
|
|
17
|
+
});
|
|
18
|
+
return scoped.packageDirs;
|
|
14
19
|
}
|
|
15
20
|
const roots = new Set([cwd]);
|
|
16
21
|
const patterns = [
|
|
@@ -40,7 +45,11 @@ export async function discoverPackageDirs(cwd, workspaceMode) {
|
|
|
40
45
|
existing.push(dir);
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
|
-
|
|
48
|
+
const scoped = await scopePackageDirsByGit(cwd, existing.sort(), options.git ?? {}, {
|
|
49
|
+
includeKinds: options.includeKinds,
|
|
50
|
+
includeDependents: options.includeDependents,
|
|
51
|
+
});
|
|
52
|
+
return scoped.packageDirs;
|
|
44
53
|
}
|
|
45
54
|
async function packageFileExists(packageJsonPath) {
|
|
46
55
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainy-updates/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "The fastest DevOps-first dependency CLI. Checks, audits, upgrades, bisects, and automates npm/pnpm dependencies in CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"renovate"
|
|
31
31
|
],
|
|
32
32
|
"bin": {
|
|
33
|
+
"cli": "./dist/bin/cli.js",
|
|
33
34
|
"rainy-updates": "./dist/bin/cli.js",
|
|
34
35
|
"rainy-up": "./dist/bin/cli.js",
|
|
35
36
|
"rup": "./dist/bin/cli.js"
|
|
@@ -51,10 +52,13 @@
|
|
|
51
52
|
],
|
|
52
53
|
"scripts": {
|
|
53
54
|
"clean": "rm -rf dist",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
55
|
+
"version:sync": "bun scripts/sync-version.mjs",
|
|
56
|
+
"version:check": "bun scripts/sync-version.mjs --check",
|
|
57
|
+
"version": "bun run version:sync",
|
|
58
|
+
"build": "bun run version:sync && tsc -p tsconfig.build.json",
|
|
59
|
+
"build:exe": "bun run version:sync && bun build ./src/bin/cli.ts --compile --outfile dist/rup",
|
|
60
|
+
"typecheck": "bun run version:check && tsc --noEmit",
|
|
61
|
+
"test": "bun run version:check && bun test",
|
|
58
62
|
"lint": "bunx biome check src tests",
|
|
59
63
|
"check": "bun run typecheck && bun test",
|
|
60
64
|
"bench:fixtures": "bun run scripts/generate-benchmark-fixtures.mjs",
|
|
@@ -86,6 +90,7 @@
|
|
|
86
90
|
},
|
|
87
91
|
"dependencies": {
|
|
88
92
|
"ink": "^6.8.0",
|
|
93
|
+
"oxc-parser": "^0.115.0",
|
|
89
94
|
"react": "^19.2.4",
|
|
90
95
|
"zod": "^4.3.6"
|
|
91
96
|
}
|