@agentvibes/guardrails 0.1.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/README.md +239 -0
- package/candidates/README.md +30 -0
- package/candidates/r10-as-cast.yml +24 -0
- package/candidates/r1d-scattered-numeric-const-in-component.yml +13 -0
- package/candidates/r2-helper-fn-in-tsx.yml +22 -0
- package/candidates/r3-hooks-outside-layout.yml +21 -0
- package/candidates/r6-jsx-comment-smell.yml +14 -0
- package/candidates/r7-raw-hsl-outside-tokens.yml +17 -0
- package/candidates/r8-inline-style-prop.yml +13 -0
- package/candidates/r9-optional-store-arg.yml +24 -0
- package/configs/biome.json +96 -0
- package/configs/tsconfig.base.json +21 -0
- package/dist/astGrep.d.ts +16 -0
- package/dist/astGrep.js +48 -0
- package/dist/astGrep.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +138 -0
- package/dist/cli.js.map +1 -0
- package/dist/deploy.d.ts +1 -0
- package/dist/deploy.js +34 -0
- package/dist/deploy.js.map +1 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.js +102 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fileWalk.d.ts +2 -0
- package/dist/fileWalk.js +45 -0
- package/dist/fileWalk.js.map +1 -0
- package/dist/findings.d.ts +12 -0
- package/dist/findings.js +8 -0
- package/dist/findings.js.map +1 -0
- package/dist/gateRed.d.ts +1 -0
- package/dist/gateRed.js +93 -0
- package/dist/gateRed.js.map +1 -0
- package/dist/gitDiff.d.ts +18 -0
- package/dist/gitDiff.js +90 -0
- package/dist/gitDiff.js.map +1 -0
- package/dist/hookPostedit.d.ts +1 -0
- package/dist/hookPostedit.js +117 -0
- package/dist/hookPostedit.js.map +1 -0
- package/dist/hookStop.d.ts +1 -0
- package/dist/hookStop.js +56 -0
- package/dist/hookStop.js.map +1 -0
- package/dist/hookStopTest.d.ts +1 -0
- package/dist/hookStopTest.js +71 -0
- package/dist/hookStopTest.js.map +1 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.js +172 -0
- package/dist/init.js.map +1 -0
- package/dist/initTest.d.ts +1 -0
- package/dist/initTest.js +88 -0
- package/dist/initTest.js.map +1 -0
- package/dist/iterate.d.ts +9 -0
- package/dist/iterate.js +79 -0
- package/dist/iterate.js.map +1 -0
- package/dist/iterateTest.d.ts +1 -0
- package/dist/iterateTest.js +91 -0
- package/dist/iterateTest.js.map +1 -0
- package/dist/leakPatterns.d.ts +5 -0
- package/dist/leakPatterns.js +22 -0
- package/dist/leakPatterns.js.map +1 -0
- package/dist/leaks.d.ts +8 -0
- package/dist/leaks.js +211 -0
- package/dist/leaks.js.map +1 -0
- package/dist/leaksRed.d.ts +1 -0
- package/dist/leaksRed.js +62 -0
- package/dist/leaksRed.js.map +1 -0
- package/dist/metrics.d.ts +10 -0
- package/dist/metrics.js +113 -0
- package/dist/metrics.js.map +1 -0
- package/dist/metricsAggregate.d.ts +5 -0
- package/dist/metricsAggregate.js +78 -0
- package/dist/metricsAggregate.js.map +1 -0
- package/dist/metricsBaseline.d.ts +21 -0
- package/dist/metricsBaseline.js +88 -0
- package/dist/metricsBaseline.js.map +1 -0
- package/dist/metricsCollect.d.ts +8 -0
- package/dist/metricsCollect.js +295 -0
- package/dist/metricsCollect.js.map +1 -0
- package/dist/metricsCycle.d.ts +1 -0
- package/dist/metricsCycle.js +79 -0
- package/dist/metricsCycle.js.map +1 -0
- package/dist/metricsTypes.d.ts +102 -0
- package/dist/metricsTypes.js +2 -0
- package/dist/metricsTypes.js.map +1 -0
- package/dist/packagePaths.d.ts +7 -0
- package/dist/packagePaths.js +11 -0
- package/dist/packagePaths.js.map +1 -0
- package/dist/pluginApi.d.ts +24 -0
- package/dist/pluginApi.js +12 -0
- package/dist/pluginApi.js.map +1 -0
- package/dist/pluginResolve.d.ts +6 -0
- package/dist/pluginResolve.js +48 -0
- package/dist/pluginResolve.js.map +1 -0
- package/dist/screenScope.d.ts +21 -0
- package/dist/screenScope.js +81 -0
- package/dist/screenScope.js.map +1 -0
- package/dist/screenScopeTest.d.ts +1 -0
- package/dist/screenScopeTest.js +116 -0
- package/dist/screenScopeTest.js.map +1 -0
- package/dist/severity.d.ts +9 -0
- package/dist/severity.js +62 -0
- package/dist/severity.js.map +1 -0
- package/dist/severityTest.d.ts +1 -0
- package/dist/severityTest.js +63 -0
- package/dist/severityTest.js.map +1 -0
- package/dist/sourceLines.d.ts +9 -0
- package/dist/sourceLines.js +52 -0
- package/dist/sourceLines.js.map +1 -0
- package/dist/structure.d.ts +2 -0
- package/dist/structure.js +98 -0
- package/dist/structure.js.map +1 -0
- package/dist/testRules.d.ts +1 -0
- package/dist/testRules.js +285 -0
- package/dist/testRules.js.map +1 -0
- package/dist/textGrep.d.ts +2 -0
- package/dist/textGrep.js +52 -0
- package/dist/textGrep.js.map +1 -0
- package/dist/tomlTable.d.ts +2 -0
- package/dist/tomlTable.js +26 -0
- package/dist/tomlTable.js.map +1 -0
- package/dist/toolResolve.d.ts +18 -0
- package/dist/toolResolve.js +50 -0
- package/dist/toolResolve.js.map +1 -0
- package/dist/verify.d.ts +3 -0
- package/dist/verify.js +49 -0
- package/dist/verify.js.map +1 -0
- package/dist/verifyDiff.d.ts +18 -0
- package/dist/verifyDiff.js +126 -0
- package/dist/verifyDiff.js.map +1 -0
- package/dist/workspaces.d.ts +6 -0
- package/dist/workspaces.js +84 -0
- package/dist/workspaces.js.map +1 -0
- package/mise.toml +9 -0
- package/package.json +62 -0
- package/rules/__fixtures__/as-any-escape/bad.ts +8 -0
- package/rules/__fixtures__/as-any-escape/good.ts +14 -0
- package/rules/__fixtures__/catch-empty/bad.ts +7 -0
- package/rules/__fixtures__/catch-empty/good.ts +14 -0
- package/rules/__fixtures__/direct-store-import/bad.tsx +23 -0
- package/rules/__fixtures__/direct-store-import/good.tsx +34 -0
- package/rules/__fixtures__/direct-store-import/showcase/ignored.tsx +9 -0
- package/rules/__fixtures__/discriminator-ternary/bad.tsx +10 -0
- package/rules/__fixtures__/discriminator-ternary/good.tsx +23 -0
- package/rules/__fixtures__/hardcoded-url-in-component/components/bad.tsx +8 -0
- package/rules/__fixtures__/hardcoded-url-in-component/components/good.tsx +14 -0
- package/rules/__fixtures__/inline-map-row/bad.tsx +72 -0
- package/rules/__fixtures__/inline-map-row/good.tsx +51 -0
- package/rules/__fixtures__/instanceof-map-set/bad.ts +7 -0
- package/rules/__fixtures__/instanceof-map-set/good.ts +8 -0
- package/rules/__fixtures__/literal-union-in-component/components/bad.ts +8 -0
- package/rules/__fixtures__/literal-union-in-component/components/good.ts +23 -0
- package/rules/__fixtures__/missing-observer/bad.tsx +66 -0
- package/rules/__fixtures__/missing-observer/good.tsx +69 -0
- package/rules/__fixtures__/no-local-kit-clone/bad.ts +30 -0
- package/rules/__fixtures__/no-local-kit-clone/bad.tsx +30 -0
- package/rules/__fixtures__/no-local-kit-clone/good.ts +31 -0
- package/rules/__fixtures__/no-local-kit-clone/good.tsx +31 -0
- package/rules/__fixtures__/non-exhaustive-match/bad.ts +8 -0
- package/rules/__fixtures__/non-exhaustive-match/good.ts +14 -0
- package/rules/__fixtures__/page-store-bare-usememo/bad.ts +15 -0
- package/rules/__fixtures__/page-store-bare-usememo/bad.tsx +13 -0
- package/rules/__fixtures__/page-store-bare-usememo/good.ts +17 -0
- package/rules/__fixtures__/page-store-bare-usememo/good.tsx +14 -0
- package/rules/__fixtures__/screen-file-styling/bad.screen.tsx +10 -0
- package/rules/__fixtures__/screen-file-styling/good.screen.tsx +8 -0
- package/rules/__fixtures__/state-loading-boolean-shape/bad.ts +36 -0
- package/rules/__fixtures__/state-loading-boolean-shape/bad.tsx +36 -0
- package/rules/__fixtures__/state-loading-boolean-shape/good.ts +44 -0
- package/rules/__fixtures__/state-loading-boolean-shape/good.tsx +44 -0
- package/rules/__fixtures__/store-async-method/bad.ts +37 -0
- package/rules/__fixtures__/store-async-method/bad.tsx +37 -0
- package/rules/__fixtures__/store-async-method/good.ts +73 -0
- package/rules/__fixtures__/store-async-method/good.tsx +73 -0
- package/rules/__fixtures__/store-context-provider/bad.ts +16 -0
- package/rules/__fixtures__/store-context-provider/bad.tsx +24 -0
- package/rules/__fixtures__/store-context-provider/good.ts +13 -0
- package/rules/__fixtures__/store-context-provider/good.tsx +11 -0
- package/rules/__fixtures__/store-delegation-getter/bad.ts +30 -0
- package/rules/__fixtures__/store-delegation-getter/bad.tsx +30 -0
- package/rules/__fixtures__/store-delegation-getter/good.ts +65 -0
- package/rules/__fixtures__/store-delegation-getter/good.tsx +65 -0
- package/rules/__fixtures__/store-new-map/bad.ts +22 -0
- package/rules/__fixtures__/store-new-map/bad.tsx +22 -0
- package/rules/__fixtures__/store-new-map/good.ts +41 -0
- package/rules/__fixtures__/store-new-map/good.tsx +41 -0
- package/rules/__fixtures__/store-no-runinaction/bad.ts +67 -0
- package/rules/__fixtures__/store-no-runinaction/bad.tsx +67 -0
- package/rules/__fixtures__/store-no-runinaction/good.ts +39 -0
- package/rules/__fixtures__/store-no-runinaction/good.tsx +39 -0
- package/rules/__fixtures__/store-no-setinterval-poll/bad.ts +19 -0
- package/rules/__fixtures__/store-no-setinterval-poll/bad.tsx +19 -0
- package/rules/__fixtures__/store-no-setinterval-poll/good.ts +29 -0
- package/rules/__fixtures__/store-no-setinterval-poll/good.tsx +29 -0
- package/rules/__fixtures__/ts-pattern-dangling-match/bad.ts +20 -0
- package/rules/__fixtures__/ts-pattern-dangling-match/good.ts +35 -0
- package/rules/__fixtures__/ui-imports-app-store/src/ui/bad.tsx +13 -0
- package/rules/__fixtures__/ui-imports-app-store/src/ui/good.tsx +23 -0
- package/rules/__fixtures__/view-file-logic/bad.view.tsx +16 -0
- package/rules/__fixtures__/view-file-logic/good.view.tsx +7 -0
- package/rules/__fixtures__/view-imports-store/bad.view.tsx +13 -0
- package/rules/__fixtures__/view-imports-store/good.view.tsx +23 -0
- package/rules/__fixtures__/zod-optional-nullable/bad.ts +10 -0
- package/rules/__fixtures__/zod-optional-nullable/bad.tsx +17 -0
- package/rules/__fixtures__/zod-optional-nullable/good.ts +16 -0
- package/rules/__fixtures__/zod-optional-nullable/good.tsx +20 -0
- package/rules/as-any-escape-tsx.yml +53 -0
- package/rules/as-any-escape.yml +29 -0
- package/rules/catch-empty-tsx.yml +55 -0
- package/rules/catch-empty.yml +31 -0
- package/rules/classname-not-composed.yml +28 -0
- package/rules/classname-visual-identity.yml +34 -0
- package/rules/demo-mode-by-default-ts.yml +76 -0
- package/rules/demo-mode-by-default.yml +54 -0
- package/rules/direct-store-import.yml +118 -0
- package/rules/discriminator-ternary.yml +70 -0
- package/rules/hardcoded-url-in-component.yml +44 -0
- package/rules/inline-map-row.yml +107 -0
- package/rules/instanceof-map-set-tsx.yml +46 -0
- package/rules/instanceof-map-set.yml +22 -0
- package/rules/json-roundtrip.yml +9 -0
- package/rules/jsx-cond-and.yml +16 -0
- package/rules/jsx-ternary.yml +35 -0
- package/rules/kind-if-without-match.yml +13 -0
- package/rules/literal-union-in-component.yml +71 -0
- package/rules/match-bool-to-null.yml +15 -0
- package/rules/missing-observer.yml +189 -0
- package/rules/mobx-effect-observable-dep.yml +23 -0
- package/rules/mobx-effect-store-write.yml +21 -0
- package/rules/mobx-usestate-from-store.yml +20 -0
- package/rules/no-local-kit-clone-tsx.yml +104 -0
- package/rules/no-local-kit-clone.yml +93 -0
- package/rules/non-exhaustive-match.yml +34 -0
- package/rules/page-store-bare-usememo-tsx.yml +47 -0
- package/rules/page-store-bare-usememo.yml +43 -0
- package/rules/screen-file-styling.yml +29 -0
- package/rules/silent-default-return.yml +17 -0
- package/rules/state-loading-boolean-shape-tsx.yml +102 -0
- package/rules/state-loading-boolean-shape.yml +91 -0
- package/rules/store-async-method-tsx.yml +108 -0
- package/rules/store-async-method.yml +98 -0
- package/rules/store-context-provider-tsx.yml +68 -0
- package/rules/store-context-provider.yml +78 -0
- package/rules/store-delegation-getter-tsx.yml +104 -0
- package/rules/store-delegation-getter.yml +93 -0
- package/rules/store-new-map-tsx.yml +84 -0
- package/rules/store-new-map.yml +73 -0
- package/rules/store-no-runinaction-tsx.yml +71 -0
- package/rules/store-no-runinaction.yml +60 -0
- package/rules/store-no-setinterval-poll-tsx.yml +62 -0
- package/rules/store-no-setinterval-poll.yml +51 -0
- package/rules/ts-pattern-dangling-match-tsx.yml +41 -0
- package/rules/ts-pattern-dangling-match.yml +63 -0
- package/rules/ui-imports-app-store.yml +49 -0
- package/rules/view-file-logic.yml +53 -0
- package/rules/view-imports-store.yml +64 -0
- package/rules/zod-optional-nullable-tsx.yml +56 -0
- package/rules/zod-optional-nullable.yml +29 -0
- package/sgconfig.yml +8 -0
- package/structure/rules/component-decl.yml +48 -0
- package/structure/sgconfig.yml +7 -0
package/dist/iterate.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
function readTask(task) {
|
|
6
|
+
if (existsSync(task))
|
|
7
|
+
return readFileSync(task, "utf8");
|
|
8
|
+
return task;
|
|
9
|
+
}
|
|
10
|
+
const FEEDBACK_HEADER = "The verification gate FAILED on your previous attempt. Fix ONLY the findings below — do not refactor anything the gate did not name, do not weaken any rule, config, or baseline. Findings:";
|
|
11
|
+
export function runIterate(opts) {
|
|
12
|
+
if (opts.task === undefined || opts.cmd === undefined) {
|
|
13
|
+
console.error("guardrails iterate: --task <file-or-text> and --cmd '<runner command>' are required. See README 'Iterate harness'.");
|
|
14
|
+
return 2;
|
|
15
|
+
}
|
|
16
|
+
const taskText = readTask(opts.task);
|
|
17
|
+
const gateCmd = opts.gate ?? `node ${opts.cliPath} verify-diff`;
|
|
18
|
+
const dir = mkdtempSync(join(tmpdir(), "guardrails-iterate-"));
|
|
19
|
+
const attempts = [];
|
|
20
|
+
let feedback = "";
|
|
21
|
+
for (let attempt = 1; attempt <= opts.max; attempt++) {
|
|
22
|
+
const prompt = feedback === "" ? taskText : `${taskText}\n\n${FEEDBACK_HEADER}\n${feedback}`;
|
|
23
|
+
const promptFile = join(dir, `prompt-${attempt}.md`);
|
|
24
|
+
writeFileSync(promptFile, prompt);
|
|
25
|
+
const t0 = performance.now();
|
|
26
|
+
const agent = spawnSync("bash", ["-c", opts.cmd], {
|
|
27
|
+
input: prompt,
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
env: {
|
|
30
|
+
...process.env,
|
|
31
|
+
GUARDRAILS_PROMPT_FILE: promptFile,
|
|
32
|
+
GUARDRAILS_ATTEMPT: String(attempt),
|
|
33
|
+
},
|
|
34
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
35
|
+
});
|
|
36
|
+
const gate = spawnSync("bash", ["-c", gateCmd], {
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
39
|
+
});
|
|
40
|
+
const seconds = Math.round((performance.now() - t0) / 100) / 10;
|
|
41
|
+
const gateOut = `${gate.stdout}${gate.stderr}`;
|
|
42
|
+
const gateTail = gateOut
|
|
43
|
+
.split("\n")
|
|
44
|
+
.filter((l) => l !== "")
|
|
45
|
+
.slice(-30)
|
|
46
|
+
.join("\n");
|
|
47
|
+
attempts.push({
|
|
48
|
+
attempt,
|
|
49
|
+
agentExit: agent.status ?? -1,
|
|
50
|
+
gateExit: gate.status ?? -1,
|
|
51
|
+
gateTail,
|
|
52
|
+
seconds,
|
|
53
|
+
});
|
|
54
|
+
console.error(`guardrails iterate: attempt ${attempt}/${opts.max} — agent exit ${agent.status}, gate exit ${gate.status} (${seconds}s)`);
|
|
55
|
+
// ast-grep-ignore: kind-if-without-match -- gate.status is spawnSync's numeric exit code (number | null), not a discriminated-union tag
|
|
56
|
+
if (gate.status === 0) {
|
|
57
|
+
finish(opts.json, attempts, true);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
feedback = gateTail;
|
|
61
|
+
}
|
|
62
|
+
finish(opts.json, attempts, false);
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
function finish(json, attempts, green) {
|
|
66
|
+
if (json) {
|
|
67
|
+
console.log(JSON.stringify({ command: "iterate", green, attempts }, null, 2));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.log(`guardrails iterate: ${green ? "GREEN" : "still red"} after ${attempts.length} attempt(s)`);
|
|
71
|
+
for (const a of attempts) {
|
|
72
|
+
console.log(` attempt ${a.attempt}: agent exit ${a.agentExit}, gate exit ${a.gateExit}, ${a.seconds}s`);
|
|
73
|
+
}
|
|
74
|
+
if (!green) {
|
|
75
|
+
const last = attempts[attempts.length - 1];
|
|
76
|
+
console.log(`last gate output:\n${last?.gateTail ?? ""}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=iterate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../src/iterate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { packageRoot } from "./packagePaths.js";
|
|
6
|
+
// Fixtures for the iterate harness, with a fake "agent" instead of an LLM:
|
|
7
|
+
// 1. converging agent — writes a violation on attempt 1, fixes it on
|
|
8
|
+
// attempt 2 (because the feedback loop told it to) → GREEN in 2 attempts
|
|
9
|
+
// 2. stubborn agent — keeps the violation every attempt → exit 1 at --max
|
|
10
|
+
// Also pins the feedback contract: attempt 2's prompt file must contain the
|
|
11
|
+
// gate's findings from attempt 1.
|
|
12
|
+
function iterate(cwd, agentScript, max) {
|
|
13
|
+
const res = spawnSync("node", [
|
|
14
|
+
join(packageRoot, "dist", "cli.js"),
|
|
15
|
+
"iterate",
|
|
16
|
+
"--task",
|
|
17
|
+
"Implement the widget per spec.",
|
|
18
|
+
"--cmd",
|
|
19
|
+
`node ${agentScript}`,
|
|
20
|
+
"--max",
|
|
21
|
+
String(max),
|
|
22
|
+
], { cwd, encoding: "utf8" });
|
|
23
|
+
if (res.error)
|
|
24
|
+
throw res.error;
|
|
25
|
+
return { status: res.status ?? -1, out: `${res.stdout}${res.stderr}` };
|
|
26
|
+
}
|
|
27
|
+
function expectStep(step, ok, detail) {
|
|
28
|
+
if (!ok) {
|
|
29
|
+
console.error(`FAIL iterate: ${step}\n${detail}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
console.log(` ok iterate: ${step}`);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
function gitRepo(dir) {
|
|
36
|
+
const git = (...args) => {
|
|
37
|
+
const r = spawnSync("git", ["-C", dir, ...args], { encoding: "utf8" });
|
|
38
|
+
if (r.status !== 0)
|
|
39
|
+
throw new Error(`git ${args.join(" ")}: ${r.stderr}`);
|
|
40
|
+
};
|
|
41
|
+
git("init", "-q");
|
|
42
|
+
writeFileSync(join(dir, "base.ts"), "export const ok = 1;\n");
|
|
43
|
+
git("add", "-A");
|
|
44
|
+
git("-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "init");
|
|
45
|
+
}
|
|
46
|
+
const CONVERGING_AGENT = `
|
|
47
|
+
const fs = require("node:fs");
|
|
48
|
+
const attempt = Number(process.env.GUARDRAILS_ATTEMPT);
|
|
49
|
+
const prompt = fs.readFileSync(process.env.GUARDRAILS_PROMPT_FILE, "utf8");
|
|
50
|
+
if (attempt === 1) {
|
|
51
|
+
fs.writeFileSync("widget.ts", "export const w = (x: unknown) => x as any;\\n");
|
|
52
|
+
} else {
|
|
53
|
+
// The harness contract: the retry prompt carries the gate's findings.
|
|
54
|
+
if (!prompt.includes("as-any-escape")) { console.error("no feedback in prompt"); process.exit(9); }
|
|
55
|
+
fs.writeFileSync("widget.ts", "export const w = (x: number): number => x;\\n");
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
const STUBBORN_AGENT = `
|
|
59
|
+
const fs = require("node:fs");
|
|
60
|
+
fs.writeFileSync("widget.ts", "export const w = (x: unknown) => x as any;\\n");
|
|
61
|
+
`;
|
|
62
|
+
function main() {
|
|
63
|
+
const base = mkdtempSync(join(tmpdir(), "guardrails-iterate-test-"));
|
|
64
|
+
try {
|
|
65
|
+
const conv = join(base, "conv");
|
|
66
|
+
const stub = join(base, "stub");
|
|
67
|
+
for (const d of [conv, stub]) {
|
|
68
|
+
spawnSync("mkdir", ["-p", d]);
|
|
69
|
+
gitRepo(d);
|
|
70
|
+
}
|
|
71
|
+
writeFileSync(join(conv, "agent.cjs"), CONVERGING_AGENT);
|
|
72
|
+
writeFileSync(join(stub, "agent.cjs"), STUBBORN_AGENT);
|
|
73
|
+
const green = iterate(conv, "agent.cjs", 4);
|
|
74
|
+
if (!expectStep("converging agent → green on attempt 2 via feedback", green.status === 0 && green.out.includes("GREEN after 2 attempt(s)"), green.out)) {
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
const red = iterate(stub, "agent.cjs", 2);
|
|
78
|
+
if (!expectStep("stubborn agent → exit 1 at --max with last findings", red.status === 1 &&
|
|
79
|
+
red.out.includes("still red after 2 attempt(s)") &&
|
|
80
|
+
red.out.includes("as-any-escape"), red.out)) {
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
console.log("iterate fixtures passed");
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
rmSync(base, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
process.exit(main());
|
|
91
|
+
//# sourceMappingURL=iterateTest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterateTest.js","sourceRoot":"","sources":["../src/iterateTest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Built-in patterns for `guardrails leaks` — GENERIC credential shapes only.
|
|
2
|
+
// Deliberately NO infrastructure markers here: a list of private hostnames,
|
|
3
|
+
// org names, or subnets is itself a fact about the private topology (spec
|
|
4
|
+
// layer A″: code carries mechanisms, config carries facts), so house marker
|
|
5
|
+
// lists reach the scanner through `.guardrails/leaks.txt`, a
|
|
6
|
+
// `[leaks] patterns_file =` manifest entry, or a plugin's `leakPatterns()` —
|
|
7
|
+
// never through this public package.
|
|
8
|
+
export const LEAK_PATTERNS = [
|
|
9
|
+
{ id: "github-token", regex: /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}\b/ },
|
|
10
|
+
{ id: "github-fine-grained-token", regex: /\bgithub_pat_[A-Za-z0-9_]{22,}\b/ },
|
|
11
|
+
{ id: "anthropic-key", regex: /\bsk-ant-[A-Za-z0-9-]{10,}\b/ },
|
|
12
|
+
{ id: "openai-key", regex: /\bsk-proj-[A-Za-z0-9_-]{20,}\b/ },
|
|
13
|
+
{ id: "aws-access-key", regex: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
14
|
+
{ id: "npm-token", regex: /\bnpm_[A-Za-z0-9]{36}\b/ },
|
|
15
|
+
{ id: "gitlab-token", regex: /\bglpat-[A-Za-z0-9_-]{20}\b/ },
|
|
16
|
+
{ id: "slack-token", regex: /\bxox[bap]-[A-Za-z0-9-]{10,}\b/ },
|
|
17
|
+
{
|
|
18
|
+
id: "private-key-block",
|
|
19
|
+
regex: /-----BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY( BLOCK)?-----/,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
//# sourceMappingURL=leakPatterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leakPatterns.js","sourceRoot":"","sources":["../src/leakPatterns.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,4EAA4E;AAC5E,6DAA6D;AAC7D,6EAA6E;AAC7E,qCAAqC;AAOrC,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,2CAA2C,EAAE;IAC1E,EAAE,EAAE,EAAE,2BAA2B,EAAE,KAAK,EAAE,kCAAkC,EAAE;IAC9E,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,8BAA8B,EAAE;IAC9D,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,gCAAgC,EAAE;IAC7D,EAAE,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACvD,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,yBAAyB,EAAE;IACrD,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,6BAA6B,EAAE;IAC5D,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,gCAAgC,EAAE;IAC9D;QACE,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,+DAA+D;KACvE;CACF,CAAC"}
|
package/dist/leaks.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type LeakPattern } from "./leakPatterns.js";
|
|
2
|
+
export interface LeakFinding {
|
|
3
|
+
file: string;
|
|
4
|
+
line: number;
|
|
5
|
+
patternId: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function scanForLeaks(targets: string[], patterns: LeakPattern[], exemptFiles: Set<string>): LeakFinding[];
|
|
8
|
+
export declare function runLeaks(targets: string[], json: boolean, patternsFile?: string): Promise<number>;
|
package/dist/leaks.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { LEAK_PATTERNS } from "./leakPatterns.js";
|
|
5
|
+
import { loadPlugin, resolvePluginName } from "./pluginResolve.js";
|
|
6
|
+
import { readTomlTable } from "./tomlTable.js";
|
|
7
|
+
import { tryResolveTool } from "./toolResolve.js";
|
|
8
|
+
// `guardrails leaks [paths]` — the public/private boundary gate (spec layer
|
|
9
|
+
// A″). The package ships only GENERIC credential patterns; house marker lists
|
|
10
|
+
// (a fact about a private topology) load at runtime from, in order:
|
|
11
|
+
// 1. built-ins (leakPatterns.ts)
|
|
12
|
+
// 2. .guardrails/leaks.txt in the target repo
|
|
13
|
+
// 3. the file named by `patterns_file =` under [leaks] in .agentvibes/project.toml
|
|
14
|
+
// 4. the project's guardrails plugin, via its optional leakPatterns() hook
|
|
15
|
+
// Exit 1 on any hit; exit 2 on an unloadable pattern (a gate that silently
|
|
16
|
+
// drops a bad pattern is a coverage hole nobody can see).
|
|
17
|
+
const SKIP_DIRS = new Set([
|
|
18
|
+
"node_modules",
|
|
19
|
+
"dist",
|
|
20
|
+
".git",
|
|
21
|
+
"coverage",
|
|
22
|
+
".turbo",
|
|
23
|
+
"build",
|
|
24
|
+
".next",
|
|
25
|
+
".claude",
|
|
26
|
+
]);
|
|
27
|
+
class PatternLoadError extends Error {
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pattern-file format: one pattern per line, `#` comments and blanks skipped.
|
|
31
|
+
* A line is `<id> <regex>` (split on first whitespace); a single-token line is
|
|
32
|
+
* a bare regex with a generated id.
|
|
33
|
+
*/
|
|
34
|
+
function parsePatternFile(path) {
|
|
35
|
+
const patterns = [];
|
|
36
|
+
const lines = readFileSync(path, "utf8").split("\n");
|
|
37
|
+
lines.forEach((raw, i) => {
|
|
38
|
+
const line = raw.trim();
|
|
39
|
+
if (line === "" || line.startsWith("#"))
|
|
40
|
+
return;
|
|
41
|
+
const sp = line.search(/\s/);
|
|
42
|
+
const id = sp > 0 ? line.slice(0, sp) : `pattern-${i + 1}`;
|
|
43
|
+
const source = sp > 0 ? line.slice(sp).trim() : line;
|
|
44
|
+
try {
|
|
45
|
+
patterns.push({ id, regex: new RegExp(source) });
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new PatternLoadError(`${path}:${i + 1}: invalid regex '${source}' (${err instanceof Error ? err.message : String(err)})`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return patterns;
|
|
52
|
+
}
|
|
53
|
+
async function loadPatterns(cwd, extraPatternFiles) {
|
|
54
|
+
const patterns = [...LEAK_PATTERNS];
|
|
55
|
+
const sources = [`built-in generic (${LEAK_PATTERNS.length})`];
|
|
56
|
+
const exemptFiles = new Set();
|
|
57
|
+
const fileCandidates = [...extraPatternFiles, join(cwd, ".guardrails", "leaks.txt")];
|
|
58
|
+
const leaksConfig = readTomlTable(join(cwd, ".agentvibes", "project.toml"), "leaks");
|
|
59
|
+
const configured = leaksConfig.patterns_file;
|
|
60
|
+
if (configured !== undefined && configured !== "") {
|
|
61
|
+
fileCandidates.push(isAbsolute(configured) ? configured : join(cwd, configured));
|
|
62
|
+
}
|
|
63
|
+
for (const candidate of fileCandidates) {
|
|
64
|
+
const abs = resolve(candidate);
|
|
65
|
+
if (!existsSync(abs) || exemptFiles.has(abs))
|
|
66
|
+
continue;
|
|
67
|
+
const loaded = parsePatternFile(abs);
|
|
68
|
+
patterns.push(...loaded);
|
|
69
|
+
sources.push(`${abs} (${loaded.length})`);
|
|
70
|
+
exemptFiles.add(abs);
|
|
71
|
+
}
|
|
72
|
+
const pluginName = resolvePluginName(cwd);
|
|
73
|
+
if (pluginName !== undefined) {
|
|
74
|
+
const plugin = await loadPlugin(cwd, pluginName);
|
|
75
|
+
if (plugin.leakPatterns !== undefined) {
|
|
76
|
+
const specs = plugin.leakPatterns();
|
|
77
|
+
for (const spec of specs) {
|
|
78
|
+
try {
|
|
79
|
+
patterns.push({ id: spec.id, regex: new RegExp(spec.pattern) });
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
throw new PatternLoadError(`plugin ${plugin.name} leakPatterns(): invalid regex for '${spec.id}' (${err instanceof Error ? err.message : String(err)})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
sources.push(`plugin ${plugin.name} (${specs.length})`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { patterns, sources, exemptFiles };
|
|
89
|
+
}
|
|
90
|
+
function isTextFile(buf) {
|
|
91
|
+
return !buf.subarray(0, 8192).includes(0);
|
|
92
|
+
}
|
|
93
|
+
// lstat, never following symlinks: a symlinked foreign tree is not this
|
|
94
|
+
// repo's content (fileWalk.ts records the incident that taught this).
|
|
95
|
+
function walk(path, out, exemptFiles) {
|
|
96
|
+
let st;
|
|
97
|
+
try {
|
|
98
|
+
st = lstatSync(path);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (st.isFile()) {
|
|
104
|
+
if (!exemptFiles.has(resolve(path)) && !path.endsWith(".tgz"))
|
|
105
|
+
out.push(path);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!st.isDirectory())
|
|
109
|
+
return;
|
|
110
|
+
for (const entry of readdirSync(path)) {
|
|
111
|
+
if (SKIP_DIRS.has(entry))
|
|
112
|
+
continue;
|
|
113
|
+
walk(join(path, entry), out, exemptFiles);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export function scanForLeaks(targets, patterns, exemptFiles) {
|
|
117
|
+
const files = [];
|
|
118
|
+
for (const t of targets)
|
|
119
|
+
walk(t, files, exemptFiles);
|
|
120
|
+
const findings = [];
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
let buf;
|
|
123
|
+
try {
|
|
124
|
+
buf = readFileSync(file);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!isTextFile(buf))
|
|
130
|
+
continue;
|
|
131
|
+
const lines = buf.toString("utf8").split("\n");
|
|
132
|
+
for (let i = 0; i < lines.length; i++) {
|
|
133
|
+
const line = lines[i];
|
|
134
|
+
if (line === undefined)
|
|
135
|
+
continue;
|
|
136
|
+
for (const p of patterns) {
|
|
137
|
+
if (p.regex.test(line))
|
|
138
|
+
findings.push({ file, line: i + 1, patternId: p.id });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return findings;
|
|
143
|
+
}
|
|
144
|
+
function runGitleaks(target) {
|
|
145
|
+
const tool = tryResolveTool("gitleaks");
|
|
146
|
+
if (tool === undefined) {
|
|
147
|
+
return {
|
|
148
|
+
ran: false,
|
|
149
|
+
ok: true,
|
|
150
|
+
detail: "gitleaks not found (PATH or mise) — pattern scan only. Install: mise use -g gitleaks@latest",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const [cmd, ...prefix] = tool.argv;
|
|
154
|
+
if (cmd === undefined)
|
|
155
|
+
return { ran: false, ok: true, detail: "gitleaks argv empty" };
|
|
156
|
+
const res = spawnSync(cmd, [...prefix, "detect", "--no-git", "--no-banner", "--redact", "--source", target], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
|
157
|
+
if (res.error)
|
|
158
|
+
return { ran: false, ok: true, detail: `gitleaks failed to start: ${res.error.message}` };
|
|
159
|
+
// ast-grep-ignore: kind-if-without-match -- res.status is spawnSync's numeric exit code (number | null), not a discriminated-union tag
|
|
160
|
+
if (res.status === 0)
|
|
161
|
+
return { ran: true, ok: true, detail: `gitleaks ${tool.version}: clean` };
|
|
162
|
+
return {
|
|
163
|
+
ran: true,
|
|
164
|
+
ok: false,
|
|
165
|
+
detail: `gitleaks ${tool.version}: findings\n${res.stdout.slice(0, 4000)}${res.stderr.slice(0, 2000)}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export async function runLeaks(targets, json, patternsFile) {
|
|
169
|
+
const paths = targets.length > 0 ? targets : ["."];
|
|
170
|
+
// --patterns <file>: an EXTRA pattern file for this run — how a public
|
|
171
|
+
// repo's CI scans itself against a house list it must not ship (the list is
|
|
172
|
+
// fetched from a private repo at CI time and never committed here).
|
|
173
|
+
if (patternsFile !== undefined && !existsSync(patternsFile)) {
|
|
174
|
+
console.error(`guardrails leaks: --patterns file not found: ${patternsFile}`);
|
|
175
|
+
return 2;
|
|
176
|
+
}
|
|
177
|
+
let loaded;
|
|
178
|
+
try {
|
|
179
|
+
loaded = await loadPatterns(process.cwd(), patternsFile === undefined ? [] : [patternsFile]);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
console.error(`guardrails leaks: ${err instanceof Error ? err.message : String(err)}`);
|
|
183
|
+
return 2;
|
|
184
|
+
}
|
|
185
|
+
const findings = scanForLeaks(paths, loaded.patterns, loaded.exemptFiles);
|
|
186
|
+
const gitleaks = runGitleaks(paths[0] ?? ".");
|
|
187
|
+
const ok = findings.length === 0 && gitleaks.ok;
|
|
188
|
+
if (json) {
|
|
189
|
+
console.log(JSON.stringify({
|
|
190
|
+
command: "leaks",
|
|
191
|
+
targets: paths,
|
|
192
|
+
ok,
|
|
193
|
+
patternSources: loaded.sources,
|
|
194
|
+
findings,
|
|
195
|
+
gitleaks,
|
|
196
|
+
}, null, 2));
|
|
197
|
+
return ok ? 0 : 1;
|
|
198
|
+
}
|
|
199
|
+
console.log(`patterns: ${loaded.sources.join(", ")}`);
|
|
200
|
+
for (const f of findings) {
|
|
201
|
+
console.log(`error[leak:${f.patternId}]: ${f.file}:${f.line}`);
|
|
202
|
+
}
|
|
203
|
+
console.log(gitleaks.detail);
|
|
204
|
+
if (ok) {
|
|
205
|
+
console.log(`guardrails leaks: OK — no leak-pattern hits in ${paths.join(" ")}`);
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
console.log(`guardrails leaks: ${findings.length} finding(s)${gitleaks.ok ? "" : " + gitleaks findings"}. Public code holds mechanisms; private facts (hostnames, paths, org names, tokens) belong in the private ops package / project config.`);
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=leaks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaks.js","sourceRoot":"","sources":["../src/leaks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/leaksRed.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { packageRoot } from "./packagePaths.js";
|
|
6
|
+
// Gate-can-go-red tests for `guardrails leaks`:
|
|
7
|
+
// 1. a fixture with a credential-shaped string (built-in generic pattern)
|
|
8
|
+
// turns the gate red, a clean dir does not;
|
|
9
|
+
// 2. the runtime pattern-loading mechanism works — a `.guardrails/leaks.txt`
|
|
10
|
+
// pattern fires on matching content, and the pattern file itself is
|
|
11
|
+
// exempt from the scan (it necessarily contains its own markers).
|
|
12
|
+
// The token is ASSEMBLED from halves so this test's own source never
|
|
13
|
+
// contains a credential-shaped string.
|
|
14
|
+
const TOKENISH = ["ghp_", "a".repeat(36)].join("");
|
|
15
|
+
function leaks(cwd, target) {
|
|
16
|
+
const res = spawnSync("node", [join(packageRoot, "dist", "cli.js"), "leaks", target], {
|
|
17
|
+
cwd,
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
});
|
|
20
|
+
if (res.error)
|
|
21
|
+
throw res.error;
|
|
22
|
+
return res.status ?? -1;
|
|
23
|
+
}
|
|
24
|
+
function expectExit(step, got, want) {
|
|
25
|
+
if (got !== want) {
|
|
26
|
+
console.error(`FAIL leaks-red: ${step} exited ${got}, expected ${want}`);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
console.log(` ok leaks-red: ${step} → exit ${want}`);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function main() {
|
|
33
|
+
const dir = mkdtempSync(join(tmpdir(), "guardrails-leaks-red-"));
|
|
34
|
+
try {
|
|
35
|
+
const badFile = join(dir, "config.ts");
|
|
36
|
+
writeFileSync(badFile, `const t = "${TOKENISH}";\n`);
|
|
37
|
+
if (!expectExit("token-shaped fixture (gate goes red)", leaks(dir, "."), 1))
|
|
38
|
+
return 1;
|
|
39
|
+
rmSync(badFile);
|
|
40
|
+
writeFileSync(join(dir, "clean.ts"), 'export const apiBase = "https://example.invalid";\n');
|
|
41
|
+
if (!expectExit("clean dir (gate goes green)", leaks(dir, "."), 0))
|
|
42
|
+
return 1;
|
|
43
|
+
// Runtime pattern loading: a repo-local pattern file arms a marker the
|
|
44
|
+
// public package does not know.
|
|
45
|
+
mkdirSync(join(dir, ".guardrails"));
|
|
46
|
+
writeFileSync(join(dir, ".guardrails", "leaks.txt"), "custom-marker examplecorp-internal\n");
|
|
47
|
+
writeFileSync(join(dir, "infra.ts"), 'const host = "examplecorp-internal.example";\n');
|
|
48
|
+
if (!expectExit("repo-local pattern fires (leaks.txt mechanism)", leaks(dir, "."), 1))
|
|
49
|
+
return 1;
|
|
50
|
+
// The pattern file itself must be exempt — it contains its own marker.
|
|
51
|
+
rmSync(join(dir, "infra.ts"));
|
|
52
|
+
if (!expectExit("pattern file self-exempt (green with only leaks.txt)", leaks(dir, "."), 0)) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
rmSync(dir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
process.exit(main());
|
|
62
|
+
//# sourceMappingURL=leaksRed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaksRed.js","sourceRoot":"","sources":["../src/leaksRed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface MetricsOptions {
|
|
2
|
+
targets: string[];
|
|
3
|
+
json: boolean;
|
|
4
|
+
check: boolean;
|
|
5
|
+
updateBaseline: boolean;
|
|
6
|
+
force: boolean;
|
|
7
|
+
snapshot: boolean;
|
|
8
|
+
baselinePath: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function runMetrics(opts: MetricsOptions): number;
|