@prism-d1/cli 1.4.3 → 1.6.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 (58) 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/aws/check.js +1 -1
  4. package/dist/src/aws/team.d.ts +39 -0
  5. package/dist/src/aws/team.d.ts.map +1 -0
  6. package/dist/src/aws/team.js +172 -0
  7. package/dist/src/aws/team.js.map +1 -0
  8. package/dist/src/commands/enable/codeburn.d.ts +27 -3
  9. package/dist/src/commands/enable/codeburn.d.ts.map +1 -1
  10. package/dist/src/commands/enable/codeburn.js +317 -40
  11. package/dist/src/commands/enable/codeburn.js.map +1 -1
  12. package/dist/src/commands/team/add-developer.d.ts +17 -0
  13. package/dist/src/commands/team/add-developer.d.ts.map +1 -0
  14. package/dist/src/commands/team/add-developer.js +22 -0
  15. package/dist/src/commands/team/add-developer.js.map +1 -0
  16. package/dist/src/commands/team/info.d.ts +16 -0
  17. package/dist/src/commands/team/info.d.ts.map +1 -0
  18. package/dist/src/commands/team/info.js +30 -0
  19. package/dist/src/commands/team/info.js.map +1 -0
  20. package/dist/src/dashboard/codewatch.d.ts +3 -1
  21. package/dist/src/dashboard/codewatch.d.ts.map +1 -1
  22. package/dist/src/dashboard/codewatch.js +1 -0
  23. package/dist/src/dashboard/codewatch.js.map +1 -1
  24. package/dist/src/dashboard/page.d.ts.map +1 -1
  25. package/dist/src/dashboard/page.js +203 -55
  26. package/dist/src/dashboard/page.js.map +1 -1
  27. package/dist/src/dashboard/payload.d.ts +31 -4
  28. package/dist/src/dashboard/payload.d.ts.map +1 -1
  29. package/dist/src/dashboard/payload.js +43 -24
  30. package/dist/src/dashboard/payload.js.map +1 -1
  31. package/dist/src/dashboard/repos.d.ts +6 -0
  32. package/dist/src/dashboard/repos.d.ts.map +1 -1
  33. package/dist/src/dashboard/repos.js +24 -0
  34. package/dist/src/dashboard/repos.js.map +1 -1
  35. package/dist/src/dashboard/setup.d.ts +21 -5
  36. package/dist/src/dashboard/setup.d.ts.map +1 -1
  37. package/dist/src/dashboard/setup.js +47 -17
  38. package/dist/src/dashboard/setup.js.map +1 -1
  39. package/dist/src/dashboard/summary.d.ts +45 -0
  40. package/dist/src/dashboard/summary.d.ts.map +1 -0
  41. package/dist/src/dashboard/summary.js +20 -0
  42. package/dist/src/dashboard/summary.js.map +1 -0
  43. package/dist/src/dashboard/usage.d.ts +16 -0
  44. package/dist/src/dashboard/usage.d.ts.map +1 -1
  45. package/dist/src/dashboard/usage.js +31 -3
  46. package/dist/src/dashboard/usage.js.map +1 -1
  47. package/dist/src/index.d.ts.map +1 -1
  48. package/dist/src/index.js +8 -0
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/utils/otel-sync-schedule.d.ts +18 -1
  51. package/dist/src/utils/otel-sync-schedule.d.ts.map +1 -1
  52. package/dist/src/utils/otel-sync-schedule.js +44 -3
  53. package/dist/src/utils/otel-sync-schedule.js.map +1 -1
  54. package/dist/src/utils/sync-mode.d.ts +43 -0
  55. package/dist/src/utils/sync-mode.d.ts.map +1 -0
  56. package/dist/src/utils/sync-mode.js +84 -0
  57. package/dist/src/utils/sync-mode.js.map +1 -0
  58. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-developer.js","sourceRoot":"","sources":["../../../../src/commands/team/add-developer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,YAAY,EAA4B,MAAM,mBAAmB,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAkB,MAAM,WAAW,CAAC;AAErD,eAAe;IACb,WAAW,EAAE,kIAAkI,eAAe,GAAG;IACjK,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gDAAgD,EAAE;IAC3G,OAAO,EAAE;QACP,GAAG,kBAAkB;QACrB,EAAE,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,sEAAsE,EAAE;QACrH,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,sDAAsD,EAAE;QACvF,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,8DAA8D,EAAE;KACpG;IACD,KAAK,CAAC,MAAM,CAAC,IAAyB,EAAE,QAAmB,EAAE;QAC3D,MAAM,UAAU,CAAC,KAAK,IAAI,EAAE;YAC1B,MAAM,CAAC,GAAG,kBAAkB,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3H,OAAO,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;CACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type TeamDeps, type TeamOptions } from '../../aws/team.js';
2
+ import { type RunHooks } from '../enable/security.js';
3
+ export declare function teamDeps(): TeamDeps;
4
+ export interface TeamHooks extends RunHooks {
5
+ teamDeps?: () => TeamDeps;
6
+ }
7
+ declare const _default: {
8
+ description: string;
9
+ options: {
10
+ flags: string;
11
+ description: string;
12
+ }[];
13
+ action(opts: TeamOptions, hooks?: TeamHooks): Promise<void>;
14
+ };
15
+ export default _default;
16
+ //# sourceMappingURL=info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../../../src/commands/team/info.ts"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhG,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAElE,wBAAgB,QAAQ,IAAI,QAAQ,CAYnC;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ;IAAG,QAAQ,CAAC,EAAE,MAAM,QAAQ,CAAA;CAAE;;;;;;;iBAKpD,WAAW,UAAS,SAAS;;AAHlD,wBASE"}
@@ -0,0 +1,30 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { JSON_OPTION } from '../../utils/audit.js';
3
+ import { configuredRegion, connectionDefaults } from '../../aws/connection.js';
4
+ import { INFO_PERMISSIONS, teamInfo } from '../../aws/team.js';
5
+ import { AWS_TARGET_OPTIONS } from '../aws/check.js';
6
+ import { runGuarded } from '../enable/security.js';
7
+ export function teamDeps() {
8
+ return {
9
+ env: process.env,
10
+ configRegion: configuredRegion,
11
+ isTTY: !!(process.stdin.isTTY && process.stdout.isTTY),
12
+ ask: (q) => new Promise((done) => {
13
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
14
+ rl.question(q, (a) => { rl.close(); done(a.trim()); });
15
+ }),
16
+ log: (line = '') => console.log(line),
17
+ err: (line = '') => console.error(line),
18
+ };
19
+ }
20
+ export default {
21
+ description: `Read-only: print the PRISM stack's OtelCollectorUrl and OtelUserPoolId, and the line to send developers (needs ${INFO_PERMISSIONS})`,
22
+ options: [...AWS_TARGET_OPTIONS, JSON_OPTION],
23
+ async action(opts, hooks = {}) {
24
+ await runGuarded(async () => {
25
+ const o = connectionDefaults({ ...opts, profile: opts.profile || undefined, region: opts.region || undefined }, hooks.env);
26
+ return teamInfo(o, (hooks.teamDeps ?? teamDeps)());
27
+ }, hooks);
28
+ },
29
+ };
30
+ //# sourceMappingURL=info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.js","sourceRoot":"","sources":["../../../../src/commands/team/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAmC,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAiB,MAAM,uBAAuB,CAAC;AAElE,MAAM,UAAU,QAAQ;IACtB,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,YAAY,EAAE,gBAAgB;QAC9B,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QACtD,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAC/B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7E,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC;QACF,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QACrC,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;KACxC,CAAC;AACJ,CAAC;AAID,eAAe;IACb,WAAW,EAAE,kHAAkH,gBAAgB,GAAG;IAClJ,OAAO,EAAE,CAAC,GAAG,kBAAkB,EAAE,WAAW,CAAC;IAC7C,KAAK,CAAC,MAAM,CAAC,IAAiB,EAAE,QAAmB,EAAE;QACnD,MAAM,UAAU,CAAC,KAAK,IAAI,EAAE;YAC1B,MAAM,CAAC,GAAG,kBAAkB,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3H,OAAO,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;CACF,CAAC"}
@@ -73,8 +73,10 @@ export interface CodewatchPanel {
73
73
  gitleaksBlocks: RunSummary[];
74
74
  /** Commits pushed without a pre-commit scan (`--no-verify`, rebase, ...). */
75
75
  skipped: RunSummary[];
76
- /** Last N runs, oldest first. */
76
+ /** Last N runs that were not skipped, oldest first. A skipped run (nothing to review, no gitleaks) has no findings to chart. */
77
77
  trend: RunSummary[];
78
+ /** Runs left out of the trend because they were skipped. */
79
+ skippedRuns: number;
78
80
  secretScanning: SecretScanning;
79
81
  /** Results mapped onto commits, newest first. */
80
82
  commits: CommitsPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"codewatch.d.ts","sourceRoot":"","sources":["../../../src/dashboard/codewatch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codewatch.d.ts","sourceRoot":"","sources":["../../../src/dashboard/codewatch.ts"],"names":[],"mappings":"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"}
@@ -113,6 +113,7 @@ export function collectCodewatch(repoRoot, trendRuns = DEFAULT_TREND_RUNS, gitle
113
113
  // --no-verify and other unhooked commits; gitleaks-missing ones are counted in secretScanning.
114
114
  skipped: all.filter((r) => r.kind === 'secrets' && r.status === 'skipped' && !isMissingSkip(r)).map(summarize).reverse(),
115
115
  trend: all.filter((r) => r.status !== 'skipped').slice(-trendRuns).map(summarize),
116
+ skippedRuns: all.filter((r) => r.status === 'skipped').length,
116
117
  secretScanning: secretScanning(all, gitleaksAvailable),
117
118
  commits: all.length ? collectCommits(repoRoot, all, { idOf: (r) => idOf.get(r) ?? null }) : { rows: [], total: 0, truncated: false, timing: { logMs: 0, blameMs: 0, blameCalls: 0 } },
118
119
  };
@@ -1 +1 @@
1
- {"version":3,"file":"codewatch.js","sourceRoot":"","sources":["../../../src/dashboard/codewatch.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAqD,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACzJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,cAAc,EAAqB,MAAM,cAAc,CAAC;AAEjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,kFAAkF;AAClF,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC;AA6EhC,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAe5B,MAAM,aAAa,GAAG,CAAC,CAAc,EAAE,EAAE,CACvC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,uBAAuB,CAAC;AAEpH,6DAA6D;AAC7D,MAAM,WAAW,GAAG,CAAC,CAAc,EAAE,EAAE,CACrC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAErK,kEAAkE;AAClE,SAAS,GAAG,CAAC,GAAW;IACtB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,UAAU,CAAC,iBAA0B,EAAE,KAAa,EAAE,KAAoB;IACjF,IAAI,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,KAAK;QACV,CAAC,CAAC,sDAAsD,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,CAAC,KAAM,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,eAAe;QACjK,CAAC,CAAC,uFAAuF,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAqB,EAAE,iBAA0B,EAAE,IAAI,GAAG,mBAAmB,EAAE;IAC5G,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;IACjB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;AACvK,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAyB,EAAE,iBAA0B,EAAE,IAAI,GAAG,mBAAmB,EAAE;IACvH,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACrG,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,iBAAiB,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;AACvJ,CAAC;AAED,SAAS,SAAS,CAAC,CAAc;IAC/B,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC5C,OAAO;QACL,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI;QAC5G,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;KACzH,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,CAAc;IAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,CAAc,EAAE,CAAc,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAE5G,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,SAAS,GAAG,kBAAkB,EAAE,iBAAiB,GAAG,IAAI;IACzG,8FAA8F;IAC9F,MAAM,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,uBAAuB,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACpO,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC/C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,YAAY,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC5E,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAAC,CAAC;QAC/G,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;YAAE,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAClF,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACxI,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAW,CAAC;IACtD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAEhI,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAClD,MAAM,UAAU,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;IAAC,CAAC;IAEjF,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;QAC1B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;QAC7D,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACtE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;QAC9D,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACtD,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAC5C,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAW,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACzN,CAAC,CAAC;QACF,SAAS,EAAE,GAAG,CAAC,MAAM;QACrB,cAAc,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACrF,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;QACzC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,GAAG,YAAY;QACjD,UAAU;QACV,UAAU;QACV,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;QAC5D,+FAA+F;QAC/F,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;QACxH,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;QACjF,cAAc,EAAE,cAAc,CAAC,GAAG,EAAE,iBAAiB,CAAC;QACtD,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE;KACtL,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"codewatch.js","sourceRoot":"","sources":["../../../src/dashboard/codewatch.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyeH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6nBH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
@@ -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; }
@@ -28,7 +29,7 @@ table { width:100%; border-collapse:collapse; font-size:13px; } th, td { text-al
28
29
  th { white-space:nowrap; } td.msg { min-width:280px; } td.count { font-variant-numeric:tabular-nums; font-weight:600; } td code { white-space:normal; overflow-wrap:anywhere; }
29
30
  th { color:var(--muted); font-weight:600; } td.num { text-align:right; font-variant-numeric: tabular-nums; }
30
31
  .pill { white-space:nowrap; display:inline-block; padding:0 7px; border-radius:10px; font-size:12px; font-weight:600; border:1px solid currentColor; }
31
- .s-ok { color:var(--ok); } .s-warn, .s-findings, .s-skipped { color:var(--warn); } .s-missing, .s-error { color:var(--bad); }
32
+ .s-ok { color:var(--ok); } .s-info { color:var(--accent); } .s-warn, .s-findings, .s-skipped { color:var(--warn); } .s-missing, .s-error { color:var(--bad); }
32
33
  .sev-high { color:var(--high); } .sev-medium { color:var(--medium); } .sev-low { color:var(--low); }
33
34
  .stats { display:flex; gap:18px; flex-wrap:wrap; margin:6px 0; } .stat b { display:block; font-size:20px; } .stat span { color:var(--muted); font-size:12px; }
34
35
  .fix { color:var(--muted); font-size:12px; overflow-wrap:anywhere; }
@@ -41,11 +42,25 @@ details.repo { border-top:1px solid var(--line); padding:8px 0; } details.repo >
41
42
  .chead { color:var(--muted); font-weight:600; } summary.crow { cursor:pointer; list-style:none; } summary.crow::-webkit-details-marker { display:none; } details.cdetails[open] > summary.crow { background:var(--bg); } details.cdetails > .tablewrap { padding:4px 0 8px 18px; }
42
43
  @media (max-width: 760px) { .crow { grid-template-columns: 1fr 1fr; } .chead { display:none; } }
43
44
  a.rlink { color:var(--accent); } a.rlink:hover { text-decoration:underline; }
45
+ .help { font-size:12px; margin-top:4px; overflow-wrap:anywhere; } .help-links { margin-top:2px; } .help-steps { margin:4px 0; padding-left:18px; } .help code { white-space:normal; overflow-wrap:anywhere; }
44
46
  .banner { border:1px solid var(--warn); border-left-width:4px; border-radius:6px; padding:10px 12px; margin:4px 0 12px; background:color-mix(in srgb, var(--warn) 10%, var(--card)); }
45
47
  .banner strong { color:var(--text); display:block; margin-bottom:4px; } .banner .fix code { white-space:normal; overflow-wrap:anywhere; } .note { color:var(--muted); font-size:12px; margin-top:10px; }
46
48
  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
49
  svg.trend line { stroke:var(--line); }
48
50
  .repo-title { font-size:12px; color:var(--muted); margin-bottom:6px; word-break:break-all; }
51
+ .summary { display:grid; gap:14px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
52
+ @media (min-width: 641px) and (max-width: 1040px) { .summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
53
+ @media (max-width: 640px) { .summary { grid-template-columns: 1fr; gap:10px; } }
54
+ .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); }
55
+ .scard .k { color:var(--muted); font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.04em; }
56
+ .scard .v { font-size:28px; font-weight:700; line-height:1.15; font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
57
+ .scard .v .unit { font-size:13px; font-weight:600; color:var(--muted); margin:0 10px 0 4px; }
58
+ .scard .d { color:var(--muted); font-size:13px; overflow-wrap:anywhere; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
59
+ .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; }
60
+ .empty-line { color:var(--muted); margin:10px 0 4px; font-size:13px; overflow-wrap:anywhere; } .empty-line b { color:var(--text); }
61
+ .changed { outline:2px solid var(--accent); outline-offset:1px; border-radius:4px; }
62
+ @keyframes prism-flash { from { background-color:color-mix(in srgb, var(--accent) 35%, transparent); } to { background-color:transparent; } }
63
+ @media (prefers-reduced-motion: no-preference) { .changed { animation:prism-flash 2.5s ease-out 1; } }
49
64
  `;
50
65
  const SCRIPT = `
51
66
  'use strict';
@@ -68,6 +83,21 @@ function table(head, rows) {
68
83
  }
69
84
  const num = (v) => el('td', { class: 'num' }, txt(v));
70
85
 
86
+ /** Repo labels (a parent path when two repos share a name), set on each load from data.repos. */
87
+ let LABELS = new Map();
88
+ const labelOf = (r) => LABELS.get(r.path) || txt(r.name);
89
+
90
+ /** Sections with no data, each collected as one line into the "Nothing recorded yet" card. */
91
+ let EMPTY = [];
92
+ function emptyLine(title, ...parts) { EMPTY.push(el('div', { class: 'slim-line' }, el('b', {}, title), ': ', ...parts)); return null; }
93
+ /** The same one line, inside a card that has other content. */
94
+ const inlineEmpty = (title, ...parts) => el('p', { class: 'empty-line' }, el('b', {}, title), ': ', ...parts);
95
+
96
+ /** Values compared between loads: key -> text. A changed value is outlined. */
97
+ let PREV = null, CUR = {};
98
+ function track(key, value, node) { CUR[key] = txt(value); node.setAttribute('data-track', key); return node; }
99
+ function changedKeys(prev, cur) { return prev ? Object.keys(cur).filter((k) => k in prev && prev[k] !== cur[k]) : []; }
100
+
71
101
  function trendSvg(trend) {
72
102
  const w = Math.max(trend.length * 14, 140), h = 48, svg = document.createElementNS(SVG, 'svg');
73
103
  svg.setAttribute('class', 'trend'); svg.setAttribute('width', String(w)); svg.setAttribute('height', String(h + 2));
@@ -98,7 +128,54 @@ function scanBanner(ss) {
98
128
  }
99
129
 
100
130
  function repoName(r) {
101
- return el('span', { title: r.path }, r.name, r.current ? el('span', { class: 'tag' }, 'this repo') : null);
131
+ return el('span', { title: r.path }, labelOf(r), r.current ? el('span', { class: 'tag' }, 'this repo') : null);
132
+ }
133
+
134
+ const REVIEW_STATUS = { ok: 'clean', findings: 'findings', error: 'failed', skipped: 'skipped' };
135
+
136
+ /** The summary strip: latest review, spend, setup warnings. Same object as summary in --json. */
137
+ /** Long detail text is clamped to three lines by CSS; the full text stays in the title. */
138
+ const DETAIL_TITLE_MIN = 80;
139
+ const detail = (...parts) => {
140
+ const d = el('div', { class: 'd' }, ...parts);
141
+ const full = d.textContent;
142
+ if (full.length > DETAIL_TITLE_MIN) d.setAttribute('title', full);
143
+ return d;
144
+ };
145
+ /** One summary card: label, large value, one detail line. */
146
+ const scard = (label, value, det) => el('div', { class: 'scard' }, el('div', { class: 'k' }, label), value, det);
147
+
148
+ /** The summary cards: latest review, spend today, spend this month, setup warnings. Same object as summary in --json. */
149
+ function summaryPanel(sum) {
150
+ const s = el('section', { class: 'card wide' }, el('h2', {}, 'Summary'));
151
+ const box = el('div', { class: 'summary' });
152
+ const rv = sum && sum.review;
153
+ if (rv) {
154
+ const ran = rv.status !== 'skipped' && rv.status !== 'error';
155
+ const counts = ran ? rv.high + ' high · ' + rv.medium + ' medium · ' + rv.low + ' low' : txt(REVIEW_STATUS[rv.status] || rv.status);
156
+ const value = ran
157
+ ? el('div', { class: 'v' }, el('span', { class: rv.high ? 'sev-high' : '' }, String(rv.high)), el('span', { class: 'unit' }, 'high'),
158
+ el('span', { class: rv.medium ? 'sev-medium' : '' }, String(rv.medium)), el('span', { class: 'unit' }, 'med'),
159
+ el('span', { class: rv.low ? 'sev-low' : '' }, String(rv.low)), el('span', { class: 'unit' }, 'low'))
160
+ : el('div', { class: 'v' }, counts.charAt(0).toUpperCase() + counts.slice(1));
161
+ box.append(scard('Latest codewatch review', track('sum:review', rv.id + ' ' + counts, value),
162
+ detail(el('span', { title: rv.path }, txt(rv.repo)), ' · ', reportLink(rv.id, when(rv.at)), ' · ' + txt(rv.reviewer) + (rv.reason ? ' · ' + txt(rv.reason) : ''))));
163
+ } else {
164
+ box.append(scard('Latest codewatch review', el('div', { class: 'v' }, 'none'), detail('No pre-push review recorded yet.')));
165
+ }
166
+ const sp = sum && sum.spend;
167
+ const ok = sp && sp.status === 'ok';
168
+ const spendCard = (label, key, n, det) => scard(label,
169
+ ok ? track(key, n, el('div', { class: 'v' }, money(n, sp.currency))) : el('div', { class: 'v' }, '-'),
170
+ detail(ok ? det : txt((sp && sp.message) || (sp && sp.status) || 'no codeburn data')));
171
+ box.append(spendCard('Spend today', 'sum:today', sp && sp.today, 'codeburn, all projects'));
172
+ box.append(spendCard('Spend this month', 'sum:month', sp && sp.month, 'codeburn, this calendar month'));
173
+ const w = (sum && sum.warnings) || { count: 0, first: null };
174
+ box.append(scard('Setup warnings',
175
+ track('sum:warnings', w.count, el('div', { class: 'v' + (w.count ? ' s-warn' : ' s-ok') }, String(w.count))),
176
+ detail(w.first ? txt(w.first) : 'Nothing needs attention.')));
177
+ s.append(box);
178
+ return s;
102
179
  }
103
180
 
104
181
  const SCAN_LABEL = { ok: ['ok', 'ok'], 'not-scanned': ['warn', 'not scanned'], 'no-verify': ['warn', 'no-verify'], flagged: ['missing', 'secrets found'], none: ['skipped', '-'] };
@@ -170,68 +247,96 @@ function codewatchDetail(repo) {
170
247
  el('div', { class: 'stat' }, el('b', {}, cw.gitleaksBlocks.length), el('span', {}, 'gitleaks blocks')),
171
248
  el('div', { class: 'stat' }, el('b', {}, cw.skipped.length), el('span', {}, 'pushed without a scan'))));
172
249
 
173
- s.append(el('h3', {}, 'Commits'));
174
- s.append(commitsList(cw.commits));
250
+ 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.'));
251
+ else s.append(el('h3', {}, 'Commits'), commitsList(cw.commits));
175
252
 
176
- s.append(el('h3', {}, 'Scans (last ' + cw.scans.length + ')'));
177
- s.append(scansList(cw.scans));
253
+ if (!cw.scans.length) s.append(inlineEmpty('Scans', 'no pre-push reviews yet.'));
254
+ else s.append(el('h3', {}, 'Scans (last ' + cw.scans.length + ')'), scansList(cw.scans));
178
255
 
179
- s.append(el('h3', {}, 'Trend (last ' + cw.trend.length + ' runs)'));
180
- s.append(trendSvg(cw.trend));
256
+ const skipped = cw.skippedRuns || 0;
257
+ if (!cw.trend.length) {
258
+ // By design: a skipped run (nothing new to review, or no gitleaks) has no findings, so it is not charted.
259
+ s.append(inlineEmpty('Trend', 'nothing to chart. ' + (skipped
260
+ ? '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.'
261
+ : 'No run has finished yet.')));
262
+ } else {
263
+ s.append(el('h3', {}, 'Trend (last ' + cw.trend.length + ' runs)'), trendSvg(cw.trend));
264
+ if (skipped) s.append(el('p', { class: 'note' }, skipped + ' skipped run' + (skipped === 1 ? ' is' : 's are') + ' not charted: they have no findings.'));
265
+ }
181
266
 
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)])));
267
+ if (!cw.gitleaksBlocks.length) s.append(inlineEmpty('Gitleaks blocks', 'no commits blocked.'));
268
+ 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
269
 
186
- s.append(el('h3', {}, 'Commits pushed without a pre-commit scan'));
187
- if (!cw.skipped.length) s.append(el('p', { class: 'empty' }, 'None.'));
270
+ if (!cw.skipped.length) s.append(inlineEmpty('Commits pushed without a pre-commit scan', 'none.'));
188
271
  else {
272
+ s.append(el('h3', {}, 'Commits pushed without a pre-commit scan'));
189
273
  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
274
  s.append(el('p', { class: 'note' }, 'Usually git commit --no-verify; rebases and cherry-picks also show up here.'));
191
275
  }
192
276
  return s;
193
277
  }
194
278
 
279
+ /** "29 other repos: not set up", expanding to one line per repo. key keeps it open across refreshes. */
280
+ function otherRepos(key, list, what, line) {
281
+ if (!list.length) return null;
282
+ return el('details', { class: 'missing-group', 'data-key': key },
283
+ el('summary', {}, list.length + ' other repo' + (list.length === 1 ? '' : 's') + ': ' + what),
284
+ el('ul', { class: 'sec-findings' }, list.map((r) => el('li', {}, repoName(r), line(r)))));
285
+ }
286
+
195
287
  function codewatchPanel(data) {
196
- const s = el('section', { class: 'card wide' }, el('h2', {}, 'Codewatch'));
288
+ const rows = data.overview.codewatch;
197
289
  const banner = scanBanner(data.machine.secretScanning);
290
+ if (!rows.length && !banner) {
291
+ const sg = (data.suggestions || []).find((x) => x.id === 'codewatch-global');
292
+ 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.');
293
+ return emptyLine('Codewatch', "isn't enabled in any repo yet. Run ", el('code', {}, 'prism-cli enable codewatch'), ' inside a repo.');
294
+ }
295
+ const s = el('section', { class: 'card wide' }, el('h2', {}, 'Codewatch'));
198
296
  if (banner) s.append(banner);
199
- const rows = data.overview.codewatch;
200
297
  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.'));
298
+ 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
299
  return s;
205
300
  }
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'),
301
+ const cur = data.usage && data.usage.currency;
302
+ const count = (key, n, cls) => track(key, n, el('td', { class: 'count' + (n ? ' ' + cls : '') }, n));
303
+ s.append(table(['Repo', 'High', 'Medium', 'Low', 'Last run', 'Reviewer', 'Unscanned', 'Gitleaks', 'Spend (month)'], rows.map((r) => [
304
+ 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'),
305
+ track('cw:last:' + r.path, r.lastRunAt, el('td', {}, r.lastRunAt ? (r.lastReviewId ? reportLink(r.lastReviewId, when(r.lastRunAt)) : when(r.lastRunAt)) : 'never')),
306
+ txt(r.reviewer || '-'), count('cw:u:' + r.path, r.unscannedCommits, 'sev-medium'),
210
307
  r.gitleaksWarning ? pill('warn', 'not scanned') : '-',
308
+ 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
309
  ])));
212
310
  const byPath = Object.fromEntries(data.repos.map((r) => [r.path, r]));
213
311
  s.append(el('h3', {}, 'Per repo'));
214
312
  for (const row of rows) {
215
313
  const repo = byPath[row.path]; if (!repo) continue;
216
314
  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'),
315
+ const d = el('details', repo.current ? { class: 'repo', open: '', 'data-key': 'cw:' + repo.path } : { class: 'repo', 'data-key': 'cw:' + repo.path },
316
+ 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
317
  repo.current ? el('span', { class: 'tag' }, 'this repo') : null),
220
318
  el('div', { class: 'repo-title' }, repo.path));
221
319
  d.append(codewatchDetail(repo));
222
320
  s.append(d);
223
321
  }
322
+ const shown = new Set(rows.map((r) => r.path));
323
+ const rest = data.overview.setup.filter((r) => !shown.has(r.path));
324
+ 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)));
325
+ if (other) s.append(other);
224
326
  return s;
225
327
  }
226
328
 
227
329
  function usagePanel(u) {
330
+ if (u.status === 'not-installed' || u.status === 'no-data') {
331
+ 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)), '.'));
332
+ }
228
333
  const s = el('section', { class: 'card' }, el('h2', {}, 'Usage (codeburn)'));
229
334
  if (u.status !== 'ok') {
230
- s.append(el('p', { class: 'empty' }, pill(u.status === 'no-data' ? 'warn' : 'missing', u.status), ' ', txt(u.message)));
335
+ s.append(el('p', { class: 'empty' }, pill('missing', u.status), ' ', txt(u.message)));
231
336
  } else {
232
337
  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'))));
338
+ 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')),
339
+ 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
340
  s.append(el('h3', {}, 'Top models this month'));
236
341
  s.append(table(['Model', 'Cost', 'Calls'], u.topModels.map((m) => [m.name, num(money(m.cost, u.currency)), num(m.calls)])));
237
342
  s.append(el('h3', {}, 'Top projects this month'));
@@ -254,9 +359,19 @@ function suggestionCallout(sg) {
254
359
  el('div', { class: 'fix' }, 'Applies to every repo on this machine, including new clones. Repos can opt out with ', el('code', {}, 'git config prism.codewatch false'), '.'));
255
360
  }
256
361
 
362
+ /** A row's help: the text, any steps and commands, then its doc links (https only), each opening in a new tab. */
363
+ function helpBlock(h) {
364
+ const links = (h.links || []).filter((l) => /^https:\\/\\//.test(txt(l.url)))
365
+ .map((l) => el('a', { href: txt(l.url), class: 'rlink', target: '_blank', rel: 'noopener noreferrer' }, txt(l.label)));
366
+ const steps = (h.steps || []).length ? el('ol', { class: 'help-steps' }, h.steps.map((x) => el('li', {}, txt(x)))) : null;
367
+ const cmds = (h.commands || []).length ? el('div', { class: 'help-links' }, 'Commands: ', h.commands.flatMap((c, n) => n ? [', ', el('code', {}, txt(c))] : [el('code', {}, txt(c))])) : null;
368
+ return el('div', { class: 'help' }, txt(h.text), steps, cmds, links.length ? el('div', { class: 'help-links' }, links.flatMap((a, n) => n ? [' · ', a] : [a])) : null);
369
+ }
370
+
257
371
  function setupPanel(data) {
258
372
  const s = el('section', { class: 'card' }, el('h2', {}, 'Setup checklist'));
259
- const rows = (items) => items.map((i) => [pill(i.status), i.label, el('div', {}, i.detail, i.fix ? el('div', { class: 'fix' }, 'Fix: ' + i.fix) : null)]);
373
+ const rows = (items) => items.map((i) => [pill(i.status), i.label, el('div', {}, i.detail,
374
+ i.help ? helpBlock(i.help) : i.fix ? el('div', { class: 'fix' }, 'Fix: ' + i.fix) : null)]);
260
375
  s.append(el('h3', {}, 'This machine'), table(['', 'Check', 'Detail'], rows(data.machine.setup)));
261
376
  const repos = data.overview.setup, c = data.overview.counts;
262
377
  s.append(el('h3', {}, 'Per repo: codewatch (' + c.codewatch + ' of ' + c.repos + ' installed)'));
@@ -271,23 +386,25 @@ function setupPanel(data) {
271
386
  ];
272
387
  const sg = (data.suggestions || []).find((x) => x.id === 'codewatch-global');
273
388
  if (sg) s.append(suggestionCallout(sg));
274
- // Always shown: the current repo (installed or not) and every installed repo.
389
+ // Shown: the current repo, repos with activity (listed first), and repos with something set up.
275
390
  const shown = repos.filter((r) => !r.collapsed);
276
391
  const hidden = repos.filter((r) => r.collapsed);
277
392
  if (shown.length) s.append(table(['Repo', 'Codewatch', 'Security Agent'], shown.map(rowOf)));
278
393
  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
- }
394
+ const optedOut = hidden.filter((r) => r.status === 'opted-out').length;
395
+ const group = el('details', { class: 'missing-group', 'data-key': 'setup:other' },
396
+ el('summary', {}, hidden.length + ' other repo' + (hidden.length === 1 ? '' : 's') + ': not set up' + (optedOut ? ' (' + optedOut + ' opted out of codewatch)' : '')),
397
+ table(['Repo', 'Codewatch', 'Security Agent'], hidden.map(rowOf)));
398
+ if (hidden.length) s.append(group);
284
399
  return s;
285
400
  }
286
401
 
287
- /** Security Agent per repo, from the enable security marker (local evidence; the page makes no AWS calls). */
402
+ const SEC_LABEL = { ok: 'ok', 'in-use': 'in use', missing: 'missing' };
403
+
404
+ /** Security Agent per repo: the enable security marker, else saved results (local evidence; the page makes no AWS calls). */
288
405
  function securityCell(sec) {
289
406
  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) : '',
407
+ 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
408
  sec.fix ? el('div', { class: 'fix' }, 'Fix: ', el('code', {}, sec.fix)) : null);
292
409
  }
293
410
 
@@ -304,13 +421,13 @@ function securityPanel(sec) {
304
421
  const tms = (sec && sec.threat_models) || [];
305
422
  const pts = (sec && sec.pentests) || [];
306
423
  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;
424
+ 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
425
  }
426
+ if (!rows.length) s.append(inlineEmpty('Code reviews', 'none yet. Run ', el('code', {}, (sec && sec.hint) || 'prism-cli security code-review'), '.'));
310
427
  for (const r of rows) {
311
428
  const counts = r.counts || {};
312
429
  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));
430
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(r)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
314
431
  b.append(el('div', { class: 'fix' },
315
432
  (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
433
  const pills = el('div', { class: 'sec-pills' });
@@ -348,7 +465,7 @@ function securityPanel(sec) {
348
465
  if (tms.length) {
349
466
  s.append(el('h3', {}, 'Threat models'));
350
467
  for (const t of tms) s.append(threatModelBlock(t, (sec && sec.threats_hint) || 'prism-cli security threats'));
351
- }
468
+ } else s.append(inlineEmpty('Threat models', 'none yet. Run ', el('code', {}, 'prism-cli security threat-model'), '.'));
352
469
  return s;
353
470
  }
354
471
 
@@ -359,7 +476,7 @@ const strideText = (list) => (list || []).map((x) => txt(x).toLowerCase().replac
359
476
  /** One cached threat model per repo, in the code review blocks' compact style. */
360
477
  function threatModelBlock(t, hint) {
361
478
  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));
479
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(t)), t.current ? el('span', { class: 'tag' }, 'this repo') : null));
363
480
  const docs = (t.scope_docs || []).length;
364
481
  b.append(el('div', { class: 'fix' },
365
482
  (t.pending ? 'started ' + when(t.started_at) : when(t.finished_at)) + ' · commit ' + txt(t.commit).slice(0, 12)
@@ -399,12 +516,12 @@ function threatModelBlock(t, hint) {
399
516
  /** Penetration-test rows, in the same Security card below the code-review rows. */
400
517
  function securityPentestRows(s, sec) {
401
518
  const rows = (sec && sec.pentests) || [];
402
- if (!rows.length) return;
519
+ if (!rows.length) { s.append(inlineEmpty('Penetration tests', 'none yet. Run ', el('code', {}, 'prism-cli security pentest'), '.')); return; }
403
520
  s.append(el('h3', {}, 'Penetration tests'));
404
521
  for (const r of rows) {
405
522
  const counts = r.counts || {};
406
523
  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));
524
+ b.append(el('div', { class: 'sec-head' }, el('b', {}, labelOf(r)), r.current ? el('span', { class: 'tag' }, 'this repo') : null));
408
525
  b.append(el('div', { class: 'fix' },
409
526
  (r.pending ? 'started ' + when(r.started_at) : when(r.finished_at)) + ' · ' + txt(r.target_host) + ' · job ' + txt(r.job_id).slice(0, 12)));
410
527
  const pills = el('div', { class: 'sec-pills' });
@@ -444,12 +561,9 @@ const AUDIT_SEVERITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];
444
561
 
445
562
  /** The latest aws check per account, in the Security panel's compact style. Local results only. */
446
563
  function awsPanel(aws) {
447
- const s = el('section', { class: 'card' }, el('h2', {}, 'AWS account'));
448
564
  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
- }
565
+ if (!rows.length) return emptyLine('AWS account', 'no checks yet. Run ', el('code', {}, (aws && aws.hint) || 'prism-cli aws check'), ' (read-only).');
566
+ const s = el('section', { class: 'card' }, el('h2', {}, 'AWS account'));
453
567
  for (const a of rows) {
454
568
  const b = el('div', { class: 'sec-repo' });
455
569
  b.append(el('div', { class: 'sec-head' }, el('b', {}, txt(a.account)), a.alias ? el('span', { class: 'tag' }, txt(a.alias)) : null));
@@ -473,22 +587,56 @@ function awsPanel(aws) {
473
587
  return s;
474
588
  }
475
589
 
590
+ /** Auto-refresh period while the tab is visible; paused while hidden. */
591
+ const AUTO_REFRESH_MS = 60000;
592
+ let lastLoad = 0, loading = false;
593
+
476
594
  async function load() {
595
+ if (loading) return;
596
+ loading = true;
597
+ try { await render(); } finally { loading = false; lastLoad = Date.now(); }
598
+ }
599
+
600
+ async function render() {
477
601
  const main = document.getElementById('main'), meta = document.getElementById('meta');
602
+ const before = meta.textContent;
478
603
  meta.textContent = 'loading...';
479
604
  let data;
480
605
  try {
481
606
  const res = await fetch('/api/data', { credentials: 'same-origin', cache: 'no-store' });
482
607
  if (!res.ok) throw new Error('HTTP ' + res.status);
483
608
  data = await res.json();
484
- } catch (e) { meta.textContent = 'failed to load: ' + e.message; return; }
609
+ } catch (e) {
610
+ // Keep the last good view; say the refresh failed.
611
+ meta.textContent = 'failed to load: ' + e.message + (before && before !== 'loading...' ? ' · showing ' + before : '');
612
+ return;
613
+ }
614
+ // Open/closed state of keyed <details> survives the redraw.
615
+ const openState = new Map([...main.querySelectorAll('details[data-key]')].map((d) => [d.getAttribute('data-key'), d.open]));
616
+ LABELS = new Map((data.repos || []).map((r) => [r.path, r.label || r.name]));
617
+ EMPTY = []; CUR = {};
485
618
  main.replaceChildren();
486
619
  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));
620
+ const panels = [summaryPanel(data.summary), codewatchPanel(data), usagePanel(data.usage), setupPanel(data), securityPanel(data.security), awsPanel(data.aws)];
621
+ main.append(...panels.filter(Boolean));
622
+ if (EMPTY.length) main.append(el('section', { class: 'card wide' }, el('h2', {}, 'Nothing recorded yet'), ...EMPTY));
623
+ for (const d of main.querySelectorAll('details[data-key]')) {
624
+ const k = d.getAttribute('data-key');
625
+ if (openState.has(k)) d.open = openState.get(k);
626
+ }
627
+ const changed = new Set(changedKeys(PREV, CUR));
628
+ for (const n of main.querySelectorAll('[data-track]')) {
629
+ const k = n.getAttribute('data-track');
630
+ if (changed.has(k)) { n.classList.add('changed'); n.setAttribute('title', 'changed since the previous load (was ' + PREV[k] + ')'); }
631
+ }
632
+ PREV = CUR;
488
633
  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);
634
+ meta.textContent = c.repos + ' repos under ' + txt(data.scope.root_display) + ' · ' + c.codewatch + ' with codewatch' + (data.scope.truncated ? ' (scan capped)' : '')
635
+ + ' · updated ' + when(data.generated_at) + (changed.size ? ' · ' + changed.size + ' value' + (changed.size === 1 ? '' : 's') + ' changed (outlined)' : '') + ' · refreshes every minute while visible';
490
636
  }
491
637
  document.getElementById('refresh').addEventListener('click', load);
638
+ setInterval(() => { if (document.visibilityState === 'visible' && Date.now() - lastLoad >= AUTO_REFRESH_MS - 1000) load(); }, AUTO_REFRESH_MS / 4);
639
+ document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible' && Date.now() - lastLoad >= AUTO_REFRESH_MS) load(); });
492
640
  load();
493
641
  `;
494
642
  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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDb,CAAC;AAEF,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgkBd,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,wFAAwF;IACxF,OAAO;;8CAEqC,KAAK,KAAK,KAAK;;;iBAG5C,KAAK,KAAK,MAAM,yBAAyB,CAAC;AAC3D,CAAC"}