@jeger-ai/opengantry 3.7.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +8 -18
  2. package/dist/cli/commands/cage.d.ts +7 -0
  3. package/dist/cli/commands/cage.d.ts.map +1 -1
  4. package/dist/cli/commands/cage.js +38 -6
  5. package/dist/cli/commands/cage.js.map +1 -1
  6. package/dist/cli/lib/cage/cage-process.d.ts +1 -1
  7. package/dist/cli/lib/cage/cage-process.d.ts.map +1 -1
  8. package/dist/cli/lib/cage/cage-process.js +2 -2
  9. package/dist/cli/lib/cage/cage-process.js.map +1 -1
  10. package/dist/cli/lib/cage/cage-push-guard.d.ts +23 -0
  11. package/dist/cli/lib/cage/cage-push-guard.d.ts.map +1 -0
  12. package/dist/cli/lib/cage/cage-push-guard.js +116 -0
  13. package/dist/cli/lib/cage/cage-push-guard.js.map +1 -0
  14. package/dist/cli/lib/cage/cage-report.d.ts +18 -1
  15. package/dist/cli/lib/cage/cage-report.d.ts.map +1 -1
  16. package/dist/cli/lib/cage/cage-report.js +34 -3
  17. package/dist/cli/lib/cage/cage-report.js.map +1 -1
  18. package/dist/cli/lib/cage/cage-run.d.ts +15 -2
  19. package/dist/cli/lib/cage/cage-run.d.ts.map +1 -1
  20. package/dist/cli/lib/cage/cage-run.js +71 -15
  21. package/dist/cli/lib/cage/cage-run.js.map +1 -1
  22. package/dist/cli/lib/cage/cage-watch.d.ts +36 -0
  23. package/dist/cli/lib/cage/cage-watch.d.ts.map +1 -0
  24. package/dist/cli/lib/cage/cage-watch.js +92 -0
  25. package/dist/cli/lib/cage/cage-watch.js.map +1 -0
  26. package/dist/cli/lib/version.gen.d.ts +1 -1
  27. package/dist/cli/lib/version.gen.js +1 -1
  28. package/dist/cli/program-cage.d.ts.map +1 -1
  29. package/dist/cli/program-cage.js +12 -2
  30. package/dist/cli/program-cage.js.map +1 -1
  31. package/package.json +1 -1
  32. package/templates/.gitagent/foreman/SUBSTRATE.version.json +1 -1
  33. package/templates/integrations/compatibility.json +1 -1
package/README.md CHANGED
@@ -141,30 +141,18 @@ See [`docs/DOMAINS.md`](docs/DOMAINS.md) for adapter details and [`docs/AGENT-GR
141
141
 
142
142
  ## Try it in 60 seconds: `gantry cage`
143
143
 
144
- No setup. Wrap any agent command:
144
+ No setup. Start your agent inside the cage, exactly as you normally would:
145
145
 
146
146
  ```bash
147
- npx -p @jeger-ai/opengantry opengantry-cage -- <agent command>
148
- # installed: gantry cage -- <agent command>
147
+ npx -p @jeger-ai/opengantry opengantry-cage -- claude
148
+ # installed (npm install -g @jeger-ai/opengantry): gantry cage -- claude, gantry cage -- aider
149
149
  ```
150
150
 
151
- Cage checks once, after the command exits, so use it for one-shot, headless agent runs rather than interactive sessions (install once; `npx` re-resolves the package on each launch):
151
+ While the session runs, cage checks about once a second and restores changes to CI configs, `.env` / `.env.*`, `.git/config`, `.git/hooks/` (and `core.hooksPath`) and your manifest `forbidden_zones` from an in-memory snapshot. Lockfile changes are listed but kept. A `git push` from inside the session is refused when the outgoing commits touch those paths, and the agent sees why. When you quit, cage prints one report: everything it restored, any refused pushes, exit `3` if anything was touched (otherwise the agent's own exit code).
152
152
 
153
- ```bash
154
- npm install -g @jeger-ai/opengantry
155
- gantry cage -- claude -p "Fix the failing test in auth.spec.ts"
156
- gantry cage -- aider --message "Refactor DB queries" --no-auto-commits --yes-always
157
-
158
- # single-task shell functions
159
- c-run() { gantry cage -- claude -p "$*"; }
160
- a-run() { gantry cage -- aider --message "$*" --no-auto-commits --yes-always; }
161
- ```
153
+ Nothing is printed into the agent's screen while it runs: events go to a session log in your temp dir (path shown at start), with a terminal bell per restore. If the agent keeps rewriting the same file, cage stops restoring it after 3 tries, marks it contested and restores it once at exit.
162
154
 
163
- Don't alias `claude` or `aider` themselves: both open an interactive REPL, and a protected-file edit early in a long session stays in place (and can be pushed) until you quit. Keep Aider's auto-commits off: cage restores the working tree, not git history or a remote, so a committed or pushed change needs a manual fix.
164
-
165
- After the command exits, cage restores changes to CI configs, `.env` / `.env.*`, `.git/config`, `.git/hooks/` (and `core.hooksPath`), and your manifest `forbidden_zones`. Lockfile changes are listed but kept. Exit `3` means something was reverted; otherwise the command's own exit code passes through.
166
-
167
- Cage detects and reverts **after** the command exits. It does not see reads, network calls, or writes outside the protected set. For scope enforcement during a run, use missions (below). Details: [`docs/FEATURES.md`](docs/FEATURES.md#zero-config-cage-gantry-cage).
155
+ Cage restores; it does not block. A change can be committed in the second before the next check, `git push --no-verify` skips the push guard, and a committed change stays in local history (cage restores the working tree only). Reads, network calls and writes outside the protected set are not seen. `--no-watch` checks once at exit instead. For scope enforced on every write, use missions (below). Details: [`docs/FEATURES.md`](docs/FEATURES.md#zero-config-cage-gantry-cage).
168
156
 
169
157
  ---
170
158
 
@@ -243,6 +231,8 @@ On failure, external agents ingest `findings[]`:
243
231
  }
244
232
  ```
245
233
 
234
+ **v3.7.1:** `gantry cage` restores protected files live during interactive agent sessions (default; `--no-watch` for exit-only) and refuses `git push` of protected paths from inside the session. See [Try it in 60 seconds](#try-it-in-60-seconds-gantry-cage).
235
+
246
236
  **v3.7.0:** zero-config `gantry cage -- <cmd>` (and `npx -p @jeger-ai/opengantry opengantry-cage -- <cmd>`) restores CI configs, `.env*`, git hooks/config and manifest forbidden zones after a one-shot agent run, reports lockfile changes, and exits 3 on tampering. See [Try it in 60 seconds](#try-it-in-60-seconds-gantry-cage).
247
237
 
248
238
  **v3.6.0:** `gate_adapter: tsc|eslint` fails closed on chained `gate_command`s (`GXT_GATE_ADAPTER_MISCONFIG`). `gantry hooks install` arms the tracked pre-commit hook so `gantry tmvc guard --strict` blocks commits outside the pinned mission. Optional local sqlite-vec contract drift index (`gantry contract embed`, `legislate --from-intent --embedding-file`). See [`docs/CHANGELOG.md`](docs/CHANGELOG.md) upgrade notes.
@@ -3,6 +3,13 @@ export interface CageCliOptions {
3
3
  json?: boolean;
4
4
  reportOnly?: boolean;
5
5
  maxFileBytes?: string;
6
+ /** Commander sets false for `--no-watch`. */
7
+ watch?: boolean;
8
+ watchIntervalMs?: string;
6
9
  }
7
10
  export declare function runCageCommand(options: CageCliOptions): Promise<void>;
11
+ /** `gantry cage-guard pre-push --root <dir>`: called by the cage session pre-push hook. */
12
+ export declare function runCageGuardPrePush(options: {
13
+ root: string;
14
+ }): Promise<void>;
8
15
  //# sourceMappingURL=cage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AASD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3E"}
1
+ {"version":3,"file":"cage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AASD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3E;AAED,2FAA2F;AAC3F,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAalF"}
@@ -1,8 +1,11 @@
1
- import { logError, setExitCode } from "../lib/cli-io.js";
1
+ import fs from "node:fs";
2
+ import { logError, readStdinIfEmpty, setExitCode } from "../lib/cli-io.js";
2
3
  import { emitCliJson, runUserCommandAsync } from "../lib/command-boundary.js";
4
+ import { CAGE_SESSION_LOG_ENV, findPushViolations } from "../lib/cage/cage-push-guard.js";
3
5
  import { formatCageReport } from "../lib/cage/cage-report.js";
6
+ import { buildCagePlan } from "../lib/cage/cage-rules.js";
4
7
  import { runCage } from "../lib/cage/cage-run.js";
5
- function parseMaxFileBytes(raw) {
8
+ function parseNonNegativeInt(raw) {
6
9
  if (raw === undefined)
7
10
  return { ok: true, value: undefined };
8
11
  const t = raw.trim();
@@ -17,14 +20,27 @@ export async function runCageCommand(options) {
17
20
  setExitCode(2);
18
21
  return;
19
22
  }
20
- const max = parseMaxFileBytes(options.maxFileBytes);
21
- if (!max.ok) {
22
- logError("cage: --max-file-bytes must be a non-negative integer");
23
+ const max = parseNonNegativeInt(options.maxFileBytes);
24
+ const interval = parseNonNegativeInt(options.watchIntervalMs);
25
+ if (!max.ok || !interval.ok || interval.value === 0) {
26
+ logError("cage: --max-file-bytes must be a non-negative integer and --watch-interval-ms a positive integer");
23
27
  setExitCode(2);
24
28
  return;
25
29
  }
26
30
  await runUserCommandAsync({ json: options.json }, async () => {
27
- const report = await runCage({ command, reportOnly: options.reportOnly, maxFileBytes: max.value });
31
+ const report = await runCage({
32
+ command,
33
+ reportOnly: options.reportOnly,
34
+ maxFileBytes: max.value,
35
+ watch: options.watch !== false,
36
+ watchIntervalMs: interval.value,
37
+ // The only line printed before the command starts; nothing is written while it runs except a bell.
38
+ onStart: ({ sessionLog, protectedFiles, watch }) => {
39
+ const mode = watch ? "watching" : "checking at exit";
40
+ const logPart = sessionLog ? `; live events: ${sessionLog}` : "";
41
+ console.error(`cage: ${mode} ${String(protectedFiles)} protected file(s)${logPart}`);
42
+ },
43
+ });
28
44
  if (options.json) {
29
45
  emitCliJson(report);
30
46
  }
@@ -36,4 +52,20 @@ export async function runCageCommand(options) {
36
52
  setExitCode(report.exit_code);
37
53
  });
38
54
  }
55
+ /** `gantry cage-guard pre-push --root <dir>`: called by the cage session pre-push hook. */
56
+ export async function runCageGuardPrePush(options) {
57
+ const stdin = await readStdinIfEmpty("");
58
+ const violations = findPushViolations(buildCagePlan(options.root), stdin);
59
+ if (violations.length === 0)
60
+ return;
61
+ const list = violations.map((v) => `${v.path} (${v.rule})`).join(", ");
62
+ console.error(`gantry cage: push refused: outgoing commits change protected paths: ${list}`);
63
+ console.error("gantry cage: revert those commits, or push outside the cage session.");
64
+ const logPath = process.env[CAGE_SESSION_LOG_ENV];
65
+ if (logPath && fs.existsSync(logPath)) {
66
+ const event = { at: new Date().toISOString(), event: "push_refused", paths: violations };
67
+ fs.appendFileSync(logPath, `${JSON.stringify(event)}\n`);
68
+ }
69
+ setExitCode(1);
70
+ }
39
71
  //# sourceMappingURL=cage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cage.js","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AASlD,SAAS,iBAAiB,CAAC,GAAuB;IAChD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC7D,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC3C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB;IAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACzF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,QAAQ,CAAC,yDAAyD,CAAC,CAAC;QACpE,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACpD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,QAAQ,CAAC,uDAAuD,CAAC,CAAC;QAClE,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IAED,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACnG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,WAAW,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,KAAK,CAAC;YAAE,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"cage.js","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"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"}
@@ -14,5 +14,5 @@ export declare function signalExitCode(signal: NodeJS.Signals): number;
14
14
  * shared process group, and forwarding it again would double-interrupt.
15
15
  * - SIGTERM / SIGHUP are forwarded to the child instead of killing the cage.
16
16
  */
17
- export declare function spawnCaged(argv: readonly string[], cwd: string): Promise<CageProcessResult>;
17
+ export declare function spawnCaged(argv: readonly string[], cwd: string, extraEnv?: NodeJS.ProcessEnv): Promise<CageProcessResult>;
18
18
  //# sourceMappingURL=cage-process.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cage-process.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAG7D;AAID;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkBjG"}
1
+ {"version":3,"file":"cage-process.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAG7D;AAID;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,CAAC,UAAe,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAkB5B"}
@@ -14,9 +14,9 @@ const FORWARDED_SIGNALS = ["SIGTERM", "SIGHUP"];
14
14
  * shared process group, and forwarding it again would double-interrupt.
15
15
  * - SIGTERM / SIGHUP are forwarded to the child instead of killing the cage.
16
16
  */
17
- export async function spawnCaged(argv, cwd) {
17
+ export async function spawnCaged(argv, cwd, extraEnv = {}) {
18
18
  const [command, ...args] = argv;
19
- const child = spawn(command, args, { cwd, stdio: "inherit", env: process.env });
19
+ const child = spawn(command, args, { cwd, stdio: "inherit", env: { ...process.env, ...extraEnv } });
20
20
  const ignoreInt = () => { };
21
21
  const forward = (sig) => {
22
22
  child.kill(sig);
@@ -1 +1 @@
1
- {"version":3,"file":"cage-process.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AASzB,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,iBAAiB,GAA8B,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAuB,EAAE,GAAW;IACnE,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACjF,MAAM,SAAS,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,GAAmB,EAAQ,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,iBAAiB;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;YACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3F,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,iBAAiB;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"cage-process.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AASzB,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,iBAAiB,GAA8B,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAuB,EACvB,GAAW,EACX,WAA8B,EAAE;IAEhC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrG,MAAM,SAAS,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,GAAmB,EAAQ,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,iBAAiB;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;YACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3F,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,iBAAiB;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type CagePlan, type CageRuleId } from "./cage-rules.js";
2
+ /** Env var the session hook reads to find the cage session log. */
3
+ export declare const CAGE_SESSION_LOG_ENV = "GANTRY_CAGE_SESSION_LOG";
4
+ export interface CagePushGuard {
5
+ hooksDir: string;
6
+ /** Env additions for the caged process tree (GIT_CONFIG_* appends, never replaces, existing entries). */
7
+ env: NodeJS.ProcessEnv;
8
+ dispose(): void;
9
+ }
10
+ /**
11
+ * Create a temporary hooks dir outside the repo and the env that points the caged process tree at it.
12
+ * Returns null outside a git work tree. Nothing is written into the repository.
13
+ */
14
+ export declare function createCagePushGuard(root: string, sessionLogPath: string | null): CagePushGuard | null;
15
+ /** Rule for a repo-relative path in an outgoing commit; lockfiles (report-only) never block a push. */
16
+ export declare function pushGuardRule(plan: CagePlan, rel: string): CageRuleId | null;
17
+ export interface PushGuardViolation {
18
+ path: string;
19
+ rule: CageRuleId;
20
+ }
21
+ /** Parse pre-push stdin (`<local ref> <local sha> <remote ref> <remote sha>` per line) and list protected paths. */
22
+ export declare function findPushViolations(plan: CagePlan, prePushStdin: string): PushGuardViolation[];
23
+ //# sourceMappingURL=cage-push-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-push-guard.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-push-guard.ts"],"names":[],"mappings":"AAMA,OAAO,EAAqC,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA2BpG,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI,CAUrG;AAED,uGAAuG;AACvG,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAS5E;AAMD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,oHAAoH;AACpH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAa7F"}
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { toPosixRel } from "../cli-io.js";
6
+ import { gitRun } from "../git/git.js";
7
+ import { CAGE_RULE_MODES, cageBasenameRule } from "./cage-rules.js";
8
+ /** Standard client-side git hooks; each session wrapper chains to the repo's real hook of the same name. */
9
+ const GIT_HOOK_NAMES = [
10
+ "applypatch-msg",
11
+ "pre-applypatch",
12
+ "post-applypatch",
13
+ "pre-commit",
14
+ "pre-merge-commit",
15
+ "prepare-commit-msg",
16
+ "commit-msg",
17
+ "post-commit",
18
+ "pre-rebase",
19
+ "post-checkout",
20
+ "post-merge",
21
+ "pre-push",
22
+ "pre-auto-gc",
23
+ "post-rewrite",
24
+ "sendemail-validate",
25
+ "fsmonitor-watchman",
26
+ "post-index-change",
27
+ "reference-transaction",
28
+ "push-to-checkout",
29
+ ];
30
+ const ZERO_SHA = /^0+$/;
31
+ /** Env var the session hook reads to find the cage session log. */
32
+ export const CAGE_SESSION_LOG_ENV = "GANTRY_CAGE_SESSION_LOG";
33
+ function shellQuote(s) {
34
+ return `'${s.replace(/'/g, `'\\''`)}'`;
35
+ }
36
+ function activeHooksDir(root) {
37
+ const r = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-path", "hooks"]);
38
+ const out = r.stdout.trim();
39
+ return r.ok && out ? path.resolve(out) : null;
40
+ }
41
+ /** The CLI entry the session pre-push hook calls (`dist/cli/index.js`, next to this module's `lib/`). */
42
+ function cliEntry() {
43
+ return fileURLToPath(new URL("../../index.js", import.meta.url));
44
+ }
45
+ function hookScript(name, realHooks, root) {
46
+ const real = shellQuote(path.join(realHooks, name));
47
+ const guard = name === "pre-push"
48
+ ? `stdin_copy="$(cat)"\nprintf '%s\\n' "$stdin_copy" | ${shellQuote(process.execPath)} ${shellQuote(cliEntry())} cage-guard pre-push --root ${shellQuote(root)} || exit 1\n`
49
+ : "";
50
+ const chain = name === "pre-push"
51
+ ? `if [ -x ${real} ]; then printf '%s\\n' "$stdin_copy" | ${real} "$@"; exit $?; fi\n`
52
+ : `if [ -x ${real} ]; then exec ${real} "$@"; fi\n`;
53
+ return `#!/bin/sh\n# gantry cage session hook (temporary; removed when the cage session ends)\n${guard}${chain}exit 0\n`;
54
+ }
55
+ function gitConfigEnv(hooksDir) {
56
+ const raw = process.env.GIT_CONFIG_COUNT;
57
+ const n = raw && /^\d+$/.test(raw) ? Number.parseInt(raw, 10) : 0;
58
+ return {
59
+ GIT_CONFIG_COUNT: String(n + 1),
60
+ [`GIT_CONFIG_KEY_${String(n)}`]: "core.hooksPath",
61
+ [`GIT_CONFIG_VALUE_${String(n)}`]: hooksDir,
62
+ };
63
+ }
64
+ /**
65
+ * Create a temporary hooks dir outside the repo and the env that points the caged process tree at it.
66
+ * Returns null outside a git work tree. Nothing is written into the repository.
67
+ */
68
+ export function createCagePushGuard(root, sessionLogPath) {
69
+ const realHooks = activeHooksDir(root);
70
+ if (!realHooks)
71
+ return null;
72
+ const hooksDir = fs.mkdtempSync(path.join(os.tmpdir(), "gantry-cage-hooks-"));
73
+ for (const name of GIT_HOOK_NAMES) {
74
+ fs.writeFileSync(path.join(hooksDir, name), hookScript(name, realHooks, root), { mode: 0o755 });
75
+ }
76
+ const env = { ...gitConfigEnv(hooksDir) };
77
+ if (sessionLogPath)
78
+ env[CAGE_SESSION_LOG_ENV] = sessionLogPath;
79
+ return { hooksDir, env, dispose: () => fs.rmSync(hooksDir, { recursive: true, force: true }) };
80
+ }
81
+ /** Rule for a repo-relative path in an outgoing commit; lockfiles (report-only) never block a push. */
82
+ export function pushGuardRule(plan, rel) {
83
+ for (const t of plan.targets) {
84
+ if (t.rule !== "ci_config" && t.rule !== "manifest_forbidden_zone")
85
+ continue;
86
+ const base = toPosixRel(plan.root, t.abs);
87
+ if (base.startsWith(".."))
88
+ continue;
89
+ if (rel === base || rel.startsWith(`${base}/`))
90
+ return t.rule;
91
+ }
92
+ const rule = cageBasenameRule(path.posix.basename(rel));
93
+ return rule && CAGE_RULE_MODES[rule] === "revert" ? rule : null;
94
+ }
95
+ function outgoingRange(localSha, remoteSha) {
96
+ return ZERO_SHA.test(remoteSha) ? [localSha, "--not", "--remotes"] : [`${remoteSha}..${localSha}`];
97
+ }
98
+ /** Parse pre-push stdin (`<local ref> <local sha> <remote ref> <remote sha>` per line) and list protected paths. */
99
+ export function findPushViolations(plan, prePushStdin) {
100
+ const hits = new Map();
101
+ for (const line of prePushStdin.split(/\r?\n/)) {
102
+ const [, localSha, , remoteSha] = line.trim().split(/\s+/);
103
+ if (!localSha || !remoteSha || ZERO_SHA.test(localSha))
104
+ continue;
105
+ const r = gitRun(plan.root, ["log", "--format=", "--name-only", ...outgoingRange(localSha, remoteSha)]);
106
+ if (!r.ok)
107
+ continue;
108
+ for (const rel of r.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)) {
109
+ const rule = pushGuardRule(plan, rel);
110
+ if (rule)
111
+ hits.set(rel, rule);
112
+ }
113
+ }
114
+ return [...hits].map(([p, rule]) => ({ path: p, rule })).sort((a, b) => a.path.localeCompare(b.path));
115
+ }
116
+ //# sourceMappingURL=cage-push-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-push-guard.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-push-guard.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,14 @@
1
1
  import type { CageRuleId } from "./cage-rules.js";
2
2
  import type { CageOutcome, CageResolvedChange } from "./cage-revert.js";
3
3
  export declare const CAGE_REPORT_SCHEMA: "gantry.cage-report.v1";
4
- /** Detect-after-the-fact limits, stated in every cage report. */
4
+ /** Limits with `--no-watch` (after-exit check only), stated in every such report. */
5
5
  export declare const CAGE_LIMITS: readonly string[];
6
6
  /** One-line stderr form of {@link CAGE_LIMITS}; `--json` keeps the full list. */
7
7
  export declare const CAGE_LIMITS_SUMMARY = "after-exit check only; reads, network calls and writes outside the protected set are not detected";
8
+ /** Limits in watch mode (the default): live restore, not prevention. */
9
+ export declare const CAGE_WATCH_LIMITS: readonly string[];
10
+ /** One-line stderr form of {@link CAGE_WATCH_LIMITS}. */
11
+ export declare const CAGE_WATCH_LIMITS_SUMMARY = "live restore, not blocking; reads, network calls and writes outside the protected set are not detected";
8
12
  /** Printed once under a violation report: the path from cage to project-specific missions. */
9
13
  export declare const CAGE_UPGRADE_FOOTER = "Need project-specific boundaries or task contracts? Run: npx -p @jeger-ai/opengantry gantry init";
10
14
  export type CageStatus = "ok" | "violations_reverted" | "violations_unresolved" | "runtime_error";
@@ -33,6 +37,17 @@ export interface CageReport {
33
37
  protected_files: number;
34
38
  manifest_zones: number;
35
39
  changes: CageReportChange[];
40
+ watch: {
41
+ enabled: boolean;
42
+ interval_ms: number | null;
43
+ live_restores: number;
44
+ contested: string[];
45
+ session_log: string | null;
46
+ };
47
+ push_guard: {
48
+ enabled: boolean;
49
+ refused_pushes: number;
50
+ };
36
51
  limits: readonly string[];
37
52
  }
38
53
  export interface CageReportInput {
@@ -46,6 +61,8 @@ export interface CageReportInput {
46
61
  protectedFiles: number;
47
62
  manifestZones: number;
48
63
  changes: readonly CageResolvedChange[];
64
+ watch: CageReport["watch"];
65
+ pushGuard: CageReport["push_guard"];
49
66
  signalExitCode: (signal: NodeJS.Signals) => number;
50
67
  }
51
68
  export declare function buildCageReport(input: CageReportInput): CageReport;
@@ -1 +1 @@
1
- {"version":3,"file":"cage-report.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAExE,eAAO,MAAM,kBAAkB,EAAG,uBAAgC,CAAC;AAEnE,iEAAiE;AACjE,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAMxC,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,sGACqE,CAAC;AAEtG,8FAA8F;AAC9F,eAAO,MAAM,mBAAmB,qGACoE,CAAC;AAErG,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,qBAAqB,GAAG,uBAAuB,GAAG,eAAe,CAAC;AAElG,2FAA2F;AAC3F,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,OAAO,kBAAkB,CAAC;IACzC,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,QAAQ,GAAG,aAAa,CAAC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACrC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACvC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC;CACpD;AAqBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,CA0BlE;AAOD,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAoB7D"}
1
+ {"version":3,"file":"cage-report.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  export const CAGE_REPORT_SCHEMA = "gantry.cage-report.v1";
2
- /** Detect-after-the-fact limits, stated in every cage report. */
2
+ /** Limits with `--no-watch` (after-exit check only), stated in every such report. */
3
3
  export const CAGE_LIMITS = [
4
4
  "detects protected-path writes after the command exits; it does not block them while it runs",
5
5
  "reads are not detected: the command can still read .env and other secrets",
@@ -9,6 +9,16 @@ export const CAGE_LIMITS = [
9
9
  ];
10
10
  /** One-line stderr form of {@link CAGE_LIMITS}; `--json` keeps the full list. */
11
11
  export const CAGE_LIMITS_SUMMARY = "after-exit check only; reads, network calls and writes outside the protected set are not detected";
12
+ /** Limits in watch mode (the default): live restore, not prevention. */
13
+ export const CAGE_WATCH_LIMITS = [
14
+ "restores protected-path writes about once per watch interval while the command runs; it does not block them",
15
+ "a change can be committed or used before the next check; the push guard covers git push from inside the session only",
16
+ "reads are not detected: the command can still read .env and other secrets",
17
+ "network and API side effects are not detected",
18
+ "writes outside the protected set, and outside the repository, are not checked",
19
+ ];
20
+ /** One-line stderr form of {@link CAGE_WATCH_LIMITS}. */
21
+ export const CAGE_WATCH_LIMITS_SUMMARY = "live restore, not blocking; reads, network calls and writes outside the protected set are not detected";
12
22
  /** Printed once under a violation report: the path from cage to project-specific missions. */
13
23
  export const CAGE_UPGRADE_FOOTER = "Need project-specific boundaries or task contracts? Run: npx -p @jeger-ai/opengantry gantry init";
14
24
  const VIOLATION_EXIT_CODE = 3;
@@ -54,13 +64,32 @@ export function buildCageReport(input) {
54
64
  outcome: c.outcome,
55
65
  error_code: c.errorCode,
56
66
  })),
57
- limits: CAGE_LIMITS,
67
+ watch: input.watch,
68
+ push_guard: input.pushGuard,
69
+ limits: input.watch.enabled ? CAGE_WATCH_LIMITS : CAGE_LIMITS,
58
70
  };
59
71
  }
60
72
  function changeLine(c) {
61
73
  const err = c.error_code ? ` ${c.error_code}` : "";
62
74
  return ` ${c.outcome.padEnd(13)} ${c.kind.padEnd(8)} ${c.path} (${c.rule})${err}`;
63
75
  }
76
+ function sessionLines(report) {
77
+ const out = [];
78
+ const w = report.watch;
79
+ if (w.live_restores > 0) {
80
+ out.push(`cage: restored ${String(w.live_restores)} protected change(s) during the session`);
81
+ }
82
+ if (w.contested.length > 0) {
83
+ out.push(`cage: contested (kept being rewritten; restored once at exit): ${w.contested.join(", ")}`);
84
+ }
85
+ if (report.push_guard.refused_pushes > 0) {
86
+ out.push(`cage: refused ${String(report.push_guard.refused_pushes)} push(es) touching protected paths`);
87
+ }
88
+ if (w.session_log && (w.live_restores > 0 || report.push_guard.refused_pushes > 0)) {
89
+ out.push(`cage: session log: ${w.session_log}`);
90
+ }
91
+ return out;
92
+ }
64
93
  /** Human summary (stderr), so it never mixes into the wrapped command's stdout. */
65
94
  export function formatCageReport(report) {
66
95
  const lines = [];
@@ -78,7 +107,9 @@ export function formatCageReport(report) {
78
107
  if (report.changes.some((c) => c.outcome === "detect_only")) {
79
108
  lines.push(`cage: detect_only files exceeded the ${String(report.max_file_bytes)}-byte per-file cap or the total snapshot budget and were not restored`);
80
109
  }
81
- lines.push(`cage: exit ${String(report.exit_code)}; limits: ${CAGE_LIMITS_SUMMARY}`);
110
+ lines.push(...sessionLines(report));
111
+ const summary = report.watch.enabled ? CAGE_WATCH_LIMITS_SUMMARY : CAGE_LIMITS_SUMMARY;
112
+ lines.push(`cage: exit ${String(report.exit_code)}; limits: ${summary}`);
82
113
  if (report.status === "violations_reverted" || report.status === "violations_unresolved") {
83
114
  lines.push(`cage: ${CAGE_UPGRADE_FOOTER}`);
84
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cage-report.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cage-report.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,kBAAkB,GAAG,uBAAgC,CAAC;AAEnE,qFAAqF;AACrF,MAAM,CAAC,MAAM,WAAW,GAAsB;IAC5C,6FAA6F;IAC7F,2EAA2E;IAC3E,+CAA+C;IAC/C,+EAA+E;IAC/E,+EAA+E;CAChF,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAC9B,mGAAmG,CAAC;AAEtG,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,6GAA6G;IAC7G,sHAAsH;IACtH,2EAA2E;IAC3E,+CAA+C;IAC/C,+EAA+E;CAChF,CAAC;AAEF,yDAAyD;AACzD,MAAM,CAAC,MAAM,yBAAyB,GACpC,wGAAwG,CAAC;AAE3G,8FAA8F;AAC9F,MAAM,CAAC,MAAM,mBAAmB,GAC9B,kGAAkG,CAAC;AAyDrG,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,MAAM,iBAAiB,GAA6B,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;AAErF,SAAS,UAAU,CAAC,KAAsB;IACxC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACpE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,uBAAuB,CAAC;IACrH,CAAC;IACD,OAAO,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB,EAAE,KAAsB;IAC9D,IAAI,MAAM,KAAK,qBAAqB,IAAI,MAAM,KAAK,uBAAuB;QAAE,OAAO,mBAAmB,CAAC;IACvG,IAAI,MAAM,KAAK,eAAe;QAAE,OAAO,uBAAuB,CAAC;IAC/D,IAAI,KAAK,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1E,OAAO,KAAK,CAAC,eAAe,IAAI,uBAAuB,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO;QACL,aAAa,EAAE,kBAAkB;QACjC,MAAM;QACN,SAAS,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;QACtC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,iBAAiB,EAAE,KAAK,CAAC,eAAe;QACxC,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,aAAa,EAAE,KAAK,CAAC,YAAY;QACjC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ;QACxD,cAAc,EAAE,KAAK,CAAC,YAAY;QAClC,eAAe,EAAE,KAAK,CAAC,cAAc;QACrC,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,CAAC,CAAC,GAAG;YACX,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,IAAI;YACvC,YAAY,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI;YACrC,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,UAAU,EAAE,CAAC,CAAC,SAAS;SACxB,CAAC,CAAC;QACH,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW;KAC9D,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAmB;IACrC,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;AACrF,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB;IACtC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;IACvB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,yCAAyC,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,oCAAoC,CAAC,CAAC;IAC1G,CAAC;IACD,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,GAAG,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAChC,IAAI,MAAM,CAAC,aAAa;QAAE,KAAK,CAAC,IAAI,CAAC,8BAA8B,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3F,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,8BAA8B,CAAC,CAAC;IACvG,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;QACxE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,aAAa,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,IAAI,CACR,wCAAwC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,uEAAuE,CAC7I,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACvF,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,MAAM,KAAK,qBAAqB,IAAI,MAAM,CAAC,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -10,12 +10,25 @@ export interface CageRunOptions {
10
10
  reportOnly?: boolean;
11
11
  maxFileBytes?: number;
12
12
  maxTotalBytes?: number;
13
+ /** Live restore while the command runs (default true). */
14
+ watch?: boolean;
15
+ watchIntervalMs?: number;
16
+ /** Session pre-push guard for git run by the caged command (default true inside a git work tree). */
17
+ pushGuard?: boolean;
18
+ /** Called once before the command starts, e.g. to print where the session log is. */
19
+ onStart?: (info: {
20
+ sessionLog: string | null;
21
+ protectedFiles: number;
22
+ watch: boolean;
23
+ }) => void;
24
+ bell?: () => void;
13
25
  }
14
26
  /** Zero-config root: the enclosing git work tree when there is one, otherwise the directory itself. */
15
27
  export declare function resolveCageRoot(cwd: string): string;
16
28
  /**
17
- * Snapshot the protected set, run the command, diff, and revert protected changes.
18
- * Bytes stay in memory only; nothing is written outside the restored paths.
29
+ * Snapshot the protected set, run the command (restoring protected changes live unless `watch: false`),
30
+ * diff once more at exit, and restore what is left. Bytes stay in memory; the session log and the
31
+ * temporary hooks dir live in the OS temp dir, never in the repository.
19
32
  */
20
33
  export declare function runCage(opts: CageRunOptions): Promise<CageReport>;
21
34
  //# sourceMappingURL=cage-run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cage-run.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-run.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMpE,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,QAAkB,CAAC;AAC3D,kEAAkE;AAClE,eAAO,MAAM,4BAA4B,QAAmB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,4FAA4F;IAC5F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA2BvE"}
1
+ {"version":3,"file":"cage-run.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-run.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAcpE,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,QAAkB,CAAC;AAC3D,kEAAkE;AAClE,eAAO,MAAM,4BAA4B,QAAmB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,4FAA4F;IAC5F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qGAAqG;IACrG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qFAAqF;IACrF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAChG,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMnD;AA2ED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA8BvE"}
@@ -1,3 +1,4 @@
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";
@@ -5,6 +6,8 @@ import { resolveCageChanges } from "./cage-revert.js";
5
6
  import { buildCagePlan } from "./cage-rules.js";
6
7
  import { 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_DEFAULT_WATCH_INTERVAL_MS, openCageSessionLog, startCageWatch, } from "./cage-watch.js";
8
11
  /** Per-file in-memory snapshot cap; larger revert-mode files are detect-only. */
9
12
  export const CAGE_DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
10
13
  /** Total in-memory snapshot budget across all protected files. */
@@ -18,33 +21,86 @@ export function resolveCageRoot(cwd) {
18
21
  return path.resolve(cwd);
19
22
  }
20
23
  }
24
+ function countRefusedPushes(log) {
25
+ if (!log || !fs.existsSync(log.path))
26
+ return 0;
27
+ return fs.readFileSync(log.path, "utf8").split("\n").filter((l) => l.includes('"event":"push_refused"')).length;
28
+ }
29
+ /** Live-restored changes first, then the final diff; a path changed again at exit takes the exit outcome. */
30
+ function mergeChanges(live, final) {
31
+ const byPath = new Map(live ? live.restored : []);
32
+ for (const c of final) {
33
+ const earlier = byPath.get(c.abs);
34
+ byPath.set(c.abs, earlier ? { ...c, kind: earlier.kind, before: earlier.before } : c);
35
+ }
36
+ return [...byPath.values()].sort((a, b) => a.rel.localeCompare(b.rel));
37
+ }
38
+ function resolveSettings(opts) {
39
+ const cwd = path.resolve(opts.cwd ?? process.cwd());
40
+ const reportOnly = opts.reportOnly === true;
41
+ return {
42
+ cwd,
43
+ root: resolveCageRoot(cwd),
44
+ reportOnly,
45
+ watch: opts.watch !== false && !reportOnly,
46
+ intervalMs: opts.watchIntervalMs ?? CAGE_DEFAULT_WATCH_INTERVAL_MS,
47
+ pushGuard: opts.pushGuard !== false,
48
+ snap: {
49
+ keepBytes: !reportOnly,
50
+ maxFileBytes: opts.maxFileBytes ?? CAGE_DEFAULT_MAX_FILE_BYTES,
51
+ maxTotalBytes: opts.maxTotalBytes ?? CAGE_DEFAULT_MAX_TOTAL_BYTES,
52
+ },
53
+ };
54
+ }
55
+ /** Run the command with the session log, live watcher and push guard; always tear the guard down. */
56
+ async function runSession(opts, s, plan, before) {
57
+ const log = s.watch || s.pushGuard ? openCageSessionLog() : null;
58
+ const guard = s.pushGuard ? createCagePushGuard(s.root, log?.path ?? null) : null;
59
+ opts.onStart?.({ sessionLog: log?.path ?? null, protectedFiles: before.entries.size, watch: s.watch });
60
+ const watcher = s.watch && log
61
+ ? startCageWatch({ plan, baseline: before, snapshotOptions: s.snap, intervalMs: s.intervalMs, log, bell: opts.bell })
62
+ : null;
63
+ try {
64
+ const proc = await spawnCaged(opts.command, s.cwd, guard?.env ?? {});
65
+ return { proc, live: watcher?.stop() ?? null, log, guarded: guard !== null };
66
+ }
67
+ finally {
68
+ watcher?.stop();
69
+ guard?.dispose();
70
+ }
71
+ }
21
72
  /**
22
- * Snapshot the protected set, run the command, diff, and revert protected changes.
23
- * Bytes stay in memory only; nothing is written outside the restored paths.
73
+ * Snapshot the protected set, run the command (restoring protected changes live unless `watch: false`),
74
+ * diff once more at exit, and restore what is left. Bytes stay in memory; the session log and the
75
+ * temporary hooks dir live in the OS temp dir, never in the repository.
24
76
  */
25
77
  export async function runCage(opts) {
26
- const cwd = path.resolve(opts.cwd ?? process.cwd());
27
- const root = resolveCageRoot(cwd);
28
- const maxFileBytes = opts.maxFileBytes ?? CAGE_DEFAULT_MAX_FILE_BYTES;
29
- const maxTotalBytes = opts.maxTotalBytes ?? CAGE_DEFAULT_MAX_TOTAL_BYTES;
30
- const reportOnly = opts.reportOnly === true;
78
+ const s = resolveSettings(opts);
31
79
  // Plan before spawning: an invalid manifest fails closed and the command never runs.
32
- const plan = buildCagePlan(root);
33
- const before = takeCageSnapshot(plan, { keepBytes: !reportOnly, maxFileBytes, maxTotalBytes });
34
- const proc = await spawnCaged(opts.command, cwd);
35
- const after = takeCageSnapshot(plan, { keepBytes: false, maxFileBytes, maxTotalBytes });
36
- const changes = resolveCageChanges(diffCageSnapshots(before, after), { reportOnly });
80
+ const plan = buildCagePlan(s.root);
81
+ const before = takeCageSnapshot(plan, s.snap);
82
+ const { proc, live, log, guarded } = await runSession(opts, s, plan, before);
83
+ const after = takeCageSnapshot(plan, { ...s.snap, keepBytes: false });
84
+ const changes = mergeChanges(live, resolveCageChanges(diffCageSnapshots(before, after), { reportOnly: s.reportOnly }));
37
85
  return buildCageReport({
38
86
  command: opts.command[0] ?? "",
39
87
  commandExitCode: proc.code,
40
88
  commandSignal: proc.signal,
41
89
  runtimeError: proc.spawnError,
42
- root,
43
- reportOnly,
44
- maxFileBytes,
90
+ root: s.root,
91
+ reportOnly: s.reportOnly,
92
+ maxFileBytes: s.snap.maxFileBytes,
45
93
  protectedFiles: before.entries.size,
46
94
  manifestZones: plan.manifestZoneCount,
47
95
  changes,
96
+ watch: {
97
+ enabled: s.watch,
98
+ interval_ms: s.watch ? s.intervalMs : null,
99
+ live_restores: live?.liveRestores ?? 0,
100
+ contested: live?.contested ?? [],
101
+ session_log: log?.path ?? null,
102
+ },
103
+ pushGuard: { enabled: guarded, refused_pushes: countRefusedPushes(log) },
48
104
  signalExitCode,
49
105
  });
50
106
  }
@@ -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,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/D,iFAAiF;AACjF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAC3D,kEAAkE;AAClE,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAW7D,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAoB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,2BAA2B,CAAC;IACtE,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,4BAA4B,CAAC;IACzE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC;IAE5C,qFAAqF;IACrF,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IAC/F,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAErF,OAAO,eAAe,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;QAC9B,eAAe,EAAE,IAAI,CAAC,IAAI;QAC1B,aAAa,EAAE,IAAI,CAAC,MAAM;QAC1B,YAAY,EAAE,IAAI,CAAC,UAAU;QAC7B,IAAI;QACJ,UAAU;QACV,YAAY;QACZ,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QACnC,aAAa,EAAE,IAAI,CAAC,iBAAiB;QACrC,OAAO;QACP,cAAc;KACf,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"cage-run.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-run.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"}
@@ -0,0 +1,92 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { resolveCageChanges } from "./cage-revert.js";
6
+ import { diffCageSnapshots, takeCageSnapshot } from "./cage-snapshot.js";
7
+ /** Default live-check interval while the caged command runs. */
8
+ export const CAGE_DEFAULT_WATCH_INTERVAL_MS = 1000;
9
+ /** Live restores of one path before cage stops fighting the command and restores it only at exit. */
10
+ export const CAGE_CONTEST_LIMIT = 3;
11
+ const BELL = "\x07";
12
+ export function openCageSessionLog(dir = os.tmpdir()) {
13
+ const file = path.join(dir, `gantry-cage-${String(process.pid)}-${crypto.randomBytes(4).toString("hex")}.log`);
14
+ fs.writeFileSync(file, "", { mode: 0o600 });
15
+ return {
16
+ path: file,
17
+ append(event) {
18
+ fs.appendFileSync(file, `${JSON.stringify({ at: new Date().toISOString(), ...event })}\n`);
19
+ },
20
+ };
21
+ }
22
+ function noteEvent(log, bell, event) {
23
+ log.append(event);
24
+ bell();
25
+ }
26
+ function handleLiveChange(change, state, log, bell) {
27
+ const base = { path: change.rel, kind: change.kind, rule: change.rule };
28
+ if (change.outcome === "reverted" || change.outcome === "removed") {
29
+ state.liveRestores += 1;
30
+ state.restored.set(change.abs, change);
31
+ noteEvent(log, bell, { event: "restored", ...base, outcome: change.outcome });
32
+ return;
33
+ }
34
+ if (!state.unrestorable.has(change.abs)) {
35
+ state.unrestorable.add(change.abs);
36
+ noteEvent(log, bell, { event: change.outcome, ...base, error_code: change.errorCode });
37
+ }
38
+ }
39
+ function tick(plan, baseline, opts, state, log, bell) {
40
+ const current = takeCageSnapshot(plan, { ...opts, keepBytes: false });
41
+ const pending = diffCageSnapshots(baseline, current).filter((c) => {
42
+ if (c.mode !== "revert" || state.contested.has(c.abs))
43
+ return false;
44
+ const count = state.restoresByPath.get(c.abs) ?? 0;
45
+ if (count < CAGE_CONTEST_LIMIT)
46
+ return true;
47
+ state.contested.add(c.abs);
48
+ noteEvent(log, bell, { event: "contested", path: c.rel, rule: c.rule, live_restores: count });
49
+ return false;
50
+ });
51
+ for (const change of resolveCageChanges(pending, { reportOnly: false })) {
52
+ if (change.outcome === "reverted" || change.outcome === "removed") {
53
+ state.restoresByPath.set(change.abs, (state.restoresByPath.get(change.abs) ?? 0) + 1);
54
+ }
55
+ handleLiveChange(change, state, log, bell);
56
+ }
57
+ }
58
+ /**
59
+ * Re-snapshot the protected set on an interval while the command runs and restore revert-rule changes
60
+ * from the baseline bytes. Lockfiles stay report-only and are left for the exit report. A path restored
61
+ * {@link CAGE_CONTEST_LIMIT} times is marked contested and restored only at exit.
62
+ */
63
+ export function startCageWatch(input) {
64
+ const bell = input.bell ?? (() => process.stderr.write(BELL));
65
+ const state = {
66
+ restoresByPath: new Map(),
67
+ contested: new Set(),
68
+ unrestorable: new Set(),
69
+ restored: new Map(),
70
+ liveRestores: 0,
71
+ };
72
+ const timer = setInterval(() => {
73
+ try {
74
+ tick(input.plan, input.baseline, input.snapshotOptions, state, input.log, bell);
75
+ }
76
+ catch (err) {
77
+ input.log.append({ event: "watch_error", message: err instanceof Error ? err.message : String(err) });
78
+ }
79
+ }, input.intervalMs);
80
+ return {
81
+ stop() {
82
+ clearInterval(timer);
83
+ const rel = (abs) => state.restored.get(abs)?.rel ?? path.relative(input.plan.root, abs).split(path.sep).join("/");
84
+ return {
85
+ liveRestores: state.liveRestores,
86
+ contested: [...state.contested].map(rel).sort(),
87
+ restored: state.restored,
88
+ };
89
+ },
90
+ };
91
+ }
92
+ //# sourceMappingURL=cage-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-watch.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-watch.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  /** Generated by scripts/gen-version.mjs — do not edit */
2
- export declare const CLI_VERSION = "3.7.0";
2
+ export declare const CLI_VERSION = "3.7.1";
3
3
  export declare const NPM_PACKAGE_NAME = "@jeger-ai/opengantry";
4
4
  //# sourceMappingURL=version.gen.d.ts.map
@@ -1,4 +1,4 @@
1
1
  /** Generated by scripts/gen-version.mjs — do not edit */
2
- export const CLI_VERSION = "3.7.0";
2
+ export const CLI_VERSION = "3.7.1";
3
3
  export const NPM_PACKAGE_NAME = "@jeger-ai/opengantry";
4
4
  //# sourceMappingURL=version.gen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"program-cage.d.ts","sourceRoot":"","sources":["../../src/cli/program-cage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAc1D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE3D"}
1
+ {"version":3,"file":"program-cage.d.ts","sourceRoot":"","sources":["../../src/cli/program-cage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAqB1D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU3D"}
@@ -1,10 +1,12 @@
1
- import { runCageCommand } from "./commands/cage.js";
1
+ import { runCageCommand, runCageGuardPrePush } from "./commands/cage.js";
2
2
  /** Shared by `gantry cage` and the standalone `opengantry-cage` bin so the two never drift. */
3
3
  export function configureCageCommand(cmd) {
4
4
  return cmd
5
- .description("Zero-config: run a command, then revert changes to CI configs, .env secrets, git hooks/config and manifest forbidden zones (lockfiles reported)")
5
+ .description("Zero-config: run a command and restore changes to CI configs, .env secrets, git hooks/config and manifest forbidden zones while it runs and at exit (lockfiles reported); refuses git pushes of protected paths from inside the session")
6
6
  .option("--json", "Emit the digest-only cage report as JSON on stdout")
7
7
  .option("--report-only", "Detect and report protected changes without reverting them")
8
+ .option("--no-watch", "Check only once, after the command exits (no live restore)")
9
+ .option("--watch-interval-ms <n>", "Live check interval while the command runs (default 1000)")
8
10
  .option("--max-file-bytes <n>", "Per-file in-memory snapshot cap; larger files are detect-only")
9
11
  .allowUnknownOption(true)
10
12
  .passThroughOptions()
@@ -15,5 +17,13 @@ export function configureCageCommand(cmd) {
15
17
  }
16
18
  export function registerCageCommands(program) {
17
19
  configureCageCommand(program.command("cage"));
20
+ program
21
+ .command("cage-guard", { hidden: true })
22
+ .description("Internal: checks run by the cage session git hooks")
23
+ .command("pre-push")
24
+ .requiredOption("--root <dir>", "Cage root (git work tree)")
25
+ .action(async (opts) => {
26
+ await runCageGuardPrePush(opts);
27
+ });
18
28
  }
19
29
  //# sourceMappingURL=program-cage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"program-cage.js","sourceRoot":"","sources":["../../src/cli/program-cage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,+FAA+F;AAC/F,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG;SACP,WAAW,CACV,iJAAiJ,CAClJ;SACA,MAAM,CAAC,QAAQ,EAAE,oDAAoD,CAAC;SACtE,MAAM,CAAC,eAAe,EAAE,4DAA4D,CAAC;SACrF,MAAM,CAAC,sBAAsB,EAAE,+DAA+D,CAAC;SAC/F,kBAAkB,CAAC,IAAI,CAAC;SACxB,kBAAkB,EAAE;SACpB,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,OAAiB,EAAE,IAAqE,EAAE,EAAE;QACzG,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"program-cage.js","sourceRoot":"","sources":["../../src/cli/program-cage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzE,+FAA+F;AAC/F,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG;SACP,WAAW,CACV,yOAAyO,CAC1O;SACA,MAAM,CAAC,QAAQ,EAAE,oDAAoD,CAAC;SACtE,MAAM,CAAC,eAAe,EAAE,4DAA4D,CAAC;SACrF,MAAM,CAAC,YAAY,EAAE,4DAA4D,CAAC;SAClF,MAAM,CAAC,yBAAyB,EAAE,2DAA2D,CAAC;SAC9F,MAAM,CAAC,sBAAsB,EAAE,+DAA+D,CAAC;SAC/F,kBAAkB,CAAC,IAAI,CAAC;SACxB,kBAAkB,EAAE;SACpB,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAC;SACnD,MAAM,CACL,KAAK,EACH,OAAiB,EACjB,IAAgH,EAChH,EAAE;QACF,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC,CACF,CAAC;AACN,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,OAAO;SACJ,OAAO,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SACvC,WAAW,CAAC,oDAAoD,CAAC;SACjE,OAAO,CAAC,UAAU,CAAC;SACnB,cAAc,CAAC,cAAc,EAAE,2BAA2B,CAAC;SAC3D,MAAM,CAAC,KAAK,EAAE,IAAsB,EAAE,EAAE;QACvC,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeger-ai/opengantry",
3
- "version": "3.7.0",
3
+ "version": "3.7.1",
4
4
  "description": "Open Source Gantry — vendor-neutral GXT substrate and Gantry CLI for local git-native agent governance",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "0.1.0",
3
- "opengantry_version": "3.7.0",
3
+ "opengantry_version": "3.7.1",
4
4
  "installed_at": "2026-01-01T00:00:00.000Z",
5
5
  "installed_by": "gantry init"
6
6
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "1",
3
- "opengantry_version": "3.7.0",
3
+ "opengantry_version": "3.7.1",
4
4
  "asset_catalog": "integrations/asset-catalog.json",
5
5
  "integrations": {
6
6
  "cursor": {