@prism-d1/cli 1.4.3 → 1.5.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.
Files changed (38) hide show
  1. package/dist/assets/github-workflows/prism-eval-gate-kiro.yml +22 -2
  2. package/dist/assets/gitlab-workflows/prism-eval-gate.yml +22 -1
  3. package/dist/src/commands/enable/codeburn.d.ts +21 -1
  4. package/dist/src/commands/enable/codeburn.d.ts.map +1 -1
  5. package/dist/src/commands/enable/codeburn.js +199 -27
  6. package/dist/src/commands/enable/codeburn.js.map +1 -1
  7. package/dist/src/dashboard/codewatch.d.ts +3 -1
  8. package/dist/src/dashboard/codewatch.d.ts.map +1 -1
  9. package/dist/src/dashboard/codewatch.js +1 -0
  10. package/dist/src/dashboard/codewatch.js.map +1 -1
  11. package/dist/src/dashboard/page.d.ts.map +1 -1
  12. package/dist/src/dashboard/page.js +190 -53
  13. package/dist/src/dashboard/page.js.map +1 -1
  14. package/dist/src/dashboard/payload.d.ts +24 -4
  15. package/dist/src/dashboard/payload.d.ts.map +1 -1
  16. package/dist/src/dashboard/payload.js +41 -24
  17. package/dist/src/dashboard/payload.js.map +1 -1
  18. package/dist/src/dashboard/repos.d.ts +6 -0
  19. package/dist/src/dashboard/repos.d.ts.map +1 -1
  20. package/dist/src/dashboard/repos.js +24 -0
  21. package/dist/src/dashboard/repos.js.map +1 -1
  22. package/dist/src/dashboard/setup.d.ts +12 -3
  23. package/dist/src/dashboard/setup.d.ts.map +1 -1
  24. package/dist/src/dashboard/setup.js +15 -4
  25. package/dist/src/dashboard/setup.js.map +1 -1
  26. package/dist/src/dashboard/summary.d.ts +45 -0
  27. package/dist/src/dashboard/summary.d.ts.map +1 -0
  28. package/dist/src/dashboard/summary.js +19 -0
  29. package/dist/src/dashboard/summary.js.map +1 -0
  30. package/dist/src/dashboard/usage.d.ts +16 -0
  31. package/dist/src/dashboard/usage.d.ts.map +1 -1
  32. package/dist/src/dashboard/usage.js +31 -3
  33. package/dist/src/dashboard/usage.js.map +1 -1
  34. package/dist/src/utils/otel-sync-schedule.d.ts +16 -1
  35. package/dist/src/utils/otel-sync-schedule.d.ts.map +1 -1
  36. package/dist/src/utils/otel-sync-schedule.js +31 -3
  37. package/dist/src/utils/otel-sync-schedule.js.map +1 -1
  38. package/package.json +1 -1
@@ -8,15 +8,16 @@
8
8
  */
9
9
  const STYLE = `
10
10
  :root { color-scheme: light dark; --bg:#f7f7f8; --card:#fff; --text:#1d1d1f; --muted:#6b6b73; --line:#e3e3e8;
11
- --ok:#1a7f37; --warn:#9a6700; --bad:#cf222e; --high:#cf222e; --medium:#bf8700; --low:#57606a; --accent:#0969da; }
11
+ --ok:#1a7f37; --warn:#9a6700; --bad:#cf222e; --high:#cf222e; --medium:#bf8700; --low:#3d6fa8; --accent:#0969da; --raised:var(--bg); }
12
12
  @media (prefers-color-scheme: dark) { :root { --bg:#0d1117; --card:#161b22; --text:#e6edf3; --muted:#8b949e; --line:#30363d;
13
- --ok:#3fb950; --warn:#d29922; --bad:#f85149; --high:#f85149; --medium:#d29922; --low:#8b949e; --accent:#58a6ff; } }
13
+ --ok:#3fb950; --warn:#d29922; --bad:#f85149; --high:#f85149; --medium:#d29922; --low:#a5d6ff; --accent:#58a6ff; --raised:#21262d; } }
14
14
  * { box-sizing: border-box; }
15
15
  body { margin:0; font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--text); }
16
- header { display:flex; align-items:center; gap:12px; padding:14px 20px; border-bottom:1px solid var(--line); background:var(--card); }
16
+ header { display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; padding:14px 20px; border-bottom:1px solid var(--line); background:var(--card); }
17
17
  header h1 { font-size:17px; margin:0; } header .meta { color:var(--muted); font-size:12px; margin-left:auto; }
18
18
  button { font:inherit; padding:4px 10px; border:1px solid var(--line); border-radius:6px; background:var(--bg); color:var(--text); cursor:pointer; }
19
- main { padding:16px 20px; display:grid; gap:16px; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); }
19
+ main { padding:16px 20px; display:grid; gap:16px; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); }
20
+ @media (max-width: 640px) { main { padding:12px; gap:12px; } header { padding:12px; } header .meta { margin-left:0; flex-basis:100%; } }
20
21
  section.card { background:var(--card); border:1px solid var(--line); border-radius:10px; padding:14px 16px; min-width:0; }
21
22
  section.wide { grid-column: 1 / -1; }
22
23
  .sec-repo { border-top:1px solid var(--line); padding:10px 0; } .sec-repo:first-of-type { border-top:none; padding-top:0; } .sec-head { display:flex; gap:6px; align-items:baseline; flex-wrap:wrap; overflow-wrap:anywhere; }
@@ -46,6 +47,19 @@ a.rlink { color:var(--accent); } a.rlink:hover { text-decoration:underline; }
46
47
  svg.trend rect.high { fill:var(--high); } svg.trend rect.medium { fill:var(--medium); } svg.trend rect.low { fill:var(--low); } svg.trend rect.zero { fill:var(--ok); }
47
48
  svg.trend line { stroke:var(--line); }
48
49
  .repo-title { font-size:12px; color:var(--muted); margin-bottom:6px; word-break:break-all; }
50
+ .summary { display:grid; gap:14px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
51
+ @media (min-width: 641px) and (max-width: 1040px) { .summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
52
+ @media (max-width: 640px) { .summary { grid-template-columns: 1fr; gap:10px; } }
53
+ .scard { min-width:0; display:flex; flex-direction:column; gap:6px; padding:14px 16px; border:1px solid var(--line); border-radius:8px; background:var(--raised); }
54
+ .scard .k { color:var(--muted); font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.04em; }
55
+ .scard .v { font-size:28px; font-weight:700; line-height:1.15; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
56
+ .scard .v .unit { font-size:13px; font-weight:600; color:var(--muted); margin:0 10px 0 4px; }
57
+ .scard .d { color:var(--muted); font-size:13px; overflow-wrap:anywhere; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
58
+ .slim-line { padding:5px 0; border-top:1px solid var(--line); font-size:13px; overflow-wrap:anywhere; } .slim-line:first-of-type { border-top:none; } .slim-line code { white-space:normal; overflow-wrap:anywhere; }
59
+ .empty-line { color:var(--muted); margin:10px 0 4px; font-size:13px; overflow-wrap:anywhere; } .empty-line b { color:var(--text); }
60
+ .changed { outline:2px solid var(--accent); outline-offset:1px; border-radius:4px; }
61
+ @keyframes prism-flash { from { background-color:color-mix(in srgb, var(--accent) 35%, transparent); } to { background-color:transparent; } }
62
+ @media (prefers-reduced-motion: no-preference) { .changed { animation:prism-flash 2.5s ease-out 1; } }
49
63
  `;
50
64
  const SCRIPT = `
51
65
  'use strict';
@@ -68,6 +82,21 @@ function table(head, rows) {
68
82
  }
69
83
  const num = (v) => el('td', { class: 'num' }, txt(v));
70
84
 
85
+ /** Repo labels (a parent path when two repos share a name), set on each load from data.repos. */
86
+ let LABELS = new Map();
87
+ const labelOf = (r) => LABELS.get(r.path) || txt(r.name);
88
+
89
+ /** Sections with no data, each collected as one line into the "Nothing recorded yet" card. */
90
+ let EMPTY = [];
91
+ function emptyLine(title, ...parts) { EMPTY.push(el('div', { class: 'slim-line' }, el('b', {}, title), ': ', ...parts)); return null; }
92
+ /** The same one line, inside a card that has other content. */
93
+ const inlineEmpty = (title, ...parts) => el('p', { class: 'empty-line' }, el('b', {}, title), ': ', ...parts);
94
+
95
+ /** Values compared between loads: key -> text. A changed value is outlined. */
96
+ let PREV = null, CUR = {};
97
+ function track(key, value, node) { CUR[key] = txt(value); node.setAttribute('data-track', key); return node; }
98
+ function changedKeys(prev, cur) { return prev ? Object.keys(cur).filter((k) => k in prev && prev[k] !== cur[k]) : []; }
99
+
71
100
  function trendSvg(trend) {
72
101
  const w = Math.max(trend.length * 14, 140), h = 48, svg = document.createElementNS(SVG, 'svg');
73
102
  svg.setAttribute('class', 'trend'); svg.setAttribute('width', String(w)); svg.setAttribute('height', String(h + 2));
@@ -98,7 +127,54 @@ function scanBanner(ss) {
98
127
  }
99
128
 
100
129
  function repoName(r) {
101
- return el('span', { title: r.path }, r.name, r.current ? el('span', { class: 'tag' }, 'this repo') : null);
130
+ return el('span', { title: r.path }, labelOf(r), r.current ? el('span', { class: 'tag' }, 'this repo') : null);
131
+ }
132
+
133
+ const REVIEW_STATUS = { ok: 'clean', findings: 'findings', error: 'failed', skipped: 'skipped' };
134
+
135
+ /** The summary strip: latest review, spend, setup warnings. Same object as summary in --json. */
136
+ /** Long detail text is clamped to three lines by CSS; the full text stays in the title. */
137
+ const DETAIL_TITLE_MIN = 80;
138
+ const detail = (...parts) => {
139
+ const d = el('div', { class: 'd' }, ...parts);
140
+ const full = d.textContent;
141
+ if (full.length > DETAIL_TITLE_MIN) d.setAttribute('title', full);
142
+ return d;
143
+ };
144
+ /** One summary card: label, large value, one detail line. */
145
+ const scard = (label, value, det) => el('div', { class: 'scard' }, el('div', { class: 'k' }, label), value, det);
146
+
147
+ /** The summary cards: latest review, spend today, spend this month, setup warnings. Same object as summary in --json. */
148
+ function summaryPanel(sum) {
149
+ const s = el('section', { class: 'card wide' }, el('h2', {}, 'Summary'));
150
+ const box = el('div', { class: 'summary' });
151
+ const rv = sum && sum.review;
152
+ if (rv) {
153
+ const ran = rv.status !== 'skipped' && rv.status !== 'error';
154
+ const counts = ran ? rv.high + ' high · ' + rv.medium + ' medium · ' + rv.low + ' low' : txt(REVIEW_STATUS[rv.status] || rv.status);
155
+ const value = ran
156
+ ? el('div', { class: 'v' }, el('span', { class: rv.high ? 'sev-high' : '' }, String(rv.high)), el('span', { class: 'unit' }, 'high'),
157
+ el('span', { class: rv.medium ? 'sev-medium' : '' }, String(rv.medium)), el('span', { class: 'unit' }, 'med'),
158
+ el('span', { class: rv.low ? 'sev-low' : '' }, String(rv.low)), el('span', { class: 'unit' }, 'low'))
159
+ : el('div', { class: 'v' }, counts.charAt(0).toUpperCase() + counts.slice(1));
160
+ box.append(scard('Latest codewatch review', track('sum:review', rv.id + ' ' + counts, value),
161
+ detail(el('span', { title: rv.path }, txt(rv.repo)), ' · ', reportLink(rv.id, when(rv.at)), ' · ' + txt(rv.reviewer) + (rv.reason ? ' · ' + txt(rv.reason) : ''))));
162
+ } else {
163
+ box.append(scard('Latest codewatch review', el('div', { class: 'v' }, 'none'), detail('No pre-push review recorded yet.')));
164
+ }
165
+ const sp = sum && sum.spend;
166
+ const ok = sp && sp.status === 'ok';
167
+ const spendCard = (label, key, n, det) => scard(label,
168
+ ok ? track(key, n, el('div', { class: 'v' }, money(n, sp.currency))) : el('div', { class: 'v' }, '-'),
169
+ detail(ok ? det : txt((sp && sp.message) || (sp && sp.status) || 'no codeburn data')));
170
+ box.append(spendCard('Spend today', 'sum:today', sp && sp.today, 'codeburn, all projects'));
171
+ box.append(spendCard('Spend this month', 'sum:month', sp && sp.month, 'codeburn, this calendar month'));
172
+ const w = (sum && sum.warnings) || { count: 0, first: null };
173
+ box.append(scard('Setup warnings',
174
+ track('sum:warnings', w.count, el('div', { class: 'v' + (w.count ? ' s-warn' : ' s-ok') }, String(w.count))),
175
+ detail(w.first ? txt(w.first) : 'Nothing needs attention.')));
176
+ s.append(box);
177
+ return s;
102
178
  }
103
179
 
104
180
  const SCAN_LABEL = { ok: ['ok', 'ok'], 'not-scanned': ['warn', 'not scanned'], 'no-verify': ['warn', 'no-verify'], flagged: ['missing', 'secrets found'], none: ['skipped', '-'] };
@@ -170,68 +246,96 @@ function codewatchDetail(repo) {
170
246
  el('div', { class: 'stat' }, el('b', {}, cw.gitleaksBlocks.length), el('span', {}, 'gitleaks blocks')),
171
247
  el('div', { class: 'stat' }, el('b', {}, cw.skipped.length), el('span', {}, 'pushed without a scan'))));
172
248
 
173
- s.append(el('h3', {}, 'Commits'));
174
- s.append(commitsList(cw.commits));
249
+ if (!cw.commits || !cw.commits.rows.length) s.append(inlineEmpty('Commits', 'none recorded yet; commits appear after a pre-commit scan or a pre-push review that runs.'));
250
+ else s.append(el('h3', {}, 'Commits'), commitsList(cw.commits));
175
251
 
176
- s.append(el('h3', {}, 'Scans (last ' + cw.scans.length + ')'));
177
- s.append(scansList(cw.scans));
252
+ if (!cw.scans.length) s.append(inlineEmpty('Scans', 'no pre-push reviews yet.'));
253
+ else s.append(el('h3', {}, 'Scans (last ' + cw.scans.length + ')'), scansList(cw.scans));
178
254
 
179
- s.append(el('h3', {}, 'Trend (last ' + cw.trend.length + ' runs)'));
180
- s.append(trendSvg(cw.trend));
255
+ const skipped = cw.skippedRuns || 0;
256
+ if (!cw.trend.length) {
257
+ // By design: a skipped run (nothing new to review, or no gitleaks) has no findings, so it is not charted.
258
+ s.append(inlineEmpty('Trend', 'nothing to chart. ' + (skipped
259
+ ? 'All ' + skipped + ' run' + (skipped === 1 ? ' was' : 's were') + ' skipped (for example a push with no changes against upstream), and skipped runs have no findings to chart.'
260
+ : 'No run has finished yet.')));
261
+ } else {
262
+ s.append(el('h3', {}, 'Trend (last ' + cw.trend.length + ' runs)'), trendSvg(cw.trend));
263
+ if (skipped) s.append(el('p', { class: 'note' }, skipped + ' skipped run' + (skipped === 1 ? ' is' : 's are') + ' not charted: they have no findings.'));
264
+ }
181
265
 
182
- s.append(el('h3', {}, 'Gitleaks blocks'));
183
- if (!cw.gitleaksBlocks.length) s.append(el('p', { class: 'empty' }, 'No commits blocked.'));
184
- else s.append(table(['When', 'Branch', 'Result', 'Secrets'], cw.gitleaksBlocks.slice(0, 20).map((r) => [when(r.started_at), txt(r.branch), pill(r.status), r.status === 'findings' ? r.high : txt(r.reason)])));
266
+ if (!cw.gitleaksBlocks.length) s.append(inlineEmpty('Gitleaks blocks', 'no commits blocked.'));
267
+ else s.append(el('h3', {}, 'Gitleaks blocks'), table(['When', 'Branch', 'Result', 'Secrets'], cw.gitleaksBlocks.slice(0, 20).map((r) => [when(r.started_at), txt(r.branch), pill(r.status), r.status === 'findings' ? r.high : txt(r.reason)])));
185
268
 
186
- s.append(el('h3', {}, 'Commits pushed without a pre-commit scan'));
187
- if (!cw.skipped.length) s.append(el('p', { class: 'empty' }, 'None.'));
269
+ if (!cw.skipped.length) s.append(inlineEmpty('Commits pushed without a pre-commit scan', 'none.'));
188
270
  else {
271
+ s.append(el('h3', {}, 'Commits pushed without a pre-commit scan'));
189
272
  s.append(table(['Commit', 'Branch', 'Recorded'], cw.skipped.slice(0, 20).map((r) => [el('code', {}, short(r.commit)), txt(r.branch), when(r.started_at)])));
190
273
  s.append(el('p', { class: 'note' }, 'Usually git commit --no-verify; rebases and cherry-picks also show up here.'));
191
274
  }
192
275
  return s;
193
276
  }
194
277
 
278
+ /** "29 other repos: not set up", expanding to one line per repo. key keeps it open across refreshes. */
279
+ function otherRepos(key, list, what, line) {
280
+ if (!list.length) return null;
281
+ return el('details', { class: 'missing-group', 'data-key': key },
282
+ el('summary', {}, list.length + ' other repo' + (list.length === 1 ? '' : 's') + ': ' + what),
283
+ el('ul', { class: 'sec-findings' }, list.map((r) => el('li', {}, repoName(r), line(r)))));
284
+ }
285
+
195
286
  function codewatchPanel(data) {
196
- const s = el('section', { class: 'card wide' }, el('h2', {}, 'Codewatch'));
287
+ const rows = data.overview.codewatch;
197
288
  const banner = scanBanner(data.machine.secretScanning);
289
+ if (!rows.length && !banner) {
290
+ const sg = (data.suggestions || []).find((x) => x.id === 'codewatch-global');
291
+ if (sg) return emptyLine('Codewatch', "isn't enabled in any repo yet. Cover every repo at once: ", el('code', {}, sg.command), ', or run ', el('code', {}, 'prism-cli enable codewatch'), ' inside one repo.');
292
+ return emptyLine('Codewatch', "isn't enabled in any repo yet. Run ", el('code', {}, 'prism-cli enable codewatch'), ' inside a repo.');
293
+ }
294
+ const s = el('section', { class: 'card wide' }, el('h2', {}, 'Codewatch'));
198
295
  if (banner) s.append(banner);
199
- const rows = data.overview.codewatch;
200
296
  if (!rows.length) {
201
- const sg = (data.suggestions || []).find((x) => x.id === 'codewatch-global');
202
- if (sg) s.append(el('p', { class: 'empty' }, "Codewatch isn't enabled in any repo yet. Cover every repo at once: ", el('code', {}, sg.command), ', or run ', el('code', {}, 'prism-cli enable codewatch'), ' inside one repo.'));
203
- else s.append(el('p', { class: 'empty' }, "Codewatch isn't enabled in any repo yet. Run ", el('code', {}, 'prism-cli enable codewatch'), ' inside a repo.'));
297
+ s.append(el('p', { class: 'empty' }, "Codewatch isn't enabled in any repo yet. Run ", el('code', {}, 'prism-cli enable codewatch'), ' inside a repo.'));
204
298
  return s;
205
299
  }
206
- const count = (n, cls) => el('td', { class: 'count' + (n ? ' ' + cls : '') }, n);
207
- s.append(table(['Repo', 'High', 'Medium', 'Low', 'Last run', 'Reviewer', 'Unscanned', 'Gitleaks'], rows.map((r) => [
208
- repoName(r), count(r.high, 'sev-high'), count(r.medium, 'sev-medium'), count(r.low, 'sev-low'),
209
- r.lastRunAt ? (r.lastReviewId ? reportLink(r.lastReviewId, when(r.lastRunAt)) : when(r.lastRunAt)) : 'never', txt(r.reviewer || '-'), count(r.unscannedCommits, 'sev-medium'),
300
+ const cur = data.usage && data.usage.currency;
301
+ const count = (key, n, cls) => track(key, n, el('td', { class: 'count' + (n ? ' ' + cls : '') }, n));
302
+ s.append(table(['Repo', 'High', 'Medium', 'Low', 'Last run', 'Reviewer', 'Unscanned', 'Gitleaks', 'Spend (month)'], rows.map((r) => [
303
+ repoName(r), count('cw:h:' + r.path, r.high, 'sev-high'), count('cw:m:' + r.path, r.medium, 'sev-medium'), count('cw:l:' + r.path, r.low, 'sev-low'),
304
+ track('cw:last:' + r.path, r.lastRunAt, el('td', {}, r.lastRunAt ? (r.lastReviewId ? reportLink(r.lastReviewId, when(r.lastRunAt)) : when(r.lastRunAt)) : 'never')),
305
+ txt(r.reviewer || '-'), count('cw:u:' + r.path, r.unscannedCommits, 'sev-medium'),
210
306
  r.gitleaksWarning ? pill('warn', 'not scanned') : '-',
307
+ track('cw:spend:' + r.path, r.spendMonth, el('td', { class: 'num', title: 'codeburn projects at or under this repo' }, r.spendMonth === null || r.spendMonth === undefined ? '-' : money(r.spendMonth, cur))),
211
308
  ])));
212
309
  const byPath = Object.fromEntries(data.repos.map((r) => [r.path, r]));
213
310
  s.append(el('h3', {}, 'Per repo'));
214
311
  for (const row of rows) {
215
312
  const repo = byPath[row.path]; if (!repo) continue;
216
313
  const total = row.high + row.medium + row.low;
217
- const d = el('details', repo.current ? { class: 'repo', open: '' } : { class: 'repo' },
218
- el('summary', {}, el('b', {}, repo.name), ' ', el('span', { class: 'fix' }, repo.codewatch.hasResults ? total + ' finding' + (total === 1 ? '' : 's') + ' in the latest reviews' : 'no results yet'),
314
+ const d = el('details', repo.current ? { class: 'repo', open: '', 'data-key': 'cw:' + repo.path } : { class: 'repo', 'data-key': 'cw:' + repo.path },
315
+ el('summary', {}, el('b', {}, labelOf(repo)), ' ', el('span', { class: 'fix' }, repo.codewatch.hasResults ? total + ' finding' + (total === 1 ? '' : 's') + ' in the latest reviews' : 'no results yet'),
219
316
  repo.current ? el('span', { class: 'tag' }, 'this repo') : null),
220
317
  el('div', { class: 'repo-title' }, repo.path));
221
318
  d.append(codewatchDetail(repo));
222
319
  s.append(d);
223
320
  }
321
+ const shown = new Set(rows.map((r) => r.path));
322
+ const rest = data.overview.setup.filter((r) => !shown.has(r.path));
323
+ const other = otherRepos('cw:other', rest, 'no codewatch', (r) => r.fix ? el('div', { class: 'fix' }, 'Fix: ', el('code', {}, r.fix)) : el('div', { class: 'fix' }, txt(r.detail)));
324
+ if (other) s.append(other);
224
325
  return s;
225
326
  }
226
327
 
227
328
  function usagePanel(u) {
329
+ if (u.status === 'not-installed' || u.status === 'no-data') {
330
+ return emptyLine('Usage (codeburn)', txt(u.message), u.status === 'not-installed' ? '' : el('span', {}, ' Usage appears once a coding tool records sessions; full charts: ', el('code', {}, txt(u.fullView)), '.'));
331
+ }
228
332
  const s = el('section', { class: 'card' }, el('h2', {}, 'Usage (codeburn)'));
229
333
  if (u.status !== 'ok') {
230
- s.append(el('p', { class: 'empty' }, pill(u.status === 'no-data' ? 'warn' : 'missing', u.status), ' ', txt(u.message)));
334
+ s.append(el('p', { class: 'empty' }, pill('missing', u.status), ' ', txt(u.message)));
231
335
  } else {
232
336
  s.append(el('div', { class: 'stats' },
233
- el('div', { class: 'stat' }, el('b', {}, money(u.today.cost, u.currency)), el('span', {}, 'today, ' + u.today.calls + ' calls')),
234
- el('div', { class: 'stat' }, el('b', {}, money(u.month.cost, u.currency)), el('span', {}, 'this month, ' + u.month.calls + ' calls, ' + u.month.sessions + ' sessions'))));
337
+ el('div', { class: 'stat' }, track('use:today', u.today.cost, el('b', {}, money(u.today.cost, u.currency))), el('span', {}, 'today, ' + u.today.calls + ' calls')),
338
+ el('div', { class: 'stat' }, track('use:month', u.month.cost, el('b', {}, money(u.month.cost, u.currency))), el('span', {}, 'this month, ' + u.month.calls + ' calls, ' + u.month.sessions + ' sessions'))));
235
339
  s.append(el('h3', {}, 'Top models this month'));
236
340
  s.append(table(['Model', 'Cost', 'Calls'], u.topModels.map((m) => [m.name, num(money(m.cost, u.currency)), num(m.calls)])));
237
341
  s.append(el('h3', {}, 'Top projects this month'));
@@ -271,23 +375,25 @@ function setupPanel(data) {
271
375
  ];
272
376
  const sg = (data.suggestions || []).find((x) => x.id === 'codewatch-global');
273
377
  if (sg) s.append(suggestionCallout(sg));
274
- // Always shown: the current repo (installed or not) and every installed repo.
378
+ // Shown: the current repo, repos with activity (listed first), and repos with something set up.
275
379
  const shown = repos.filter((r) => !r.collapsed);
276
380
  const hidden = repos.filter((r) => r.collapsed);
277
381
  if (shown.length) s.append(table(['Repo', 'Codewatch', 'Security Agent'], shown.map(rowOf)));
278
382
  else if (!hidden.length) s.append(el('p', { class: 'empty' }, 'No git repositories found under ' + txt(data.scope.root_display) + '.'));
279
- if (hidden.length) {
280
- s.append(el('details', { class: 'missing-group' },
281
- el('summary', {}, hidden.length + ' repo' + (hidden.length === 1 ? '' : 's') + ' without codewatch'),
282
- table(['Repo', 'Codewatch', 'Security Agent'], hidden.map(rowOf))));
283
- }
383
+ const optedOut = hidden.filter((r) => r.status === 'opted-out').length;
384
+ const group = el('details', { class: 'missing-group', 'data-key': 'setup:other' },
385
+ el('summary', {}, hidden.length + ' other repo' + (hidden.length === 1 ? '' : 's') + ': not set up' + (optedOut ? ' (' + optedOut + ' opted out of codewatch)' : '')),
386
+ table(['Repo', 'Codewatch', 'Security Agent'], hidden.map(rowOf)));
387
+ if (hidden.length) s.append(group);
284
388
  return s;
285
389
  }
286
390
 
287
- /** Security Agent per repo, from the enable security marker (local evidence; the page makes no AWS calls). */
391
+ const SEC_LABEL = { ok: 'ok', 'in-use': 'in use', missing: 'missing' };
392
+
393
+ /** Security Agent per repo: the enable security marker, else saved results (local evidence; the page makes no AWS calls). */
288
394
  function securityCell(sec) {
289
395
  if (!sec) return el('span', { class: 'fix' }, '-');
290
- return el('div', {}, pill(sec.status === 'ok' ? 'ok' : 'missing', sec.status), ' ', sec.status === 'ok' ? txt(sec.detail) : '',
396
+ return el('div', {}, pill(sec.status === 'missing' ? 'missing' : 'ok', SEC_LABEL[sec.status] || sec.status), ' ', sec.status === 'missing' ? '' : txt(sec.detail).replace(/^in use /, ''),
291
397
  sec.fix ? el('div', { class: 'fix' }, 'Fix: ', el('code', {}, sec.fix)) : null);
292
398
  }
293
399
 
@@ -304,13 +410,13 @@ function securityPanel(sec) {
304
410
  const tms = (sec && sec.threat_models) || [];
305
411
  const pts = (sec && sec.pentests) || [];
306
412
  if (!rows.length && !tms.length && !pts.length) {
307
- s.append(el('p', { class: 'empty' }, 'No Security Agent code reviews, threat models or penetration tests yet. Run ', el('code', {}, (sec && sec.hint) || 'prism-cli security code-review'), ', ', el('code', {}, 'prism-cli security threat-model'), ' or ', el('code', {}, 'prism-cli security pentest'), ' in a repo set up with ', el('code', {}, 'prism-cli enable security'), '.'));
308
- return s;
413
+ return emptyLine('Security findings', 'No Security Agent code reviews, threat models or penetration tests yet. Run ', el('code', {}, (sec && sec.hint) || 'prism-cli security code-review'), ', ', el('code', {}, 'prism-cli security threat-model'), ' or ', el('code', {}, 'prism-cli security pentest'), ' in a repo set up with ', el('code', {}, 'prism-cli enable security'), '.');
309
414
  }
415
+ if (!rows.length) s.append(inlineEmpty('Code reviews', 'none yet. Run ', el('code', {}, (sec && sec.hint) || 'prism-cli security code-review'), '.'));
310
416
  for (const r of rows) {
311
417
  const counts = r.counts || {};
312
418
  const b = el('div', { class: 'sec-repo' });
313
- b.append(el('div', { class: 'sec-head' }, el('b', {}, txt(r.name)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
419
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(r)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
314
420
  b.append(el('div', { class: 'fix' },
315
421
  (r.pending ? 'started ' + when(r.started_at) : when(r.finished_at)) + ' · ' + (r.scope === 'diff' ? 'branch diff' : r.scope === 'full' ? 'whole repo' : 'fetched') + ' · job ' + txt(r.job_id).slice(0, 12)));
316
422
  const pills = el('div', { class: 'sec-pills' });
@@ -348,7 +454,7 @@ function securityPanel(sec) {
348
454
  if (tms.length) {
349
455
  s.append(el('h3', {}, 'Threat models'));
350
456
  for (const t of tms) s.append(threatModelBlock(t, (sec && sec.threats_hint) || 'prism-cli security threats'));
351
- }
457
+ } else s.append(inlineEmpty('Threat models', 'none yet. Run ', el('code', {}, 'prism-cli security threat-model'), '.'));
352
458
  return s;
353
459
  }
354
460
 
@@ -359,7 +465,7 @@ const strideText = (list) => (list || []).map((x) => txt(x).toLowerCase().replac
359
465
  /** One cached threat model per repo, in the code review blocks' compact style. */
360
466
  function threatModelBlock(t, hint) {
361
467
  const b = el('div', { class: 'sec-repo' });
362
- b.append(el('div', { class: 'sec-head' }, el('b', {}, txt(t.name)), t.current ? el('span', { class: 'tag' }, 'this repo') : null));
468
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(t)), t.current ? el('span', { class: 'tag' }, 'this repo') : null));
363
469
  const docs = (t.scope_docs || []).length;
364
470
  b.append(el('div', { class: 'fix' },
365
471
  (t.pending ? 'started ' + when(t.started_at) : when(t.finished_at)) + ' · commit ' + txt(t.commit).slice(0, 12)
@@ -399,12 +505,12 @@ function threatModelBlock(t, hint) {
399
505
  /** Penetration-test rows, in the same Security card below the code-review rows. */
400
506
  function securityPentestRows(s, sec) {
401
507
  const rows = (sec && sec.pentests) || [];
402
- if (!rows.length) return;
508
+ if (!rows.length) { s.append(inlineEmpty('Penetration tests', 'none yet. Run ', el('code', {}, 'prism-cli security pentest'), '.')); return; }
403
509
  s.append(el('h3', {}, 'Penetration tests'));
404
510
  for (const r of rows) {
405
511
  const counts = r.counts || {};
406
512
  const b = el('div', { class: 'sec-repo' });
407
- b.append(el('div', { class: 'sec-head' }, el('b', {}, txt(r.name)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
513
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(r)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
408
514
  b.append(el('div', { class: 'fix' },
409
515
  (r.pending ? 'started ' + when(r.started_at) : when(r.finished_at)) + ' · ' + txt(r.target_host) + ' · job ' + txt(r.job_id).slice(0, 12)));
410
516
  const pills = el('div', { class: 'sec-pills' });
@@ -444,12 +550,9 @@ const AUDIT_SEVERITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];
444
550
 
445
551
  /** The latest aws check per account, in the Security panel's compact style. Local results only. */
446
552
  function awsPanel(aws) {
447
- const s = el('section', { class: 'card' }, el('h2', {}, 'AWS account'));
448
553
  const rows = (aws && aws.accounts) || [];
449
- if (!rows.length) {
450
- s.append(el('p', { class: 'empty' }, 'No AWS account checks yet. Run ', el('code', {}, (aws && aws.hint) || 'prism-cli aws check'), ' (read-only).'));
451
- return s;
452
- }
554
+ if (!rows.length) return emptyLine('AWS account', 'no checks yet. Run ', el('code', {}, (aws && aws.hint) || 'prism-cli aws check'), ' (read-only).');
555
+ const s = el('section', { class: 'card' }, el('h2', {}, 'AWS account'));
453
556
  for (const a of rows) {
454
557
  const b = el('div', { class: 'sec-repo' });
455
558
  b.append(el('div', { class: 'sec-head' }, el('b', {}, txt(a.account)), a.alias ? el('span', { class: 'tag' }, txt(a.alias)) : null));
@@ -473,22 +576,56 @@ function awsPanel(aws) {
473
576
  return s;
474
577
  }
475
578
 
579
+ /** Auto-refresh period while the tab is visible; paused while hidden. */
580
+ const AUTO_REFRESH_MS = 60000;
581
+ let lastLoad = 0, loading = false;
582
+
476
583
  async function load() {
584
+ if (loading) return;
585
+ loading = true;
586
+ try { await render(); } finally { loading = false; lastLoad = Date.now(); }
587
+ }
588
+
589
+ async function render() {
477
590
  const main = document.getElementById('main'), meta = document.getElementById('meta');
591
+ const before = meta.textContent;
478
592
  meta.textContent = 'loading...';
479
593
  let data;
480
594
  try {
481
595
  const res = await fetch('/api/data', { credentials: 'same-origin', cache: 'no-store' });
482
596
  if (!res.ok) throw new Error('HTTP ' + res.status);
483
597
  data = await res.json();
484
- } catch (e) { meta.textContent = 'failed to load: ' + e.message; return; }
598
+ } catch (e) {
599
+ // Keep the last good view; say the refresh failed.
600
+ meta.textContent = 'failed to load: ' + e.message + (before && before !== 'loading...' ? ' · showing ' + before : '');
601
+ return;
602
+ }
603
+ // Open/closed state of keyed <details> survives the redraw.
604
+ const openState = new Map([...main.querySelectorAll('details[data-key]')].map((d) => [d.getAttribute('data-key'), d.open]));
605
+ LABELS = new Map((data.repos || []).map((r) => [r.path, r.label || r.name]));
606
+ EMPTY = []; CUR = {};
485
607
  main.replaceChildren();
486
608
  if (data.scope.error) main.append(el('section', { class: 'card wide' }, pill('error'), ' ', data.scope.error));
487
- main.append(codewatchPanel(data), usagePanel(data.usage), setupPanel(data), securityPanel(data.security), awsPanel(data.aws));
609
+ const panels = [summaryPanel(data.summary), codewatchPanel(data), usagePanel(data.usage), setupPanel(data), securityPanel(data.security), awsPanel(data.aws)];
610
+ main.append(...panels.filter(Boolean));
611
+ if (EMPTY.length) main.append(el('section', { class: 'card wide' }, el('h2', {}, 'Nothing recorded yet'), ...EMPTY));
612
+ for (const d of main.querySelectorAll('details[data-key]')) {
613
+ const k = d.getAttribute('data-key');
614
+ if (openState.has(k)) d.open = openState.get(k);
615
+ }
616
+ const changed = new Set(changedKeys(PREV, CUR));
617
+ for (const n of main.querySelectorAll('[data-track]')) {
618
+ const k = n.getAttribute('data-track');
619
+ if (changed.has(k)) { n.classList.add('changed'); n.setAttribute('title', 'changed since the previous load (was ' + PREV[k] + ')'); }
620
+ }
621
+ PREV = CUR;
488
622
  const c = data.overview.counts;
489
- meta.textContent = c.repos + ' repos under ' + txt(data.scope.root_display) + ' · ' + c.codewatch + ' with codewatch' + (data.scope.truncated ? ' (scan capped)' : '') + ' · updated ' + when(data.generated_at);
623
+ meta.textContent = c.repos + ' repos under ' + txt(data.scope.root_display) + ' · ' + c.codewatch + ' with codewatch' + (data.scope.truncated ? ' (scan capped)' : '')
624
+ + ' · updated ' + when(data.generated_at) + (changed.size ? ' · ' + changed.size + ' value' + (changed.size === 1 ? '' : 's') + ' changed (outlined)' : '') + ' · refreshes every minute while visible';
490
625
  }
491
626
  document.getElementById('refresh').addEventListener('click', load);
627
+ setInterval(() => { if (document.visibilityState === 'visible' && Date.now() - lastLoad >= AUTO_REFRESH_MS - 1000) load(); }, AUTO_REFRESH_MS / 4);
628
+ document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible' && Date.now() - lastLoad >= AUTO_REFRESH_MS) load(); });
492
629
  load();
493
630
  `;
494
631
  export function renderPage(nonce) {
@@ -1 +1 @@
1
- {"version":3,"file":"page.js","sourceRoot":"","sources":["../../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCb,CAAC;AAEF,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2bd,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,wFAAwF;IACxF,OAAO;;8CAEqC,KAAK,KAAK,KAAK;;;iBAG5C,KAAK,KAAK,MAAM,yBAAyB,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDb,CAAC;AAEF,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsjBd,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,wFAAwF;IACxF,OAAO;;8CAEqC,KAAK,KAAK,KAAK;;;iBAG5C,KAAK,KAAK,MAAM,yBAAyB,CAAC;AAC3D,CAAC"}
@@ -6,15 +6,22 @@
6
6
  import { type CodewatchPanel, type SecretScanning } from './codewatch.js';
7
7
  import { type DiscoverOptions } from './repos.js';
8
8
  import { type CheckItem, type CodewatchSetup, type MachineDeps, type SecuritySetup } from './setup.js';
9
+ import { type DashboardSummary } from './summary.js';
9
10
  import { type GlobalStatus } from '../codewatch/globalHooks.js';
10
11
  export declare function tildeHome(p: string, home: string): string;
11
- import { type CodeburnExport, type UsagePanel } from './usage.js';
12
+ import { type CodeburnExport, type RepoSpend, type UsagePanel } from './usage.js';
12
13
  import { type SecurityPanel } from './security.js';
13
14
  import { type AwsPanel } from './aws.js';
14
15
  export declare const PAYLOAD_SCHEMA = "prism.dashboard.v2";
15
16
  export interface RepoPayload {
16
17
  name: string;
18
+ /** The name, or a short parent path when another repo has the same name. */
19
+ label: string;
17
20
  path: string;
21
+ /** codeburn spend this month in projects at or under this repo; null when codeburn has no data. */
22
+ spend: RepoSpend | null;
23
+ /** Codewatch results, Security Agent results, or codeburn spend this month. */
24
+ active: boolean;
18
25
  current: boolean;
19
26
  hasPrism: boolean;
20
27
  codewatch: CodewatchPanel;
@@ -25,7 +32,10 @@ export interface RepoPayload {
25
32
  /** One row of the setup table. */
26
33
  export interface SetupRow {
27
34
  name: string;
35
+ label: string;
28
36
  path: string;
37
+ /** See RepoPayload.active; active repos are listed first. */
38
+ active: boolean;
29
39
  current: boolean;
30
40
  status: CodewatchSetup['status'];
31
41
  reviewer: string | null;
@@ -35,13 +45,21 @@ export interface SetupRow {
35
45
  global: boolean;
36
46
  /** Security Agent row for this repo. */
37
47
  security: SecuritySetup;
38
- /** Shown inside the collapsed "repos without codewatch" group: missing or opted out, and not the current repo. */
48
+ /**
49
+ * Shown inside the one collapsed "other repos: not set up" line: not the
50
+ * current repo, no activity, codewatch missing or opted out, and no
51
+ * Security Agent marker.
52
+ */
39
53
  collapsed: boolean;
40
54
  }
41
55
  /** One row of the codewatch summary table. */
42
56
  export interface CodewatchRow {
43
57
  name: string;
58
+ label: string;
44
59
  path: string;
60
+ active: boolean;
61
+ /** codeburn spend this month for this repo, or null when there is none to match. */
62
+ spendMonth: number | null;
45
63
  current: boolean;
46
64
  high: number;
47
65
  medium: number;
@@ -102,6 +120,8 @@ export interface DashboardPayload {
102
120
  security: SecurityPanel;
103
121
  /** The latest cached aws check result per AWS account (repos and ~). */
104
122
  aws: AwsPanel;
123
+ /** The one-glance summary shown at the top of the page (buildSummary). */
124
+ summary: DashboardSummary;
105
125
  }
106
126
  export interface PayloadOptions extends DiscoverOptions {
107
127
  trendRuns?: number;
@@ -109,9 +129,9 @@ export interface PayloadOptions extends DiscoverOptions {
109
129
  machineDeps?: MachineDeps;
110
130
  pathEnv?: string;
111
131
  }
112
- /** The current repo first, then installed, bypassed (warn), missing and opted-out repos, each by name. */
132
+ /** The current repo first, then repos with activity, then installed, bypassed (warn), missing and opted-out repos, each by name. */
113
133
  export declare function sortSetupRows(rows: SetupRow[]): SetupRow[];
114
- /** Most high findings first, then medium, then low, then by name. */
134
+ /** Repos with activity first, then most high findings, then medium, then low, then by name. */
115
135
  export declare function sortCodewatchRows(rows: CodewatchRow[]): CodewatchRow[];
116
136
  export declare function buildPayload(opts: PayloadOptions): Promise<DashboardPayload>;
117
137
  //# sourceMappingURL=payload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/dashboard/payload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/dashboard/payload.ts"],"names":[],"mappings":"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"}