openpitstop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +512 -0
- package/demo-repo/README.md +24 -0
- package/demo-repo/package-lock.json +4483 -0
- package/demo-repo/package.json +17 -0
- package/demo-repo/src/config.js +5 -0
- package/demo-repo/src/db.js +31 -0
- package/demo-repo/src/server.js +13 -0
- package/demo-repo/src/userController.js +22 -0
- package/demo-repo/src/userRepo.js +18 -0
- package/demo-repo/src/userService.js +59 -0
- package/demo-repo/test/user.test.js +12 -0
- package/demo-repo-fintech/README.md +27 -0
- package/demo-repo-fintech/package-lock.json +1023 -0
- package/demo-repo-fintech/package.json +14 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/src/gateway.js +26 -0
- package/demo-repo-fintech/src/server.js +61 -0
- package/demo-repo-generators/package-lock.json +1508 -0
- package/demo-repo-generators/package.json +17 -0
- package/demo-repo-generators/public/index.html +10 -0
- package/demo-repo-generators/scripts/build.js +17 -0
- package/demo-repo-generators/src/counter.js +16 -0
- package/demo-repo-integrity/README.md +49 -0
- package/demo-repo-integrity/package.json +12 -0
- package/demo-repo-integrity/src/money.js +19 -0
- package/demo-repo-integrity/test/money.test.js +17 -0
- package/dist/analyzers/accessibility.d.ts +18 -0
- package/dist/analyzers/accessibility.js +248 -0
- package/dist/analyzers/accessibility.js.map +1 -0
- package/dist/analyzers/dependencyGraph.d.ts +14 -0
- package/dist/analyzers/dependencyGraph.js +206 -0
- package/dist/analyzers/dependencyGraph.js.map +1 -0
- package/dist/analyzers/devex.d.ts +19 -0
- package/dist/analyzers/devex.js +301 -0
- package/dist/analyzers/devex.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +47 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/index.d.ts +12 -0
- package/dist/analyzers/index.js +38 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
- package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
- package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
- package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
- package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
- package/dist/analyzers/integrity/exitCheat.js +31 -0
- package/dist/analyzers/integrity/exitCheat.js.map +1 -0
- package/dist/analyzers/integrity/git.d.ts +10 -0
- package/dist/analyzers/integrity/git.js +181 -0
- package/dist/analyzers/integrity/git.js.map +1 -0
- package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
- package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
- package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
- package/dist/analyzers/integrity/helpers.d.ts +21 -0
- package/dist/analyzers/integrity/helpers.js +66 -0
- package/dist/analyzers/integrity/helpers.js.map +1 -0
- package/dist/analyzers/integrity/index.d.ts +10 -0
- package/dist/analyzers/integrity/index.js +27 -0
- package/dist/analyzers/integrity/index.js.map +1 -0
- package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
- package/dist/analyzers/integrity/mockOverreach.js +72 -0
- package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
- package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
- package/dist/analyzers/integrity/suppressionCreep.js +27 -0
- package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
- package/dist/analyzers/integrity/testTamper.d.ts +2 -0
- package/dist/analyzers/integrity/testTamper.js +153 -0
- package/dist/analyzers/integrity/testTamper.js.map +1 -0
- package/dist/analyzers/integrity/types.d.ts +63 -0
- package/dist/analyzers/integrity/types.js +13 -0
- package/dist/analyzers/integrity/types.js.map +1 -0
- package/dist/analyzers/ledger/attacks.d.ts +17 -0
- package/dist/analyzers/ledger/attacks.js +135 -0
- package/dist/analyzers/ledger/attacks.js.map +1 -0
- package/dist/analyzers/ledger/discover.d.ts +15 -0
- package/dist/analyzers/ledger/discover.js +122 -0
- package/dist/analyzers/ledger/discover.js.map +1 -0
- package/dist/analyzers/ledger/evidence.d.ts +22 -0
- package/dist/analyzers/ledger/evidence.js +127 -0
- package/dist/analyzers/ledger/evidence.js.map +1 -0
- package/dist/analyzers/ledger/harness.d.ts +4 -0
- package/dist/analyzers/ledger/harness.js +270 -0
- package/dist/analyzers/ledger/harness.js.map +1 -0
- package/dist/analyzers/ledger/index.d.ts +14 -0
- package/dist/analyzers/ledger/index.js +116 -0
- package/dist/analyzers/ledger/index.js.map +1 -0
- package/dist/analyzers/ledger/types.d.ts +31 -0
- package/dist/analyzers/ledger/types.js +2 -0
- package/dist/analyzers/ledger/types.js.map +1 -0
- package/dist/analyzers/perf.d.ts +2 -0
- package/dist/analyzers/perf.js +106 -0
- package/dist/analyzers/perf.js.map +1 -0
- package/dist/analyzers/reliability.d.ts +22 -0
- package/dist/analyzers/reliability.js +339 -0
- package/dist/analyzers/reliability.js.map +1 -0
- package/dist/analyzers/routes.d.ts +17 -0
- package/dist/analyzers/routes.js +115 -0
- package/dist/analyzers/routes.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +302 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/suiteRunner.d.ts +64 -0
- package/dist/analyzers/suiteRunner.js +381 -0
- package/dist/analyzers/suiteRunner.js.map +1 -0
- package/dist/analyzers/tests.d.ts +2 -0
- package/dist/analyzers/tests.js +215 -0
- package/dist/analyzers/tests.js.map +1 -0
- package/dist/analyzers/types.d.ts +203 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/util.d.ts +35 -0
- package/dist/analyzers/util.js +209 -0
- package/dist/analyzers/util.js.map +1 -0
- package/dist/ciCheckA.d.ts +2 -0
- package/dist/ciCheckA.js +5 -0
- package/dist/ciCheckA.js.map +1 -0
- package/dist/ciCheckB.d.ts +2 -0
- package/dist/ciCheckB.js +6 -0
- package/dist/ciCheckB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/budget.d.ts +33 -0
- package/dist/commands/budget.js +108 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/ci.d.ts +14 -0
- package/dist/commands/ci.js +157 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +163 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/digest.d.ts +8 -0
- package/dist/commands/digest.js +189 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +98 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.js +170 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +136 -0
- package/dist/commands/gate.js.map +1 -0
- package/dist/commands/honesty.d.ts +2 -0
- package/dist/commands/honesty.js +182 -0
- package/dist/commands/honesty.js.map +1 -0
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +244 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/integrity.d.ts +2 -0
- package/dist/commands/integrity.js +74 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +90 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/pen.d.ts +17 -0
- package/dist/commands/pen.js +158 -0
- package/dist/commands/pen.js.map +1 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +37 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/readyCheck.d.ts +21 -0
- package/dist/commands/readyCheck.js +68 -0
- package/dist/commands/readyCheck.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +48 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/repro.d.ts +3 -0
- package/dist/commands/repro.js +61 -0
- package/dist/commands/repro.js.map +1 -0
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.js +348 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +163 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +103 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/try.d.ts +12 -0
- package/dist/commands/try.js +208 -0
- package/dist/commands/try.js.map +1 -0
- package/dist/commands/verify.d.ts +44 -0
- package/dist/commands/verify.js +381 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.js +70 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +61 -0
- package/dist/evidence.js.map +1 -0
- package/dist/graph/correlate.d.ts +7 -0
- package/dist/graph/correlate.js +246 -0
- package/dist/graph/correlate.js.map +1 -0
- package/dist/graph/integrity.d.ts +22 -0
- package/dist/graph/integrity.js +39 -0
- package/dist/graph/integrity.js.map +1 -0
- package/dist/installer/targets.d.ts +23 -0
- package/dist/installer/targets.js +162 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/memory/store.d.ts +33 -0
- package/dist/memory/store.js +131 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/pen/dynamic.d.ts +36 -0
- package/dist/pen/dynamic.js +622 -0
- package/dist/pen/dynamic.js.map +1 -0
- package/dist/pen/fix.d.ts +35 -0
- package/dist/pen/fix.js +239 -0
- package/dist/pen/fix.js.map +1 -0
- package/dist/pen/proof.d.ts +46 -0
- package/dist/pen/proof.js +112 -0
- package/dist/pen/proof.js.map +1 -0
- package/dist/pen/report.d.ts +11 -0
- package/dist/pen/report.js +258 -0
- package/dist/pen/report.js.map +1 -0
- package/dist/pen/static.d.ts +17 -0
- package/dist/pen/static.js +565 -0
- package/dist/pen/static.js.map +1 -0
- package/dist/pen/store.d.ts +11 -0
- package/dist/pen/store.js +36 -0
- package/dist/pen/store.js.map +1 -0
- package/dist/pen/types.d.ts +112 -0
- package/dist/pen/types.js +53 -0
- package/dist/pen/types.js.map +1 -0
- package/dist/report/format.d.ts +126 -0
- package/dist/report/format.js +706 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/score.d.ts +47 -0
- package/dist/report/score.js +164 -0
- package/dist/report/score.js.map +1 -0
- package/dist/repro/framework.d.ts +26 -0
- package/dist/repro/framework.js +224 -0
- package/dist/repro/framework.js.map +1 -0
- package/dist/repro/generate.d.ts +14 -0
- package/dist/repro/generate.js +669 -0
- package/dist/repro/generate.js.map +1 -0
- package/dist/repro/ids.d.ts +32 -0
- package/dist/repro/ids.js +175 -0
- package/dist/repro/ids.js.map +1 -0
- package/dist/repro/index.d.ts +24 -0
- package/dist/repro/index.js +69 -0
- package/dist/repro/index.js.map +1 -0
- package/dist/repro/pen.d.ts +26 -0
- package/dist/repro/pen.js +321 -0
- package/dist/repro/pen.js.map +1 -0
- package/dist/repro/run.d.ts +18 -0
- package/dist/repro/run.js +36 -0
- package/dist/repro/run.js.map +1 -0
- package/dist/sandbox/proxy.d.ts +41 -0
- package/dist/sandbox/proxy.js +231 -0
- package/dist/sandbox/proxy.js.map +1 -0
- package/dist/sandbox/startCmd.d.ts +24 -0
- package/dist/sandbox/startCmd.js +71 -0
- package/dist/sandbox/startCmd.js.map +1 -0
- package/dist/ui/spinner.d.ts +18 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/verify/metrics.d.ts +13 -0
- package/dist/verify/metrics.js +72 -0
- package/dist/verify/metrics.js.map +1 -0
- package/package.json +68 -0
- package/scripts/cheat-demo.cjs +102 -0
- package/templates/ledger/preload.cjs +331 -0
- package/templates/pen/preload.cjs +196 -0
- package/templates/pitstop.prompt.md +371 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { buildModel, collectIntegrity, loadHistory, loadProofs } from "../report/format.js";
|
|
7
|
+
import { computeScore, gradeColor } from "../report/score.js";
|
|
8
|
+
import { enumerateFindings } from "../repro/ids.js";
|
|
9
|
+
const BLOCKS = "▁▂▃▄▅▆▇█";
|
|
10
|
+
function sparkline(values) {
|
|
11
|
+
if (values.length === 0)
|
|
12
|
+
return "·";
|
|
13
|
+
if (values.length === 1)
|
|
14
|
+
return chalk.dim(String(values[0]));
|
|
15
|
+
const min = Math.min(...values);
|
|
16
|
+
const max = Math.max(...values);
|
|
17
|
+
const span = max - min || 1;
|
|
18
|
+
const chars = values.map((v) => BLOCKS[Math.min(7, Math.floor(((v - min) / span) * 7.999))]);
|
|
19
|
+
const improving = values[values.length - 1] >= values[0];
|
|
20
|
+
return (improving ? chalk.green : chalk.yellow)(chars.join(""));
|
|
21
|
+
}
|
|
22
|
+
function windowOf(repo, days) {
|
|
23
|
+
const hist = loadHistory(repo);
|
|
24
|
+
if (!days)
|
|
25
|
+
return { scans: hist.scans, verifies: hist.verifies };
|
|
26
|
+
const cut = Date.now() - days * 86_400_000;
|
|
27
|
+
return {
|
|
28
|
+
scans: hist.scans.filter((s) => Date.parse(s.timestamp) >= cut),
|
|
29
|
+
verifies: hist.verifies.filter((v) => Date.parse(v.timestamp) >= cut),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `pitstop digest` — the shareable progress story. Reads the `.pitstop`
|
|
34
|
+
* history and answers "what happened to this repo since <window>?" for humans:
|
|
35
|
+
* score movement, what got fixed, verifies/gates run, cheat attempts caught,
|
|
36
|
+
* flakiest tests, and what's still open.
|
|
37
|
+
*/
|
|
38
|
+
export const digest = new Command("digest")
|
|
39
|
+
.description("Progress digest over the .pitstop history: score movement, what got fixed, gate results, " +
|
|
40
|
+
"cheat catches, flaky tests and open findings. --md writes a shareable markdown file.")
|
|
41
|
+
.argument("[repo]", "path to the repo (default: current dir)", ".")
|
|
42
|
+
.option("--days <n>", "only look at the last n days (default: all history)")
|
|
43
|
+
.option("-m, --md [file]", "also write a markdown digest (default PITSTOP_DIGEST.md)")
|
|
44
|
+
.action((repoArg, options) => {
|
|
45
|
+
const repo = path.resolve(repoArg);
|
|
46
|
+
const days = options.days ? Math.max(1, Math.floor(Number(options.days) || 7)) : null;
|
|
47
|
+
const { scans, verifies } = windowOf(repo, days);
|
|
48
|
+
if (scans.length === 0) {
|
|
49
|
+
console.log(chalk.yellow(`no scans ${days ? `in the last ${days} day(s) ` : ""}in ${path.join(repo, ".pitstop")} — run \`pitstop scan\` (or \`pitstop try\`) a few times first.`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const model = buildModel(repo);
|
|
53
|
+
const integ = collectIntegrity(repo);
|
|
54
|
+
const windowInteg = integ
|
|
55
|
+
? {
|
|
56
|
+
...integ,
|
|
57
|
+
events: integ.events.filter((e) => !days || Date.parse(e.timestamp) >= Date.now() - days * 86_400_000),
|
|
58
|
+
}
|
|
59
|
+
: undefined;
|
|
60
|
+
const firstScore = computeScore(scans[0]);
|
|
61
|
+
const lastScore = computeScore(scans[scans.length - 1]);
|
|
62
|
+
const scores = scans.map((s) => computeScore(s).score);
|
|
63
|
+
const delta = lastScore.score - firstScore.score;
|
|
64
|
+
const metric = (label, get, worse) => {
|
|
65
|
+
const a = get(scans[0]);
|
|
66
|
+
const b = get(scans[scans.length - 1]);
|
|
67
|
+
if (a == null || b == null)
|
|
68
|
+
return null;
|
|
69
|
+
return { label, a, b, changed: worse(b - a) };
|
|
70
|
+
};
|
|
71
|
+
const metrics = [
|
|
72
|
+
metric("security", (s) => (s.security.status === "ok" ? s.security.issues.length : null), (d) => d > 0),
|
|
73
|
+
metric("circular imports", (s) => (s.dependencyGraph.status === "ok" ? s.dependencyGraph.circular.length : null), (d) => d > 0),
|
|
74
|
+
metric("clones", (s) => (s.duplication.status === "ok" ? s.duplication.cloneCount : null), (d) => d > 0),
|
|
75
|
+
metric("failed tests", (s) => (s.tests.status === "ok" ? s.tests.failed : null), (d) => d > 0),
|
|
76
|
+
metric("coverage %", (s) => (s.tests.status === "ok" ? s.tests.coverage ?? null : null), (d) => d < 0),
|
|
77
|
+
metric("flaky tests", (s) => (s.reliability.status === "ok" ? s.reliability.flakyTests.length : null), (d) => d > 0),
|
|
78
|
+
].filter((m) => m != null);
|
|
79
|
+
const fixed = [];
|
|
80
|
+
const regressed = [];
|
|
81
|
+
for (const m of metrics) {
|
|
82
|
+
if (m.a === m.b)
|
|
83
|
+
continue;
|
|
84
|
+
const d = m.b - m.a;
|
|
85
|
+
if (m.changed)
|
|
86
|
+
regressed.push(`${m.label} ${m.a} → ${m.b}`);
|
|
87
|
+
else
|
|
88
|
+
fixed.push(`${m.label} ${m.a} → ${m.b}`);
|
|
89
|
+
}
|
|
90
|
+
const flakyTally = new Map();
|
|
91
|
+
for (const s of scans) {
|
|
92
|
+
if (s.reliability.status !== "ok")
|
|
93
|
+
continue;
|
|
94
|
+
for (const f of s.reliability.flakyTests) {
|
|
95
|
+
const hit = flakyTally.get(f.name);
|
|
96
|
+
if (hit)
|
|
97
|
+
hit.count++;
|
|
98
|
+
else
|
|
99
|
+
flakyTally.set(f.name, { name: f.name, count: 1 });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const flakyTop = [...flakyTally.values()].sort((a, b) => b.count - a.count).slice(0, 3);
|
|
103
|
+
const failedChecks = verifies.filter((v) => (v.exitCode ?? 0) >= 1);
|
|
104
|
+
const catches = (windowInteg?.events ?? []).filter((e) => e.verdict !== "CLEAN").length;
|
|
105
|
+
const latest = scans[scans.length - 1];
|
|
106
|
+
const open = enumerateFindings(latest)
|
|
107
|
+
.sort((a, b) => (a.severity === "critical" || a.severity === "high" ? -1 : 1))
|
|
108
|
+
.slice(0, 3);
|
|
109
|
+
const lines = [];
|
|
110
|
+
lines.push(`${chalk.bold("OpenPitStop Score".padEnd(20))}: ${gradeColorFn(lastScore.grade)(chalk.bold(`${lastScore.score}/100 (${lastScore.grade})`))} ${delta !== 0 ? chalk.dim(`was ${firstScore.score}/100 (${firstScore.grade}) · ${delta > 0 ? chalk.green(`+${delta}`) : chalk.red(delta)}`) : chalk.dim(`unchanged from ${firstScore.score}/100`)}`);
|
|
111
|
+
lines.push(`${chalk.bold("Trend".padEnd(20))}: ${sparkline(scores)}`);
|
|
112
|
+
lines.push("");
|
|
113
|
+
lines.push(chalk.bold("What changed:"));
|
|
114
|
+
if (fixed.length === 0 && regressed.length === 0)
|
|
115
|
+
lines.push(` ${chalk.dim("no measurable change across scans")}`);
|
|
116
|
+
for (const f of fixed)
|
|
117
|
+
lines.push(` ${chalk.green("✓ " + f)}`);
|
|
118
|
+
for (const r of regressed)
|
|
119
|
+
lines.push(` ${chalk.red("✗ " + r)}`);
|
|
120
|
+
lines.push("");
|
|
121
|
+
lines.push(chalk.bold("Gate activity:"));
|
|
122
|
+
lines.push(` ${verifies.length} verify(ies) in window · ${failedChecks.length} failed gate(s) · ${catches} integrity catch(es)${windowInteg?.selfCorrected ? `, ${windowInteg.selfCorrected} self-corrected` : ""}`);
|
|
123
|
+
if (flakyTop.length > 0) {
|
|
124
|
+
lines.push(` flakiest: ${flakyTop.map((f) => `${chalk.yellow(f.name)} (${f.count}×)`).join(", ")}`);
|
|
125
|
+
}
|
|
126
|
+
lines.push("");
|
|
127
|
+
const proofs = loadProofs(repo);
|
|
128
|
+
lines.push(chalk.bold("Permanent proof:"));
|
|
129
|
+
lines.push(proofs.length > 0
|
|
130
|
+
? ` ${chalk.green(`${proofs.length} committed repro test(s)`)} — every fix proven failing-then-passing`
|
|
131
|
+
: ` ${chalk.yellow("none committed yet")}`);
|
|
132
|
+
lines.push("");
|
|
133
|
+
lines.push(chalk.bold("Still open:"));
|
|
134
|
+
if (open.length === 0)
|
|
135
|
+
lines.push(` ${chalk.green("nothing — latest scan is clean")}`);
|
|
136
|
+
for (const f of open) {
|
|
137
|
+
lines.push(` ${chalk.red(`${f.severity.toUpperCase()} ${f.type}`)}: ${f.description} [${chalk.dim(f.id)}]`);
|
|
138
|
+
}
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push(chalk.dim(`${scans.length} scan(s) · ${verifies.length} verify(ies) · oldest ${scans[0].timestamp.slice(0, 19).replace("T", " ")} · newest ${scans[scans.length - 1].timestamp.slice(0, 19).replace("T", " ")}${days ? ` · last ${days} day(s)` : ""}`));
|
|
141
|
+
console.log(boxen(lines.join("\n"), {
|
|
142
|
+
title: ` PITSTOP — Digest `,
|
|
143
|
+
titleAlignment: "center",
|
|
144
|
+
borderStyle: "double",
|
|
145
|
+
padding: 1,
|
|
146
|
+
borderColor: delta < 0 ? "red" : "green",
|
|
147
|
+
}));
|
|
148
|
+
if (options.md) {
|
|
149
|
+
const mdPath = typeof options.md === "string" && options.md.length > 0
|
|
150
|
+
? path.resolve(options.md)
|
|
151
|
+
: path.join(repo, "PITSTOP_DIGEST.md");
|
|
152
|
+
const md = [
|
|
153
|
+
`# PITSTOP — Digest`,
|
|
154
|
+
``,
|
|
155
|
+
`_Repo: \`${repo}\` · ${scans.length} scan(s) in ${days ? `the last ${days} day(s)` : "all history"}_ `,
|
|
156
|
+
``,
|
|
157
|
+
`## OpenPitStop Score: **${lastScore.score}/100 (${lastScore.grade})**${delta !== 0 ? ` (was ${firstScore.score}/100, ${delta > 0 ? "+" : ""}${delta})` : ""}`,
|
|
158
|
+
``,
|
|
159
|
+
`## What changed`,
|
|
160
|
+
``,
|
|
161
|
+
...(fixed.length === 0 && regressed.length === 0 ? ["No measurable change across scans.", ""] : []),
|
|
162
|
+
...fixed.map((f) => `- ✅ ${f}`),
|
|
163
|
+
...regressed.map((r) => `- ❌ ${r}`),
|
|
164
|
+
``,
|
|
165
|
+
`## Gate activity`,
|
|
166
|
+
``,
|
|
167
|
+
`- ${verifies.length} verify(ies) · ${failedChecks.length} failed gate(s) · ${catches} integrity catch(es)`,
|
|
168
|
+
...(flakyTop.length > 0 ? [`- Flakiest: ${flakyTop.map((f) => `${f.name} (${f.count}×)`).join(", ")}`] : []),
|
|
169
|
+
``,
|
|
170
|
+
`## Permanent proof`,
|
|
171
|
+
``,
|
|
172
|
+
proofs.length > 0
|
|
173
|
+
? `- ${proofs.length} committed repro test(s) — every fix proven failing-then-passing`
|
|
174
|
+
: `- None committed yet.`,
|
|
175
|
+
``,
|
|
176
|
+
`## Still open`,
|
|
177
|
+
``,
|
|
178
|
+
...(open.length === 0 ? ["- Nothing — latest scan is clean.", ""] : open.map((f) => `- **${f.severity.toUpperCase()} ${f.type}** — ${f.description} (\`${f.id}\`)`)),
|
|
179
|
+
``,
|
|
180
|
+
].join("\n");
|
|
181
|
+
fs.writeFileSync(mdPath, md);
|
|
182
|
+
console.log(chalk.dim(`\nMarkdown digest written to ${mdPath}\n`));
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
function gradeColorFn(g) {
|
|
186
|
+
const c = gradeColor(g);
|
|
187
|
+
return c === "green" ? chalk.green : c === "yellow" ? chalk.yellow : chalk.red;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/commands/digest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { commandExists, safeExec } from "../analyzers/util.js";
|
|
7
|
+
/**
|
|
8
|
+
* `pitstop doctor` — why is a category "skipped"? One command answers it.
|
|
9
|
+
* Checks the toolchain (node, git, jscpd, gitleaks, semgrep, pa11y/axe) and
|
|
10
|
+
* prints install hints for everything missing.
|
|
11
|
+
*/
|
|
12
|
+
function hasCommand(cmd) {
|
|
13
|
+
try {
|
|
14
|
+
return commandExists(cmd);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const doctor = new Command("doctor")
|
|
21
|
+
.description("Check the local toolchain and explain which scan categories can (and cannot) run")
|
|
22
|
+
.argument("[repo]", "path to the repo to diagnose (defaults to cwd)", ".")
|
|
23
|
+
.action((repoArg) => {
|
|
24
|
+
const repo = path.resolve(repoArg);
|
|
25
|
+
const rows = [];
|
|
26
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
27
|
+
rows.push([
|
|
28
|
+
"Node.js",
|
|
29
|
+
major >= 22,
|
|
30
|
+
`v${process.versions.node}${major >= 22 ? "" : " — OpenPitStop needs v22+ (npm install node@latest)"}`,
|
|
31
|
+
]);
|
|
32
|
+
const git = hasCommand("git");
|
|
33
|
+
rows.push(["git", git, "the fix loop, integrity gate and verify all diff against git"]);
|
|
34
|
+
let hasHead = false;
|
|
35
|
+
if (git) {
|
|
36
|
+
const inRepo = safeExec("git", ["rev-parse", "--is-inside-work-tree"], repo, 10000);
|
|
37
|
+
hasHead =
|
|
38
|
+
inRepo.code === 0 &&
|
|
39
|
+
safeExec("git", ["rev-parse", "--verify", "HEAD"], repo, 10000).code === 0;
|
|
40
|
+
}
|
|
41
|
+
rows.push([
|
|
42
|
+
"git baseline (HEAD)",
|
|
43
|
+
hasHead,
|
|
44
|
+
hasHead ? "commits present — integrity gate can diff fixes" : "no commits yet — the integrity gate needs a committed baseline",
|
|
45
|
+
]);
|
|
46
|
+
rows.push(["npm", hasCommand("npm"), "bundled with Node.js"]);
|
|
47
|
+
const jscpd = hasCommand("jscpd");
|
|
48
|
+
rows.push(["jscpd (duplication)", jscpd, jscpd ? "ready" : "npm install -g jscpd — enables the duplication analyzer"]);
|
|
49
|
+
const gitleaks = hasCommand("gitleaks");
|
|
50
|
+
rows.push(["gitleaks (secrets)", gitleaks, gitleaks ? "ready" : "brew install gitleaks / scoop install gitleaks — adds secret scanning"]);
|
|
51
|
+
const semgrep = hasCommand("semgrep");
|
|
52
|
+
rows.push(["semgrep (code scanning)", semgrep, semgrep ? "ready" : "pip install semgrep — adds static code scanning"]);
|
|
53
|
+
const pa11y = hasCommand("pa11y");
|
|
54
|
+
rows.push(["pa11y / axe (accessibility)", pa11y, pa11y ? "ready" : "npm install -g pa11y — enables live-page accessibility checks (falls back to static JSX lint)"]);
|
|
55
|
+
let testFrameworks = "none detected";
|
|
56
|
+
const pkgPath = path.join(repo, "package.json");
|
|
57
|
+
if (fs.existsSync(pkgPath)) {
|
|
58
|
+
try {
|
|
59
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
60
|
+
const all = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
61
|
+
const found = ["jest", "vitest", "mocha", "ava"].filter((f) => f in all);
|
|
62
|
+
if (found.length > 0)
|
|
63
|
+
testFrameworks = found.join(", ");
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* ignore */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
rows.push(["test framework", testFrameworks !== "none detected", testFrameworks]);
|
|
70
|
+
const pitstopDir = path.join(repo, ".pitstop");
|
|
71
|
+
rows.push([
|
|
72
|
+
".pitstop history",
|
|
73
|
+
fs.existsSync(pitstopDir),
|
|
74
|
+
fs.existsSync(pitstopDir) ? "scans recorded" : "no scans yet — run `pitstop scan` to start the history",
|
|
75
|
+
]);
|
|
76
|
+
const lines = rows.map(([name, ok, note]) => {
|
|
77
|
+
const statusRaw = ok ? "✓ ok" : "✗ missing";
|
|
78
|
+
const status = (ok ? chalk.green : chalk.red)(statusRaw.padEnd(9));
|
|
79
|
+
const label = chalk.bold(name.padEnd(24));
|
|
80
|
+
const hint = ok ? chalk.dim(note) : chalk.yellow(note);
|
|
81
|
+
return ` ${label} ${status} ${hint}`;
|
|
82
|
+
});
|
|
83
|
+
const missing = rows.filter(([, ok]) => !ok);
|
|
84
|
+
const critical = rows.filter(([name, ok]) => !ok && ["Node.js", "git", "npm"].includes(name));
|
|
85
|
+
const verdict = critical.length > 0
|
|
86
|
+
? chalk.red(`${critical.length} critical missing — install those before relying on OpenPitStop`)
|
|
87
|
+
: missing.length === 0
|
|
88
|
+
? chalk.green("everything OpenPitStop needs is installed — the full scan should run with no `skipped` categories")
|
|
89
|
+
: chalk.yellow(`${missing.length} optional tool(s) missing — those scan categories will print "skipped" (install hints above)`);
|
|
90
|
+
console.log(boxen([...lines, "", ` ${chalk.bold("Verdict:")} ${verdict}`].join("\n"), {
|
|
91
|
+
title: " PITSTOP — Doctor ",
|
|
92
|
+
titleAlignment: "center",
|
|
93
|
+
borderStyle: "double",
|
|
94
|
+
padding: 1,
|
|
95
|
+
borderColor: critical.length > 0 ? "red" : missing.length > 0 ? "yellow" : "green",
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execa } from "execa";
|
|
6
|
+
import { loadScan, repro } from "../repro/index.js";
|
|
7
|
+
import { resolveFinding } from "../repro/ids.js";
|
|
8
|
+
import { loadPenLatest, resolvePenFinding } from "../pen/store.js";
|
|
9
|
+
import { runVerify } from "./verify.js";
|
|
10
|
+
/**
|
|
11
|
+
* `pitstop drive <finding-id>` — hand a finding to YOUR agent, then verify.
|
|
12
|
+
*
|
|
13
|
+
* OpenPitStop never fixes your code itself (no magic auto-edits on a repo it does
|
|
14
|
+
* not own). Instead: drive builds a precise mission prompt for the agent you
|
|
15
|
+
* already trust (claude/codex/opencode/...), runs it, then runs `pitstop
|
|
16
|
+
* verify` on the result and reports PASS/FAIL honestly.
|
|
17
|
+
*
|
|
18
|
+
* The agent command comes from --agent or PITSTOP_AGENT; `{prompt}` is where
|
|
19
|
+
* the mission text is inserted, e.g.:
|
|
20
|
+
* pitstop drive security-abc123 --agent "claude -p \"{prompt}\""
|
|
21
|
+
*/
|
|
22
|
+
function missionPrompt(repo, finding, reproHint, fromPen) {
|
|
23
|
+
return [
|
|
24
|
+
`You are fixing one specific OpenPitStop finding in the repo at ${repo}.`,
|
|
25
|
+
``,
|
|
26
|
+
`Finding: ${finding.id} (${finding.source}/${finding.type}, severity ${finding.severity})`,
|
|
27
|
+
`Description: ${finding.description}`,
|
|
28
|
+
finding.file ? `Location: ${finding.file}${finding.line ? ":" + finding.line : ""}` : "",
|
|
29
|
+
``,
|
|
30
|
+
`Work like this, in order, without skipping:`,
|
|
31
|
+
`1. RUN the repro first: \`npx openpitstop repro ${finding.id}\`. It MUST FAIL (the bug is live).`,
|
|
32
|
+
` If it refuses or passes, STOP and re-read the finding — do not fix blind.`,
|
|
33
|
+
reproHint,
|
|
34
|
+
`2. Make the smallest fix that addresses the root cause. Do not refactor unrelated code.`,
|
|
35
|
+
`3. Re-run the SAME repro: it must now PASS.`,
|
|
36
|
+
fromPen
|
|
37
|
+
? `4. \`npx openpitstop verify\` is a static gate — for runtime pen findings the repro PASS is the real verdict;`
|
|
38
|
+
: `4. Run \`npx openpitstop verify\` — it must come back clean (no regressions, integrity intact).`,
|
|
39
|
+
`5. If anything regressed, revert the smallest unit and try again.`,
|
|
40
|
+
``,
|
|
41
|
+
`Finish by reporting: what was wrong, what you changed, and the exact commands you ran.`,
|
|
42
|
+
].filter(Boolean).join("\n");
|
|
43
|
+
}
|
|
44
|
+
export const drive = new Command("drive")
|
|
45
|
+
.description("Hand a finding to YOUR own agent (claude/codex/opencode/...), then verify the result. " +
|
|
46
|
+
"Mission: repro FAILS first → fix → repro PASSES → `pitstop verify` clean. OpenPitStop never auto-edits your code.")
|
|
47
|
+
.argument("<finding-id>", "finding id from scan-latest.json or pen-latest.json")
|
|
48
|
+
.argument("[repo]", "path to the repo", ".")
|
|
49
|
+
.option("--agent <cmd>", "agent command with {prompt} placeholder (or set PITSTOP_AGENT)")
|
|
50
|
+
.action(async (findingId, repoArg, options) => {
|
|
51
|
+
const repo = path.resolve(repoArg);
|
|
52
|
+
const scan = loadScan(repo);
|
|
53
|
+
const pen = loadPenLatest(repo);
|
|
54
|
+
let finding = null;
|
|
55
|
+
let reproHint = "";
|
|
56
|
+
let fromPen = false;
|
|
57
|
+
const scanHit = scan ? resolveFinding(scan, findingId) : null;
|
|
58
|
+
if (scanHit) {
|
|
59
|
+
finding = {
|
|
60
|
+
id: scanHit.id,
|
|
61
|
+
source: scanHit.source,
|
|
62
|
+
severity: scanHit.severity,
|
|
63
|
+
type: scanHit.type,
|
|
64
|
+
description: scanHit.description,
|
|
65
|
+
file: scanHit.file,
|
|
66
|
+
line: scanHit.line,
|
|
67
|
+
};
|
|
68
|
+
reproHint = `The repro generator exists for ${scanHit.source}/${scanHit.type} — use it as your contract.`;
|
|
69
|
+
}
|
|
70
|
+
else if (pen) {
|
|
71
|
+
const pf = resolvePenFinding(pen, findingId);
|
|
72
|
+
if (pf) {
|
|
73
|
+
fromPen = true;
|
|
74
|
+
finding = {
|
|
75
|
+
id: pf.id,
|
|
76
|
+
source: pf.source,
|
|
77
|
+
severity: pf.severity,
|
|
78
|
+
type: pf.type,
|
|
79
|
+
description: pf.title,
|
|
80
|
+
file: pf.file,
|
|
81
|
+
line: pf.line,
|
|
82
|
+
};
|
|
83
|
+
reproHint = pf.attack
|
|
84
|
+
? `Replay the attack if needed: ${pf.attack.method} ${pf.attack.path}`
|
|
85
|
+
: `This is a static observation — apply the fix guidance, then re-run \`npx openpitstop pen\` for this finding.`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!finding) {
|
|
89
|
+
console.log(chalk.yellow(`\nfinding "${findingId}" not found in scan-latest.json or pen-latest.json\n`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const agentCmd = options.agent ?? process.env.PITSTOP_AGENT ?? "";
|
|
93
|
+
if (!agentCmd) {
|
|
94
|
+
console.log(boxen(`pitstop drive ${findingId}\n\n` +
|
|
95
|
+
`${chalk.yellow("no agent command configured")}\n\n` +
|
|
96
|
+
`Pass one with --agent (use {prompt} as the placeholder) or export PITSTOP_AGENT.\n\n` +
|
|
97
|
+
`Examples:\n` +
|
|
98
|
+
` --agent 'claude -p "{prompt}"'\n` +
|
|
99
|
+
` --agent 'codex exec -- "{prompt}"'\n` +
|
|
100
|
+
` --agent 'opencode run "{prompt}"'`, {
|
|
101
|
+
title: " PITSTOP — Drive ",
|
|
102
|
+
titleAlignment: "center",
|
|
103
|
+
borderStyle: "round",
|
|
104
|
+
padding: 1,
|
|
105
|
+
borderColor: "yellow",
|
|
106
|
+
}));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const prompt = missionPrompt(repo, finding, reproHint, fromPen);
|
|
110
|
+
const tokens = agentCmd.split(/\s+/).map((t) => (t.includes("{prompt}") ? prompt : t)).filter(Boolean);
|
|
111
|
+
const [cmd, ...args] = tokens;
|
|
112
|
+
const finalArgs = args.map((a) => a.replace("{prompt}", prompt));
|
|
113
|
+
console.log(chalk.cyan(`\nDriving ${findingId} → ${cmd} (agent's own credits; OpenPitStop verifies the result)\n`));
|
|
114
|
+
console.log(chalk.dim(prompt.split("\n").slice(0, 4).join("\n") + "\n"));
|
|
115
|
+
let agentCode = -1;
|
|
116
|
+
try {
|
|
117
|
+
const sub = execa(cmd, finalArgs, {
|
|
118
|
+
cwd: repo,
|
|
119
|
+
stdout: "inherit",
|
|
120
|
+
stderr: "inherit",
|
|
121
|
+
reject: false,
|
|
122
|
+
timeout: 30 * 60 * 1000,
|
|
123
|
+
windowsHide: true,
|
|
124
|
+
});
|
|
125
|
+
agentCode = (await sub).exitCode ?? -1;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.log(chalk.red(`agent could not be started: ${err.message}`));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.log(chalk.dim(`\nagent exited with code ${agentCode} — verifying the result...\n`));
|
|
132
|
+
if (fromPen) {
|
|
133
|
+
const r = await repro(repo, findingId);
|
|
134
|
+
const pass = r.status === "generated-and-ran" && r.ran?.passed === true;
|
|
135
|
+
const paint = pass ? chalk.green : chalk.red;
|
|
136
|
+
const why = pass
|
|
137
|
+
? "repro now PASSES — the runtime hypothesis is unproven, the fix holds."
|
|
138
|
+
: r.status === "generated-and-ran"
|
|
139
|
+
? "repro still FAILS — the bug is live; the agent did not address it."
|
|
140
|
+
: `repro could not run: ${r.reason ?? r.status}`;
|
|
141
|
+
const v = await runVerify(repo);
|
|
142
|
+
console.log(boxen(`${paint(pass ? "VERIFIED — repro now PASSES (bug addressed)" : "NOT VERIFIED — repro still FAILS (bug live)")}\n\n` +
|
|
143
|
+
`${why}\n\n` +
|
|
144
|
+
`test: ${r.file ?? "?"} · ${pass ? "PASS" : "FAIL"}\n` +
|
|
145
|
+
`verify (static gate, context only): score ${v.currentScore.score}/100 (${v.currentScore.grade}) vs baseline ${v.baselineScore.score}/100 (${v.baselineScore.grade}) · delta ${v.scoreDelta >= 0 ? "+" : ""}${v.scoreDelta} · integrity ${v.integrity.verdict}`, {
|
|
146
|
+
title: ` PITSTOP — Drive ${findingId} `,
|
|
147
|
+
titleAlignment: "center",
|
|
148
|
+
borderStyle: "round",
|
|
149
|
+
padding: 1,
|
|
150
|
+
borderColor: pass ? "green" : "red",
|
|
151
|
+
}));
|
|
152
|
+
process.exitCode = pass ? 0 : 1;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const v = await runVerify(repo);
|
|
156
|
+
const paint = v.exitCode === 0 ? chalk.green : chalk.red;
|
|
157
|
+
console.log(boxen(`${paint(v.exitCode === 0 ? "VERIFIED — pitstop verify is clean" : `NOT VERIFIED — pitstop verify exited ${v.exitCode}`)}\n\n` +
|
|
158
|
+
`score: ${v.currentScore.score}/100 (${v.currentScore.grade}) vs baseline ${v.baselineScore.score}/100 (${v.baselineScore.grade}) · delta ${v.scoreDelta >= 0 ? "+" : ""}${v.scoreDelta}\n` +
|
|
159
|
+
`integrity: ${v.integrity.verdict} · stale baseline: ${v.stale ? "yes" : "no"}\n` +
|
|
160
|
+
(v.exitCode !== 0 ? `\nnext: \`pitstop verify\` shows the failing categories — drive it again or fix by hand.` : ""), {
|
|
161
|
+
title: ` PITSTOP — Drive ${findingId} `,
|
|
162
|
+
titleAlignment: "center",
|
|
163
|
+
borderStyle: "round",
|
|
164
|
+
padding: 1,
|
|
165
|
+
borderColor: v.exitCode === 0 ? "green" : "red",
|
|
166
|
+
}));
|
|
167
|
+
process.exitCode = v.exitCode === 0 ? 0 : 1;
|
|
168
|
+
});
|
|
169
|
+
export default drive;
|
|
170
|
+
//# sourceMappingURL=drive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drive.js","sourceRoot":"","sources":["../../src/commands/drive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;;;;;;;GAWG;AAEH,SAAS,aAAa,CACpB,IAAY,EACZ,OAA0H,EAC1H,SAAiB,EACjB,OAAgB;IAEhB,OAAO;QACL,kEAAkE,IAAI,GAAG;QACzE,EAAE;QACF,YAAY,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,cAAc,OAAO,CAAC,QAAQ,GAAG;QAC1F,gBAAgB,OAAO,CAAC,WAAW,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;QACxF,EAAE;QACF,6CAA6C;QAC7C,mDAAmD,OAAO,CAAC,EAAE,qCAAqC;QAClG,8EAA8E;QAC9E,SAAS;QACT,yFAAyF;QACzF,6CAA6C;QAC7C,OAAO;YACL,CAAC,CAAC,+GAA+G;YACjH,CAAC,CAAC,iGAAiG;QACrG,mEAAmE;QACnE,EAAE;QACF,wFAAwF;KACzF,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KACtC,WAAW,CACV,wFAAwF;IACtF,mHAAmH,CACtH;KACA,QAAQ,CAAC,cAAc,EAAE,qDAAqD,CAAC;KAC/E,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,CAAC;KAC3C,MAAM,CAAC,eAAe,EAAE,gEAAgE,CAAC;KACzF,MAAM,CACL,KAAK,EAAE,SAAiB,EAAE,OAAe,EAAE,OAA2B,EAAE,EAAE;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,OAAO,GAA6H,IAAI,CAAC;IAC7I,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,GAAG;YACR,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC;QACF,SAAS,GAAG,kCAAkC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,6BAA6B,CAAC;IAC5G,CAAC;SAAM,IAAI,GAAG,EAAE,CAAC;QACf,MAAM,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7C,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,GAAG;gBACR,EAAE,EAAE,EAAE,CAAC,EAAE;gBACT,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,QAAQ,EAAE,EAAE,CAAC,QAAQ;gBACrB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,WAAW,EAAE,EAAE,CAAC,KAAK;gBACrB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,IAAI,EAAE,EAAE,CAAC,IAAI;aACd,CAAC;YACF,SAAS,GAAG,EAAE,CAAC,MAAM;gBACnB,CAAC,CAAC,gCAAgC,EAAE,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE;gBACtE,CAAC,CAAC,8GAA8G,CAAC;QACrH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,SAAS,sDAAsD,CAAC,CAAC,CAAC;QACzG,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;IAClE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CACT,KAAK,CACH,iBAAiB,SAAS,MAAM;YAC9B,GAAG,KAAK,CAAC,MAAM,CAAC,6BAA6B,CAAC,MAAM;YACpD,sFAAsF;YACtF,aAAa;YACb,oCAAoC;YACpC,wCAAwC;YACxC,qCAAqC,EACvC;YACE,KAAK,EAAE,mBAAmB;YAC1B,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,OAAO;YACpB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,QAAQ;SACtB,CACF,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvG,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAEjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,SAAS,MAAM,GAAG,2DAA2D,CAAC,CAAC,CAAC;IACpH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAEzE,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE;YAChC,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;YACvB,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,+BAAgC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAChF,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,SAAS,8BAA8B,CAAC,CAAC,CAAC;IAE5F,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,KAAK,mBAAmB,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAI;YACd,CAAC,CAAC,uEAAuE;YACzE,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,mBAAmB;gBAChC,CAAC,CAAC,oEAAoE;gBACtE,CAAC,CAAC,wBAAwB,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CACT,KAAK,CACH,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC,6CAA6C,CAAC,MAAM;YAClH,GAAG,GAAG,MAAM;YACZ,SAAS,CAAC,CAAC,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI;YACtD,6CAA6C,CAAC,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY,CAAC,KAAK,iBAAiB,CAAC,CAAC,aAAa,CAAC,KAAK,SAAS,CAAC,CAAC,aAAa,CAAC,KAAK,aAAa,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,UAAU,gBAAgB,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,EACjQ;YACE,KAAK,EAAE,oBAAoB,SAAS,GAAG;YACvC,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,OAAO;YACpB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;SACpC,CACF,CACF,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;IACzD,OAAO,CAAC,GAAG,CACT,KAAK,CACH,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,QAAQ,EAAE,CAAC,MAAM;QAC5H,UAAU,CAAC,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY,CAAC,KAAK,iBAAiB,CAAC,CAAC,aAAa,CAAC,KAAK,SAAS,CAAC,CAAC,aAAa,CAAC,KAAK,aAAa,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,UAAU,IAAI;QAC3L,cAAc,CAAC,CAAC,SAAS,CAAC,OAAO,sBAAsB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI;QACjF,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,0FAA0F,CAAC,CAAC,CAAC,EAAE,CAAC,EACtH;QACE,KAAK,EAAE,oBAAoB,SAAS,GAAG;QACvC,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,OAAO;QACpB,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;KAChD,CACF,CACF,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC,CACF,CAAC;AAEJ,eAAe,KAAK,CAAC"}
|