tamperward 2.29.18 → 2.29.20

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 (2) hide show
  1. package/dist/cli/index.js +68 -12
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -11638,14 +11638,31 @@ function watcherTelemetry(log) {
11638
11638
  return { state: "degraded", health, reason: health.last_error ?? "observer reported an error" };
11639
11639
  return { state: "healthy", health };
11640
11640
  }
11641
- function watchTree(dir, cb, onState, onError) {
11641
+ function watchTree(dir, cb, onState, onError, watchFn = watch) {
11642
+ const describe2 = (e) => e instanceof Error ? e.message : String(e);
11642
11643
  if (process.env.TAMPERWARD_WATCH_NO_RECURSIVE !== "1") {
11643
11644
  try {
11644
- const w = watch(dir, { recursive: true }, (kind, fname) => {
11645
+ let recursiveClosed = false;
11646
+ const closeRecursive = () => {
11647
+ if (recursiveClosed) return;
11648
+ recursiveClosed = true;
11649
+ w.close();
11650
+ };
11651
+ const w = watchFn(dir, { recursive: true }, (kind, fname) => {
11645
11652
  if (fname) cb(kind, String(fname));
11646
11653
  });
11654
+ w.on("error", (e) => {
11655
+ if (recursiveClosed) return;
11656
+ recursiveClosed = true;
11657
+ try {
11658
+ w.close();
11659
+ } catch {
11660
+ }
11661
+ onState("recursive", 0);
11662
+ onError(`recursive watch on ${dir}: ${describe2(e)}`);
11663
+ });
11647
11664
  onState("recursive", 1);
11648
- return { close: () => w.close() };
11665
+ return { close: closeRecursive };
11649
11666
  } catch {
11650
11667
  }
11651
11668
  }
@@ -11656,7 +11673,7 @@ function watchTree(dir, cb, onState, onError) {
11656
11673
  if (closed || watchers.has(rel) || SKIP2.test(rel + "/")) return;
11657
11674
  let w;
11658
11675
  try {
11659
- w = watch(rel ? join12(dir, rel) : dir, (kind, fname) => {
11676
+ w = watchFn(rel ? join12(dir, rel) : dir, (kind, fname) => {
11660
11677
  if (closed || !fname) return;
11661
11678
  const child = rel ? `${rel}/${String(fname)}` : String(fname);
11662
11679
  cb(kind, child);
@@ -11666,11 +11683,19 @@ function watchTree(dir, cb, onState, onError) {
11666
11683
  }
11667
11684
  });
11668
11685
  } catch (e) {
11669
- onError(
11670
- `watch directory ${rel || "."}: ${e instanceof Error ? e.message : String(e)}`
11671
- );
11686
+ onError(`watch directory ${rel || "."}: ${describe2(e)}`);
11672
11687
  return;
11673
11688
  }
11689
+ w.on("error", (e) => {
11690
+ if (closed || watchers.get(rel) !== w) return;
11691
+ watchers.delete(rel);
11692
+ try {
11693
+ w.close();
11694
+ } catch {
11695
+ }
11696
+ onError(`watch directory ${rel || "."}: ${describe2(e)}`);
11697
+ onState("fallback", watchers.size);
11698
+ });
11674
11699
  watchers.set(rel, w);
11675
11700
  onState("fallback", watchers.size);
11676
11701
  };
@@ -11704,7 +11729,7 @@ function watchTree(dir, cb, onState, onError) {
11704
11729
  }
11705
11730
  };
11706
11731
  }
11707
- function startWatcher(dir, log, policy) {
11732
+ function startWatcher(dir, log, policy, watchFn = watch) {
11708
11733
  try {
11709
11734
  mkdirSync4(join12(log, ".."), { recursive: true });
11710
11735
  } catch {
@@ -11784,7 +11809,8 @@ function startWatcher(dir, log, policy) {
11784
11809
  health.watched_dirs = watchedDirs;
11785
11810
  persistHealth();
11786
11811
  },
11787
- (detail) => degrade(detail)
11812
+ (detail) => degrade(detail),
11813
+ watchFn
11788
11814
  );
11789
11815
  let closed = false;
11790
11816
  return {
@@ -13024,6 +13050,8 @@ async function startHookService(opts) {
13024
13050
  const cache3 = opts.cache ?? new SnapshotCache();
13025
13051
  let served = 0;
13026
13052
  let inFlight = false;
13053
+ const sockets = /* @__PURE__ */ new Set();
13054
+ let acceptedSocket = null;
13027
13055
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
13028
13056
  const respond = (sock, body) => {
13029
13057
  try {
@@ -13056,6 +13084,7 @@ async function startHookService(opts) {
13056
13084
  if (named !== null && !within(root, named)) return respond(sock, { refused: "payload cwd outside the bound repository" });
13057
13085
  if (inFlight) return respond(sock, { refused: "another request is in flight" });
13058
13086
  inFlight = true;
13087
+ acceptedSocket = sock;
13059
13088
  sock.write(JSON.stringify({ v: HOOK_SERVICE_PROTOCOL, version: TW_VERSION, accepted: true }) + "\n");
13060
13089
  setImmediate(() => {
13061
13090
  try {
@@ -13074,24 +13103,38 @@ async function startHookService(opts) {
13074
13103
  respond(sock, { exitCode: result.exitCode, stdout: result.stdout });
13075
13104
  } finally {
13076
13105
  inFlight = false;
13106
+ acceptedSocket = null;
13077
13107
  }
13078
13108
  });
13079
13109
  };
13080
13110
  const server = createServer((sock) => {
13111
+ sockets.add(sock);
13081
13112
  let buf = "";
13082
13113
  let answered = false;
13083
13114
  sock.setEncoding("utf8");
13115
+ const deadline = setTimeout(() => {
13116
+ if (answered) return;
13117
+ answered = true;
13118
+ respond(sock, { refused: "request deadline exceeded" });
13119
+ }, REQUEST_DEADLINE_MS);
13120
+ deadline.unref?.();
13084
13121
  sock.on("error", () => sock.destroy());
13122
+ sock.on("close", () => {
13123
+ clearTimeout(deadline);
13124
+ sockets.delete(sock);
13125
+ });
13085
13126
  sock.on("data", (chunk) => {
13086
13127
  if (answered) return;
13087
13128
  buf += chunk;
13088
13129
  if (buf.length > MAX_REQUEST_BYTES) {
13089
13130
  answered = true;
13131
+ clearTimeout(deadline);
13090
13132
  return respond(sock, { refused: "request too large" });
13091
13133
  }
13092
13134
  const nl = buf.indexOf("\n");
13093
13135
  if (nl < 0) return;
13094
13136
  answered = true;
13137
+ clearTimeout(deadline);
13095
13138
  handle(sock, buf.slice(0, nl));
13096
13139
  });
13097
13140
  });
@@ -13122,11 +13165,22 @@ async function startHookService(opts) {
13122
13165
  if (closed) return resolve17();
13123
13166
  closed = true;
13124
13167
  setSnapshotCache(null);
13125
- server.close(() => {
13168
+ let settled = false;
13169
+ let drain;
13170
+ const finalize = () => {
13171
+ if (settled) return;
13172
+ settled = true;
13173
+ clearTimeout(drain);
13126
13174
  removeQuietly(paths.socket);
13127
13175
  removeQuietly(paths.state);
13128
13176
  resolve17();
13129
- });
13177
+ };
13178
+ server.close(finalize);
13179
+ for (const sock of sockets) if (sock !== acceptedSocket) sock.destroy();
13180
+ drain = setTimeout(() => {
13181
+ for (const sock of sockets) sock.destroy();
13182
+ }, SHUTDOWN_DRAIN_MS);
13183
+ drain.unref?.();
13130
13184
  })
13131
13185
  };
13132
13186
  }
@@ -13264,7 +13318,7 @@ function runHookService(args) {
13264
13318
  });
13265
13319
  return -1;
13266
13320
  }
13267
- var MAX_REQUEST_BYTES, STOP_WAIT_MS;
13321
+ var MAX_REQUEST_BYTES, STOP_WAIT_MS, REQUEST_DEADLINE_MS, SHUTDOWN_DRAIN_MS;
13268
13322
  var init_hook_service = __esm({
13269
13323
  "src/cli/hook-service.ts"() {
13270
13324
  "use strict";
@@ -13277,6 +13331,8 @@ var init_hook_service = __esm({
13277
13331
  init_hook_client();
13278
13332
  MAX_REQUEST_BYTES = 64 * 1024 * 1024;
13279
13333
  STOP_WAIT_MS = 1e4;
13334
+ REQUEST_DEADLINE_MS = 3e4;
13335
+ SHUTDOWN_DRAIN_MS = 5e3;
13280
13336
  }
13281
13337
  });
13282
13338
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.29.18",
3
+ "version": "2.29.20",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",