ccperm 1.11.2 → 1.12.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/dist/interactive.js +42 -7
- package/package.json +1 -1
package/dist/interactive.js
CHANGED
|
@@ -62,6 +62,7 @@ function startInteractive(merged, results) {
|
|
|
62
62
|
const projects = merged.filter((r) => r.totalCount > 0 && !r.isGlobal).sort((a, b) => b.totalCount - a.totalCount);
|
|
63
63
|
const withPerms = [...globals, ...projects];
|
|
64
64
|
const emptyCount = merged.filter((r) => r.totalCount === 0 && !r.isGlobal).length;
|
|
65
|
+
const riskMap = buildRiskMap(results);
|
|
65
66
|
if (withPerms.length === 0) {
|
|
66
67
|
console.log(`\n ${colors_js_1.GREEN}No projects with permissions found.${colors_js_1.NC}\n`);
|
|
67
68
|
resolve();
|
|
@@ -85,7 +86,7 @@ function startInteractive(merged, results) {
|
|
|
85
86
|
const render = () => {
|
|
86
87
|
process.stdout.write('\x1b[2J\x1b[H\x1b[?25l');
|
|
87
88
|
if (state.view === 'list')
|
|
88
|
-
renderList(state, withPerms, emptyCount);
|
|
89
|
+
renderList(state, withPerms, emptyCount, riskMap);
|
|
89
90
|
else
|
|
90
91
|
renderDetail(state, withPerms, results);
|
|
91
92
|
};
|
|
@@ -136,16 +137,39 @@ function startInteractive(merged, results) {
|
|
|
136
137
|
render();
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
|
-
function
|
|
140
|
+
function buildRiskMap(results) {
|
|
141
|
+
const map = new Map();
|
|
142
|
+
for (const r of results) {
|
|
143
|
+
let crit = 0, hi = 0;
|
|
144
|
+
for (const g of r.groups) {
|
|
145
|
+
for (const item of g.items) {
|
|
146
|
+
const info = (0, explain_js_1.explain)(g.category, item.name);
|
|
147
|
+
if (info.risk === 'critical')
|
|
148
|
+
crit++;
|
|
149
|
+
else if (info.risk === 'high')
|
|
150
|
+
hi++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
map.set(r.display, { critical: crit, high: hi });
|
|
154
|
+
}
|
|
155
|
+
return map;
|
|
156
|
+
}
|
|
157
|
+
function renderList(state, withPerms, emptyCount, riskMap) {
|
|
140
158
|
const rows = process.stdout.rows || 24;
|
|
141
159
|
const cols = process.stdout.columns || 80;
|
|
142
|
-
const w = Math.min(cols, 82);
|
|
143
|
-
const inner = w - 4; // box border + 1 space each side
|
|
144
160
|
const cats = ['Bash', 'WebFetch', 'MCP', 'Tools'];
|
|
145
161
|
const catsPresent = cats.filter((c) => withPerms.some((r) => r.groups.has(c)));
|
|
162
|
+
const hasRisk = [...riskMap.values()].some((v) => v.critical > 0 || v.high > 0);
|
|
163
|
+
const riskColWidth = hasRisk ? 3 : 0;
|
|
146
164
|
const catColWidth = catsPresent.length * 7;
|
|
165
|
+
const typeColWidth = 7;
|
|
147
166
|
const maxName = Math.max(...withPerms.map((r) => r.shortName.length), 7);
|
|
148
|
-
const
|
|
167
|
+
const nameColWidth = Math.min(maxName + typeColWidth, 35);
|
|
168
|
+
const nameWidth = nameColWidth - typeColWidth;
|
|
169
|
+
// content: marker(2) + nameCol + gap(2) + catCols + gap(2) + total(5) + gap(1) + riskCol
|
|
170
|
+
const contentWidth = 2 + nameColWidth + 2 + catColWidth + 2 + 5 + (hasRisk ? 1 + riskColWidth : 0);
|
|
171
|
+
const w = Math.min(cols, contentWidth + 4);
|
|
172
|
+
const inner = w - 4;
|
|
149
173
|
const hasGlobalSep = withPerms.some((r) => r.isGlobal) && withPerms.some((r) => !r.isGlobal);
|
|
150
174
|
// box takes: top(1) + header(2) + sep(1) + content + globalSep?(1) + emptyLine?(1) + bottom(1)
|
|
151
175
|
const chrome = 5 + (hasGlobalSep ? 1 : 0) + (emptyCount > 0 ? 1 : 0);
|
|
@@ -157,7 +181,8 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
157
181
|
const scrollInfo = withPerms.length > visibleRows ? `${state.cursor + 1}/${withPerms.length}` : '';
|
|
158
182
|
const lines = [];
|
|
159
183
|
lines.push(boxTop('ccperm', scrollInfo, w));
|
|
160
|
-
|
|
184
|
+
const riskHeader = hasRisk ? ` ${colors_js_1.DIM}⚠${colors_js_1.NC}` : '';
|
|
185
|
+
lines.push(boxLine(`${colors_js_1.DIM}${pad('PROJECT', nameColWidth)} ${catsPresent.map((c) => rpad(c, 5)).join(' ')} TOTAL${colors_js_1.NC}${riskHeader}`, w));
|
|
161
186
|
lines.push(boxSep(w));
|
|
162
187
|
const globalCount = withPerms.filter((r) => r.isGlobal).length;
|
|
163
188
|
const end = Math.min(state.scrollOffset + visibleRows, withPerms.length);
|
|
@@ -176,7 +201,17 @@ function renderList(state, withPerms, emptyCount) {
|
|
|
176
201
|
return `${colors_js_1.DIM}${rpad('·', 5)}${colors_js_1.NC}`;
|
|
177
202
|
}).join(' ');
|
|
178
203
|
const totalCol = isCursor ? `${colors_js_1.BOLD}${rpad(r.totalCount, 5)}${colors_js_1.NC}` : rpad(r.totalCount, 5);
|
|
179
|
-
|
|
204
|
+
let riskCol = '';
|
|
205
|
+
if (hasRisk) {
|
|
206
|
+
const risk = riskMap.get(r.display) || { critical: 0, high: 0 };
|
|
207
|
+
if (risk.critical > 0)
|
|
208
|
+
riskCol = ` ${colors_js_1.RED}${rpad(risk.critical, 2)}${colors_js_1.NC}`;
|
|
209
|
+
else if (risk.high > 0)
|
|
210
|
+
riskCol = ` ${colors_js_1.YELLOW}${rpad(risk.high, 2)}${colors_js_1.NC}`;
|
|
211
|
+
else
|
|
212
|
+
riskCol = ` ${colors_js_1.DIM}${rpad('·', 2)}${colors_js_1.NC}`;
|
|
213
|
+
}
|
|
214
|
+
lines.push(boxLine(`${nameCol} ${catCols} ${totalCol}${riskCol}`, w));
|
|
180
215
|
// separator after global section
|
|
181
216
|
if (r.isGlobal && i + 1 < withPerms.length && !withPerms[i + 1].isGlobal) {
|
|
182
217
|
lines.push(boxSep(w));
|