@jeger-ai/opengantry 3.7.0 → 3.8.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 +15 -15
- package/dist/cli/commands/cage.d.ts +16 -0
- package/dist/cli/commands/cage.d.ts.map +1 -1
- package/dist/cli/commands/cage.js +77 -8
- package/dist/cli/commands/cage.js.map +1 -1
- package/dist/cli/lib/cage/cage-config.d.ts +29 -0
- package/dist/cli/lib/cage/cage-config.d.ts.map +1 -0
- package/dist/cli/lib/cage/cage-config.js +143 -0
- package/dist/cli/lib/cage/cage-config.js.map +1 -0
- package/dist/cli/lib/cage/cage-process.d.ts +1 -1
- package/dist/cli/lib/cage/cage-process.d.ts.map +1 -1
- package/dist/cli/lib/cage/cage-process.js +2 -2
- package/dist/cli/lib/cage/cage-process.js.map +1 -1
- package/dist/cli/lib/cage/cage-push-guard.d.ts +26 -0
- package/dist/cli/lib/cage/cage-push-guard.d.ts.map +1 -0
- package/dist/cli/lib/cage/cage-push-guard.js +114 -0
- package/dist/cli/lib/cage/cage-push-guard.js.map +1 -0
- package/dist/cli/lib/cage/cage-report.d.ts +37 -2
- package/dist/cli/lib/cage/cage-report.d.ts.map +1 -1
- package/dist/cli/lib/cage/cage-report.js +50 -4
- package/dist/cli/lib/cage/cage-report.js.map +1 -1
- package/dist/cli/lib/cage/cage-revert.d.ts +1 -1
- package/dist/cli/lib/cage/cage-rules.d.ts +49 -3
- package/dist/cli/lib/cage/cage-rules.d.ts.map +1 -1
- package/dist/cli/lib/cage/cage-rules.js +145 -7
- package/dist/cli/lib/cage/cage-rules.js.map +1 -1
- package/dist/cli/lib/cage/cage-run.d.ts +22 -2
- package/dist/cli/lib/cage/cage-run.d.ts.map +1 -1
- package/dist/cli/lib/cage/cage-run.js +85 -17
- package/dist/cli/lib/cage/cage-run.js.map +1 -1
- package/dist/cli/lib/cage/cage-snapshot.d.ts +11 -0
- package/dist/cli/lib/cage/cage-snapshot.d.ts.map +1 -1
- package/dist/cli/lib/cage/cage-snapshot.js +60 -18
- package/dist/cli/lib/cage/cage-snapshot.js.map +1 -1
- package/dist/cli/lib/cage/cage-suggest.d.ts +28 -0
- package/dist/cli/lib/cage/cage-suggest.d.ts.map +1 -0
- package/dist/cli/lib/cage/cage-suggest.js +164 -0
- package/dist/cli/lib/cage/cage-suggest.js.map +1 -0
- package/dist/cli/lib/cage/cage-watch.d.ts +36 -0
- package/dist/cli/lib/cage/cage-watch.d.ts.map +1 -0
- package/dist/cli/lib/cage/cage-watch.js +92 -0
- package/dist/cli/lib/cage/cage-watch.js.map +1 -0
- package/dist/cli/lib/version.gen.d.ts +1 -1
- package/dist/cli/lib/version.gen.js +1 -1
- package/dist/cli/program-cage.d.ts +9 -2
- package/dist/cli/program-cage.d.ts.map +1 -1
- package/dist/cli/program-cage.js +50 -6
- package/dist/cli/program-cage.js.map +1 -1
- package/package.json +1 -1
- package/templates/.gitagent/foreman/SUBSTRATE.version.json +1 -1
- package/templates/integrations/compatibility.json +1 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { getRepoRoot } from "../git/git.js";
|
|
3
4
|
import { buildCageReport } from "./cage-report.js";
|
|
4
5
|
import { resolveCageChanges } from "./cage-revert.js";
|
|
5
6
|
import { buildCagePlan } from "./cage-rules.js";
|
|
6
|
-
import { diffCageSnapshots, takeCageSnapshot } from "./cage-snapshot.js";
|
|
7
|
+
import { CAGE_MAX_SCAN_BYTES, CAGE_MAX_TARGETS, diffCageSnapshots, takeCageSnapshot, } from "./cage-snapshot.js";
|
|
7
8
|
import { signalExitCode, spawnCaged } from "./cage-process.js";
|
|
9
|
+
import { createCagePushGuard } from "./cage-push-guard.js";
|
|
10
|
+
import { CAGE_SESSION_ENV } from "./cage-suggest.js";
|
|
11
|
+
import { CAGE_DEFAULT_WATCH_INTERVAL_MS, openCageSessionLog, startCageWatch, } from "./cage-watch.js";
|
|
8
12
|
/** Per-file in-memory snapshot cap; larger revert-mode files are detect-only. */
|
|
9
13
|
export const CAGE_DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
10
14
|
/** Total in-memory snapshot budget across all protected files. */
|
|
@@ -18,33 +22,97 @@ export function resolveCageRoot(cwd) {
|
|
|
18
22
|
return path.resolve(cwd);
|
|
19
23
|
}
|
|
20
24
|
}
|
|
25
|
+
function countRefusedPushes(log) {
|
|
26
|
+
if (!log || !fs.existsSync(log.path))
|
|
27
|
+
return 0;
|
|
28
|
+
return fs.readFileSync(log.path, "utf8").split("\n").filter((l) => l.includes('"event":"push_refused"')).length;
|
|
29
|
+
}
|
|
30
|
+
/** Live-restored changes first, then the final diff; a path changed again at exit takes the exit outcome. */
|
|
31
|
+
function mergeChanges(live, final) {
|
|
32
|
+
const byPath = new Map(live ? live.restored : []);
|
|
33
|
+
for (const c of final) {
|
|
34
|
+
const earlier = byPath.get(c.abs);
|
|
35
|
+
byPath.set(c.abs, earlier ? { ...c, kind: earlier.kind, before: earlier.before } : c);
|
|
36
|
+
}
|
|
37
|
+
return [...byPath.values()].sort((a, b) => a.rel.localeCompare(b.rel));
|
|
38
|
+
}
|
|
39
|
+
function resolveSettings(opts) {
|
|
40
|
+
const cwd = path.resolve(opts.cwd ?? process.cwd());
|
|
41
|
+
const reportOnly = opts.reportOnly === true;
|
|
42
|
+
return {
|
|
43
|
+
cwd,
|
|
44
|
+
root: resolveCageRoot(cwd),
|
|
45
|
+
reportOnly,
|
|
46
|
+
watch: opts.watch !== false && !reportOnly,
|
|
47
|
+
intervalMs: opts.watchIntervalMs ?? CAGE_DEFAULT_WATCH_INTERVAL_MS,
|
|
48
|
+
pushGuard: opts.pushGuard !== false,
|
|
49
|
+
snap: {
|
|
50
|
+
keepBytes: !reportOnly,
|
|
51
|
+
maxFileBytes: opts.maxFileBytes ?? CAGE_DEFAULT_MAX_FILE_BYTES,
|
|
52
|
+
maxTotalBytes: opts.maxTotalBytes ?? CAGE_DEFAULT_MAX_TOTAL_BYTES,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Run the command with the session log, live watcher and push guard; always tear the guard down. */
|
|
57
|
+
async function runSession(opts, s, plan, before) {
|
|
58
|
+
const log = s.watch || s.pushGuard ? openCageSessionLog() : null;
|
|
59
|
+
const guard = s.pushGuard ? createCagePushGuard(plan, log?.path ?? null) : null;
|
|
60
|
+
opts.onStart?.({
|
|
61
|
+
sessionLog: log?.path ?? null,
|
|
62
|
+
protectedFiles: before.entries.size,
|
|
63
|
+
watch: s.watch,
|
|
64
|
+
overrides: plan.overrides,
|
|
65
|
+
configCommitted: plan.config.committed,
|
|
66
|
+
});
|
|
67
|
+
const watcher = s.watch && log
|
|
68
|
+
? startCageWatch({ plan, baseline: before, snapshotOptions: s.snap, intervalMs: s.intervalMs, log, bell: opts.bell })
|
|
69
|
+
: null;
|
|
70
|
+
try {
|
|
71
|
+
// The session marker lets setup-only commands (cage suggest) refuse to run inside the cage.
|
|
72
|
+
const proc = await spawnCaged(opts.command, s.cwd, { ...guard?.env, [CAGE_SESSION_ENV]: "1" });
|
|
73
|
+
return { proc, live: watcher?.stop() ?? null, log, guarded: guard !== null };
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
watcher?.stop();
|
|
77
|
+
guard?.dispose();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
21
80
|
/**
|
|
22
|
-
* Snapshot the protected set, run the command
|
|
23
|
-
* Bytes stay in memory
|
|
81
|
+
* Snapshot the protected set, run the command (restoring protected changes live unless `watch: false`),
|
|
82
|
+
* diff once more at exit, and restore what is left. Bytes stay in memory; the session log and the
|
|
83
|
+
* temporary hooks dir live in the OS temp dir, never in the repository.
|
|
24
84
|
*/
|
|
25
85
|
export async function runCage(opts) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const after = takeCageSnapshot(plan, { keepBytes: false, maxFileBytes, maxTotalBytes });
|
|
36
|
-
const changes = resolveCageChanges(diffCageSnapshots(before, after), { reportOnly });
|
|
86
|
+
const s = resolveSettings(opts);
|
|
87
|
+
// Plan and baseline before spawning: an invalid manifest, .cage.yaml or override, or a protected set
|
|
88
|
+
// over the caps, fails closed and the command never runs.
|
|
89
|
+
const plan = buildCagePlan(s.root, { overrides: opts.allowOverride });
|
|
90
|
+
const limits = { maxTargets: opts.maxTargets ?? CAGE_MAX_TARGETS, maxScanBytes: opts.maxScanBytes ?? CAGE_MAX_SCAN_BYTES };
|
|
91
|
+
const before = takeCageSnapshot(plan, { ...s.snap, limits });
|
|
92
|
+
const { proc, live, log, guarded } = await runSession(opts, s, plan, before);
|
|
93
|
+
const after = takeCageSnapshot(plan, { ...s.snap, keepBytes: false });
|
|
94
|
+
const changes = mergeChanges(live, resolveCageChanges(diffCageSnapshots(before, after), { reportOnly: s.reportOnly }));
|
|
37
95
|
return buildCageReport({
|
|
38
96
|
command: opts.command[0] ?? "",
|
|
39
97
|
commandExitCode: proc.code,
|
|
40
98
|
commandSignal: proc.signal,
|
|
41
99
|
runtimeError: proc.spawnError,
|
|
42
|
-
root,
|
|
43
|
-
reportOnly,
|
|
44
|
-
maxFileBytes,
|
|
100
|
+
root: s.root,
|
|
101
|
+
reportOnly: s.reportOnly,
|
|
102
|
+
maxFileBytes: s.snap.maxFileBytes,
|
|
45
103
|
protectedFiles: before.entries.size,
|
|
46
104
|
manifestZones: plan.manifestZoneCount,
|
|
105
|
+
cageConfig: plan.config,
|
|
106
|
+
overrides: plan.overrides,
|
|
47
107
|
changes,
|
|
108
|
+
watch: {
|
|
109
|
+
enabled: s.watch,
|
|
110
|
+
interval_ms: s.watch ? s.intervalMs : null,
|
|
111
|
+
live_restores: live?.liveRestores ?? 0,
|
|
112
|
+
contested: live?.contested ?? [],
|
|
113
|
+
session_log: log?.path ?? null,
|
|
114
|
+
},
|
|
115
|
+
pushGuard: { enabled: guarded, refused_pushes: countRefusedPushes(log) },
|
|
48
116
|
signalExitCode,
|
|
49
117
|
});
|
|
50
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cage-run.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-run.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"cage-run.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-run.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { type CagePlan, type CageRuleId, type CageRuleMode } from "./cage-rules.js";
|
|
2
|
+
/** Baseline caps so an oversized or hostile protected set cannot stall the session. */
|
|
3
|
+
export declare const CAGE_MAX_TARGETS = 5000;
|
|
4
|
+
export declare const CAGE_MAX_SCAN_BYTES: number;
|
|
2
5
|
export interface CageEntry {
|
|
3
6
|
abs: string;
|
|
4
7
|
/** POSIX path relative to the cage root (may start with `../` for a linked worktree git dir). */
|
|
5
8
|
rel: string;
|
|
6
9
|
rule: CageRuleId;
|
|
7
10
|
mode: CageRuleMode;
|
|
11
|
+
/** A revert rule downgraded to report by `--allow-override`. */
|
|
12
|
+
overridden: boolean;
|
|
8
13
|
type: "file" | "symlink";
|
|
9
14
|
sha256: string;
|
|
10
15
|
size: number;
|
|
@@ -21,6 +26,11 @@ export interface CageSnapshotOptions {
|
|
|
21
26
|
keepBytes: boolean;
|
|
22
27
|
maxFileBytes: number;
|
|
23
28
|
maxTotalBytes: number;
|
|
29
|
+
/** Baseline only: abort when the protected set exceeds these caps (counts are reported). */
|
|
30
|
+
limits?: {
|
|
31
|
+
maxTargets: number;
|
|
32
|
+
maxScanBytes: number;
|
|
33
|
+
};
|
|
24
34
|
}
|
|
25
35
|
export interface CageChange {
|
|
26
36
|
abs: string;
|
|
@@ -28,6 +38,7 @@ export interface CageChange {
|
|
|
28
38
|
kind: "added" | "modified" | "deleted";
|
|
29
39
|
rule: CageRuleId;
|
|
30
40
|
mode: CageRuleMode;
|
|
41
|
+
overridden: boolean;
|
|
31
42
|
before: CageEntry | null;
|
|
32
43
|
after: CageEntry | null;
|
|
33
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cage-snapshot.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-snapshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cage-snapshot.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-snapshot.ts"],"names":[],"mappings":"AAKA,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;AAEzB,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,mBAAmB,QAAmB,CAAC;AAEpD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iGAAiG;IACjG,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,gEAAgE;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,MAAM,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB;AAqID,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,GAAG,YAAY,CAYxF;AAOD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CAezF"}
|
|
@@ -2,7 +2,11 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { toPosixRel } from "../cli-io.js";
|
|
5
|
-
import {
|
|
5
|
+
import { GantryUserError } from "../errors.js";
|
|
6
|
+
import { CAGE_RULE_MODES, CAGE_SCAN_SKIP_DIRS, applyCageOverride, cageBasenameRule, } from "./cage-rules.js";
|
|
7
|
+
/** Baseline caps so an oversized or hostile protected set cannot stall the session. */
|
|
8
|
+
export const CAGE_MAX_TARGETS = 5000;
|
|
9
|
+
export const CAGE_MAX_SCAN_BYTES = 64 * 1024 * 1024;
|
|
6
10
|
const HASH_CHUNK_BYTES = 1024 * 1024;
|
|
7
11
|
function sha256Hex(data) {
|
|
8
12
|
return crypto.createHash("sha256").update(data).digest("hex");
|
|
@@ -23,10 +27,26 @@ function hashFileChunked(abs) {
|
|
|
23
27
|
}
|
|
24
28
|
return hash.digest("hex");
|
|
25
29
|
}
|
|
26
|
-
|
|
30
|
+
/** Past a cap, keep counting sizes (lstat only, no hashing) so the error can state the full totals. */
|
|
31
|
+
function countScan(abs, st, ctx) {
|
|
32
|
+
const limits = ctx.opts.limits;
|
|
33
|
+
if (!limits || ctx.scanned.seen.has(abs))
|
|
34
|
+
return ctx.scanned.over;
|
|
35
|
+
ctx.scanned.seen.add(abs);
|
|
36
|
+
ctx.scanned.files += 1;
|
|
37
|
+
ctx.scanned.bytes += st.isFile() ? st.size : 0;
|
|
38
|
+
if (ctx.scanned.files > limits.maxTargets || ctx.scanned.bytes > limits.maxScanBytes)
|
|
39
|
+
ctx.scanned.over = true;
|
|
40
|
+
return ctx.scanned.over;
|
|
41
|
+
}
|
|
42
|
+
function readEntry(abs, rule, ruleMode, ctx) {
|
|
27
43
|
const st = fs.lstatSync(abs);
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
if (countScan(abs, st, ctx))
|
|
45
|
+
return null;
|
|
46
|
+
const rel = toPosixRel(ctx.plan.root, abs);
|
|
47
|
+
const resolved = applyCageOverride(ctx.plan, rel, { rule, mode: ruleMode, overridden: false });
|
|
48
|
+
const mode = resolved.mode;
|
|
49
|
+
const base = { abs, rel, rule, mode, overridden: resolved.overridden, fileMode: st.mode & 0o7777 };
|
|
30
50
|
if (st.isSymbolicLink()) {
|
|
31
51
|
const target = fs.readlinkSync(abs);
|
|
32
52
|
return { ...base, type: "symlink", sha256: sha256Hex(`symlink\0${target}`), size: 0, bytes: null, linkTarget: target };
|
|
@@ -42,13 +62,13 @@ function readEntry(abs, rule, ctx) {
|
|
|
42
62
|
ctx.budget -= bytes.length;
|
|
43
63
|
return { ...base, type: "file", sha256: sha256Hex(bytes), size: bytes.length, bytes, linkTarget: null };
|
|
44
64
|
}
|
|
45
|
-
function captureEntry(abs, rule, ctx) {
|
|
65
|
+
function captureEntry(abs, rule, mode, ctx) {
|
|
46
66
|
const existing = ctx.entries.get(abs);
|
|
47
67
|
// A path matched by several rules keeps the strictest one (revert beats report).
|
|
48
|
-
if (existing && (existing.mode === "revert" ||
|
|
68
|
+
if (existing && (existing.mode === "revert" || existing.overridden || mode === "report"))
|
|
49
69
|
return;
|
|
50
70
|
try {
|
|
51
|
-
const entry = readEntry(abs, rule, ctx);
|
|
71
|
+
const entry = readEntry(abs, rule, mode, ctx);
|
|
52
72
|
if (entry)
|
|
53
73
|
ctx.entries.set(abs, entry);
|
|
54
74
|
}
|
|
@@ -76,7 +96,7 @@ function walkTree(absDir, skipDirs, visit) {
|
|
|
76
96
|
visit(child);
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
|
-
function captureTarget(abs, rule, ctx) {
|
|
99
|
+
function captureTarget(abs, rule, mode, ctx) {
|
|
80
100
|
let st;
|
|
81
101
|
try {
|
|
82
102
|
st = fs.lstatSync(abs);
|
|
@@ -85,20 +105,42 @@ function captureTarget(abs, rule, ctx) {
|
|
|
85
105
|
return;
|
|
86
106
|
}
|
|
87
107
|
if (st.isDirectory()) {
|
|
88
|
-
walkTree(abs, null, (child) => captureEntry(child, rule, ctx));
|
|
108
|
+
walkTree(abs, null, (child) => captureEntry(child, rule, mode, ctx));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
captureEntry(abs, rule, mode, ctx);
|
|
112
|
+
}
|
|
113
|
+
/** Whole-tree scan: built-in basename rules (`.env*`, lockfiles) and `.cage.yaml` globs. */
|
|
114
|
+
function captureScanned(abs, ctx) {
|
|
115
|
+
const rule = cageBasenameRule(path.basename(abs));
|
|
116
|
+
if (rule)
|
|
117
|
+
captureEntry(abs, rule, CAGE_RULE_MODES[rule], ctx);
|
|
118
|
+
if (ctx.plan.globs.length === 0)
|
|
89
119
|
return;
|
|
120
|
+
const rel = toPosixRel(ctx.plan.root, abs);
|
|
121
|
+
for (const g of ctx.plan.globs) {
|
|
122
|
+
if (g.re.test(rel))
|
|
123
|
+
captureEntry(abs, "cage_protect", g.mode, ctx);
|
|
90
124
|
}
|
|
91
|
-
|
|
125
|
+
}
|
|
126
|
+
function limitsError(ctx) {
|
|
127
|
+
const l = ctx.opts.limits;
|
|
128
|
+
const mib = (n) => (n / (1024 * 1024)).toFixed(1);
|
|
129
|
+
return new GantryUserError("GXT_CAGE_LIMITS_EXCEEDED", `cage: protected set too large: ${String(ctx.scanned.files)} file(s), ${mib(ctx.scanned.bytes)} MiB (limits ${String(l.maxTargets)} files, ${mib(l.maxScanBytes)} MiB); the command was not run`, "Narrow broad .cage.yaml or manifest forbidden_zones entries.", 2);
|
|
92
130
|
}
|
|
93
131
|
export function takeCageSnapshot(plan, opts) {
|
|
94
|
-
const ctx = {
|
|
132
|
+
const ctx = {
|
|
133
|
+
plan,
|
|
134
|
+
opts,
|
|
135
|
+
budget: opts.maxTotalBytes,
|
|
136
|
+
entries: new Map(),
|
|
137
|
+
scanned: { files: 0, bytes: 0, over: false, seen: new Set() },
|
|
138
|
+
};
|
|
95
139
|
for (const target of plan.targets)
|
|
96
|
-
captureTarget(target.abs, target.rule, ctx);
|
|
97
|
-
walkTree(plan.root, CAGE_SCAN_SKIP_DIRS, (abs) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
captureEntry(abs, rule, ctx);
|
|
101
|
-
});
|
|
140
|
+
captureTarget(target.abs, target.rule, target.mode, ctx);
|
|
141
|
+
walkTree(plan.root, CAGE_SCAN_SKIP_DIRS, (abs) => captureScanned(abs, ctx));
|
|
142
|
+
if (ctx.scanned.over)
|
|
143
|
+
throw limitsError(ctx);
|
|
102
144
|
return { entries: ctx.entries };
|
|
103
145
|
}
|
|
104
146
|
function entryChanged(a, b) {
|
|
@@ -121,7 +163,7 @@ export function diffCageSnapshots(before, after) {
|
|
|
121
163
|
if (!kind)
|
|
122
164
|
continue;
|
|
123
165
|
const ref = (b ?? a);
|
|
124
|
-
changes.push({ abs, rel: ref.rel, kind, rule: ref.rule, mode: ref.mode, before: b, after: a });
|
|
166
|
+
changes.push({ abs, rel: ref.rel, kind, rule: ref.rule, mode: ref.mode, overridden: ref.overridden, before: b, after: a });
|
|
125
167
|
}
|
|
126
168
|
return changes.sort((x, y) => x.rel.localeCompare(y.rel));
|
|
127
169
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cage-snapshot.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GAIjB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"cage-snapshot.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type CageProtectEntry } from "./cage-config.js";
|
|
2
|
+
/**
|
|
3
|
+
* `gantry cage suggest`: a developer-run setup step that proposes `.cage.yaml` protect entries from
|
|
4
|
+
* static heuristics. It only proposes; nothing it prints or writes is ever loaded by cage. A human
|
|
5
|
+
* reviews the proposal, moves it into `.cage.yaml` and commits it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CAGE_SUGGESTED_FILE = ".cage.yaml.suggested";
|
|
8
|
+
/** Set by cage for the wrapped process tree; suggest refuses to run inside a session. */
|
|
9
|
+
export declare const CAGE_SESSION_ENV = "GANTRY_CAGE_SESSION";
|
|
10
|
+
export type CageSuggestCategory = "migrations" | "infrastructure" | "container" | "package_registry" | "code_owners" | "ci" | "git_hooks" | "keys";
|
|
11
|
+
export interface CageSuggestion extends CageProtectEntry {
|
|
12
|
+
category: CageSuggestCategory;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CageSuggestResult {
|
|
16
|
+
root: string;
|
|
17
|
+
suggestions: CageSuggestion[];
|
|
18
|
+
/** Matches left out because built-in rules or the existing `.cage.yaml` already protect them. */
|
|
19
|
+
already_covered: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function suggestCageRules(root: string): CageSuggestResult;
|
|
22
|
+
/** `.cage.yaml` text for the suggestions: one reason comment per entry; parses with the cage loader. */
|
|
23
|
+
export declare function formatCageSuggestionsYaml(result: CageSuggestResult, existingConfig: boolean): string;
|
|
24
|
+
/** Write `.cage.yaml.suggested` at the root; never `.cage.yaml`. Refuses to overwrite unless forced. */
|
|
25
|
+
export declare function writeCageSuggestions(root: string, text: string, force: boolean): string;
|
|
26
|
+
/** Inside a cage session the wrapped agent must not author cage rules, not even as a proposal. */
|
|
27
|
+
export declare function assertOutsideCageSession(env?: NodeJS.ProcessEnv): void;
|
|
28
|
+
//# sourceMappingURL=cage-suggest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cage-suggest.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-suggest.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAG3E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,yBAAkC,CAAC;AAEnE,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,gBAAgB,GAChB,WAAW,GACX,kBAAkB,GAClB,aAAa,GACb,IAAI,GACJ,WAAW,GACX,MAAM,CAAC;AAEX,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,iGAAiG;IACjG,eAAe,EAAE,MAAM,CAAC;CACzB;AA+GD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAWhE;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,CAWpG;AAED,wGAAwG;AACxG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAWvF;AAED,kGAAkG;AAClG,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CASnF"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { toPosixRel } from "../cli-io.js";
|
|
4
|
+
import { GantryUserError } from "../errors.js";
|
|
5
|
+
import { CAGE_CONFIG_FILE } from "./cage-config.js";
|
|
6
|
+
import { CAGE_SCAN_SKIP_DIRS, buildCagePlan, classifyCagePath } from "./cage-rules.js";
|
|
7
|
+
/**
|
|
8
|
+
* `gantry cage suggest`: a developer-run setup step that proposes `.cage.yaml` protect entries from
|
|
9
|
+
* static heuristics. It only proposes; nothing it prints or writes is ever loaded by cage. A human
|
|
10
|
+
* reviews the proposal, moves it into `.cage.yaml` and commits it.
|
|
11
|
+
*/
|
|
12
|
+
export const CAGE_SUGGESTED_FILE = `${CAGE_CONFIG_FILE}.suggested`;
|
|
13
|
+
/** Set by cage for the wrapped process tree; suggest refuses to run inside a session. */
|
|
14
|
+
export const CAGE_SESSION_ENV = "GANTRY_CAGE_SESSION";
|
|
15
|
+
/** Directories protected as a whole tree when found anywhere (by exact name). */
|
|
16
|
+
const DIR_RULES = new Map([
|
|
17
|
+
["migrations", { category: "migrations", reason: "database migrations" }],
|
|
18
|
+
["alembic", { category: "migrations", reason: "Alembic database migrations" }],
|
|
19
|
+
["k8s", { category: "infrastructure", reason: "Kubernetes manifests" }],
|
|
20
|
+
["kubernetes", { category: "infrastructure", reason: "Kubernetes manifests" }],
|
|
21
|
+
["helm", { category: "infrastructure", reason: "Helm charts" }],
|
|
22
|
+
["charts", { category: "infrastructure", reason: "Helm charts" }],
|
|
23
|
+
[".buildkite", { category: "ci", reason: "Buildkite pipeline" }],
|
|
24
|
+
[".woodpecker", { category: "ci", reason: "Woodpecker CI pipeline" }],
|
|
25
|
+
[".githooks", { category: "git_hooks", reason: "committed git hooks (run on commit and push)" }],
|
|
26
|
+
[".husky", { category: "git_hooks", reason: "Husky git hooks (run on commit and push)" }],
|
|
27
|
+
]);
|
|
28
|
+
/** Root-relative directory paths protected as a whole tree. */
|
|
29
|
+
const DIR_PATHS = new Map([
|
|
30
|
+
["db/migrate", { category: "migrations", reason: "Rails database migrations" }],
|
|
31
|
+
[".github/actions", { category: "ci", reason: "custom GitHub Actions run by CI" }],
|
|
32
|
+
]);
|
|
33
|
+
/** Files protected by exact basename wherever they are. */
|
|
34
|
+
const FILE_RULES = new Map([
|
|
35
|
+
["Dockerfile", { category: "container", reason: "container build definition" }],
|
|
36
|
+
["docker-compose.yml", { category: "container", reason: "container runtime definition" }],
|
|
37
|
+
["docker-compose.yaml", { category: "container", reason: "container runtime definition" }],
|
|
38
|
+
["compose.yml", { category: "container", reason: "container runtime definition" }],
|
|
39
|
+
["compose.yaml", { category: "container", reason: "container runtime definition" }],
|
|
40
|
+
[".npmrc", { category: "package_registry", reason: "npm registry and auth config" }],
|
|
41
|
+
[".yarnrc.yml", { category: "package_registry", reason: "Yarn registry and auth config" }],
|
|
42
|
+
[".pypirc", { category: "package_registry", reason: "PyPI upload credentials" }],
|
|
43
|
+
["pip.conf", { category: "package_registry", reason: "pip index config" }],
|
|
44
|
+
[".gemrc", { category: "package_registry", reason: "RubyGems source config" }],
|
|
45
|
+
["CODEOWNERS", { category: "code_owners", reason: "review ownership rules" }],
|
|
46
|
+
["Jenkinsfile", { category: "ci", reason: "Jenkins pipeline" }],
|
|
47
|
+
["bitbucket-pipelines.yml", { category: "ci", reason: "Bitbucket Pipelines config" }],
|
|
48
|
+
[".drone.yml", { category: "ci", reason: "Drone CI pipeline" }],
|
|
49
|
+
[".travis.yml", { category: "ci", reason: "Travis CI pipeline" }],
|
|
50
|
+
[".woodpecker.yml", { category: "ci", reason: "Woodpecker CI pipeline" }],
|
|
51
|
+
["cloudbuild.yaml", { category: "ci", reason: "Google Cloud Build pipeline" }],
|
|
52
|
+
[".pre-commit-config.yaml", { category: "git_hooks", reason: "pre-commit hook config" }],
|
|
53
|
+
["lefthook.yml", { category: "git_hooks", reason: "Lefthook git hook config" }],
|
|
54
|
+
["lefthook.yaml", { category: "git_hooks", reason: "Lefthook git hook config" }],
|
|
55
|
+
[".lefthook.yml", { category: "git_hooks", reason: "Lefthook git hook config" }],
|
|
56
|
+
]);
|
|
57
|
+
/** Extensions protected repo-wide by glob once one such file exists. */
|
|
58
|
+
const EXT_GLOBS = new Map([
|
|
59
|
+
[".tf", { category: "infrastructure", reason: "Terraform infrastructure" }],
|
|
60
|
+
[".tfvars", { category: "infrastructure", reason: "Terraform variables (often secrets)" }],
|
|
61
|
+
[".pem", { category: "keys", reason: "PEM keys and certificates" }],
|
|
62
|
+
[".key", { category: "keys", reason: "private keys" }],
|
|
63
|
+
[".p12", { category: "keys", reason: "PKCS#12 key stores" }],
|
|
64
|
+
[".pfx", { category: "keys", reason: "PKCS#12 key stores" }],
|
|
65
|
+
]);
|
|
66
|
+
/** Build output and vendored trees: anchors found there are not the project's own. */
|
|
67
|
+
const SUGGEST_SKIP_DIRS = new Set([...CAGE_SCAN_SKIP_DIRS, "dist", "build", "vendor", "target"]);
|
|
68
|
+
function dirRule(rel, name) {
|
|
69
|
+
return DIR_PATHS.get(rel) ?? DIR_RULES.get(name);
|
|
70
|
+
}
|
|
71
|
+
function fileRule(name) {
|
|
72
|
+
return FILE_RULES.get(name) ?? (name.startsWith("Dockerfile.") ? FILE_RULES.get("Dockerfile") : undefined);
|
|
73
|
+
}
|
|
74
|
+
function collect(root, absDir, found) {
|
|
75
|
+
let dirents;
|
|
76
|
+
try {
|
|
77
|
+
dirents = fs.readdirSync(absDir, { withFileTypes: true });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const d of dirents) {
|
|
83
|
+
const abs = path.join(absDir, d.name);
|
|
84
|
+
const rel = toPosixRel(root, abs);
|
|
85
|
+
if (d.isDirectory()) {
|
|
86
|
+
if (SUGGEST_SKIP_DIRS.has(d.name))
|
|
87
|
+
continue;
|
|
88
|
+
const rule = dirRule(rel, d.name);
|
|
89
|
+
// A matched tree is suggested whole; nothing inside it needs its own entry.
|
|
90
|
+
if (rule)
|
|
91
|
+
found.set(`path:${rel}`, { kind: "path", value: rel, mode: "revert", ...rule });
|
|
92
|
+
else
|
|
93
|
+
collect(root, abs, found);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!d.isFile())
|
|
97
|
+
continue;
|
|
98
|
+
const byName = fileRule(d.name);
|
|
99
|
+
if (byName)
|
|
100
|
+
found.set(`path:${rel}`, { kind: "path", value: rel, mode: "revert", ...byName });
|
|
101
|
+
const ext = path.extname(d.name);
|
|
102
|
+
const byExt = EXT_GLOBS.get(ext);
|
|
103
|
+
if (byExt)
|
|
104
|
+
found.set(`glob:**/*${ext}`, { kind: "glob", value: `**/*${ext}`, mode: "revert", ...byExt });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Already protected: a path the plan reverts, or a glob `.cage.yaml` already lists verbatim. */
|
|
108
|
+
function isCovered(plan, s) {
|
|
109
|
+
if (s.kind === "glob")
|
|
110
|
+
return plan.globs.some((g) => g.pattern === s.value && g.mode === "revert");
|
|
111
|
+
return classifyCagePath(plan, s.value, { overrides: false })?.mode === "revert";
|
|
112
|
+
}
|
|
113
|
+
function byCodePoint(a, b) {
|
|
114
|
+
if (a === b)
|
|
115
|
+
return 0;
|
|
116
|
+
return a < b ? -1 : 1;
|
|
117
|
+
}
|
|
118
|
+
export function suggestCageRules(root) {
|
|
119
|
+
// An invalid existing .cage.yaml fails closed here too, with the same message cage would give.
|
|
120
|
+
const plan = buildCagePlan(root);
|
|
121
|
+
const found = new Map();
|
|
122
|
+
collect(root, root, found);
|
|
123
|
+
const all = [...found.values()];
|
|
124
|
+
const suggestions = all
|
|
125
|
+
.filter((s) => !isCovered(plan, s))
|
|
126
|
+
// Code-point order, so the proposal is byte-identical across machines and locales.
|
|
127
|
+
.sort((a, b) => byCodePoint(a.value, b.value) || byCodePoint(a.kind, b.kind));
|
|
128
|
+
return { root, suggestions, already_covered: all.length - suggestions.length };
|
|
129
|
+
}
|
|
130
|
+
/** `.cage.yaml` text for the suggestions: one reason comment per entry; parses with the cage loader. */
|
|
131
|
+
export function formatCageSuggestionsYaml(result, existingConfig) {
|
|
132
|
+
const head = [
|
|
133
|
+
"# Proposed by gantry cage suggest. Review every entry, then",
|
|
134
|
+
existingConfig
|
|
135
|
+
? `# add the ones you want to the protect: list in ${CAGE_CONFIG_FILE} and commit it.`
|
|
136
|
+
: `# save the ones you want as ${CAGE_CONFIG_FILE} and commit it.`,
|
|
137
|
+
`# cage never reads this proposal.`,
|
|
138
|
+
];
|
|
139
|
+
if (result.suggestions.length === 0)
|
|
140
|
+
return `${head.join("\n")}\nprotect: []\n`;
|
|
141
|
+
const body = result.suggestions.flatMap((s) => [` # ${s.category}: ${s.reason}`, ` - ${s.kind}: ${JSON.stringify(s.value)}`]);
|
|
142
|
+
return `${[...head, "protect:", ...body].join("\n")}\n`;
|
|
143
|
+
}
|
|
144
|
+
/** Write `.cage.yaml.suggested` at the root; never `.cage.yaml`. Refuses to overwrite unless forced. */
|
|
145
|
+
export function writeCageSuggestions(root, text, force) {
|
|
146
|
+
const file = path.join(root, CAGE_SUGGESTED_FILE);
|
|
147
|
+
try {
|
|
148
|
+
fs.writeFileSync(file, text, { flag: force ? "w" : "wx" });
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (err.code === "EEXIST") {
|
|
152
|
+
throw new GantryUserError("GXT_CAGE_SUGGEST_EXISTS", `cage suggest: ${CAGE_SUGGESTED_FILE} already exists`, "Re-run with --force to replace it.", 2);
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
return file;
|
|
157
|
+
}
|
|
158
|
+
/** Inside a cage session the wrapped agent must not author cage rules, not even as a proposal. */
|
|
159
|
+
export function assertOutsideCageSession(env = process.env) {
|
|
160
|
+
if (env[CAGE_SESSION_ENV]) {
|
|
161
|
+
throw new GantryUserError("GXT_CAGE_SUGGEST_IN_SESSION", "cage suggest: refused inside a cage session; run it yourself, outside gantry cage", undefined, 2);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=cage-suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cage-suggest.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-suggest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CageResolvedChange } from "./cage-revert.js";
|
|
2
|
+
import type { CagePlan } from "./cage-rules.js";
|
|
3
|
+
import { type CageSnapshot, type CageSnapshotOptions } from "./cage-snapshot.js";
|
|
4
|
+
/** Default live-check interval while the caged command runs. */
|
|
5
|
+
export declare const CAGE_DEFAULT_WATCH_INTERVAL_MS = 1000;
|
|
6
|
+
/** Live restores of one path before cage stops fighting the command and restores it only at exit. */
|
|
7
|
+
export declare const CAGE_CONTEST_LIMIT = 3;
|
|
8
|
+
/** Append-only session event log in the OS temp dir: one JSON object per line, never file bodies. */
|
|
9
|
+
export interface CageSessionLog {
|
|
10
|
+
path: string;
|
|
11
|
+
append(event: Record<string, unknown>): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function openCageSessionLog(dir?: string): CageSessionLog;
|
|
14
|
+
export interface CageWatchSummary {
|
|
15
|
+
liveRestores: number;
|
|
16
|
+
contested: string[];
|
|
17
|
+
/** Live-resolved revert-rule changes, by path; the exit report merges these with the final diff. */
|
|
18
|
+
restored: Map<string, CageResolvedChange>;
|
|
19
|
+
}
|
|
20
|
+
export interface CageWatcher {
|
|
21
|
+
stop(): CageWatchSummary;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Re-snapshot the protected set on an interval while the command runs and restore revert-rule changes
|
|
25
|
+
* from the baseline bytes. Lockfiles stay report-only and are left for the exit report. A path restored
|
|
26
|
+
* {@link CAGE_CONTEST_LIMIT} times is marked contested and restored only at exit.
|
|
27
|
+
*/
|
|
28
|
+
export declare function startCageWatch(input: {
|
|
29
|
+
plan: CagePlan;
|
|
30
|
+
baseline: CageSnapshot;
|
|
31
|
+
snapshotOptions: CageSnapshotOptions;
|
|
32
|
+
intervalMs: number;
|
|
33
|
+
log: CageSessionLog;
|
|
34
|
+
bell?: () => void;
|
|
35
|
+
}): CageWatcher;
|
|
36
|
+
//# sourceMappingURL=cage-watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cage-watch.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-watch.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAuC,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEtH,gEAAgE;AAChE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AACnD,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAIpC,qGAAqG;AACrG,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,SAAc,GAAG,cAAc,CASpE;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oGAAoG;IACpG,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,IAAI,gBAAgB,CAAC;CAC1B;AA+CD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;CACnB,GAAG,WAAW,CA4Bd"}
|