tamperward 2.23.4 → 2.23.6
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 +1 -1
- package/dist/cli/index.js +404 -292
- package/package.json +1 -1
- package/schemas/verify-v1.schema.json +6 -1
package/dist/cli/index.js
CHANGED
|
@@ -206,7 +206,7 @@ function forwardedEnv(env) {
|
|
|
206
206
|
return out3;
|
|
207
207
|
}
|
|
208
208
|
function exchange(paths, req2, timeoutMs) {
|
|
209
|
-
return new Promise((
|
|
209
|
+
return new Promise((resolve16) => {
|
|
210
210
|
let done = false;
|
|
211
211
|
let handedOff = false;
|
|
212
212
|
let accepted = false;
|
|
@@ -217,7 +217,7 @@ function exchange(paths, req2, timeoutMs) {
|
|
|
217
217
|
done = true;
|
|
218
218
|
clearTimeout(timer);
|
|
219
219
|
sock.destroy();
|
|
220
|
-
|
|
220
|
+
resolve16(value);
|
|
221
221
|
};
|
|
222
222
|
const sock = createConnection(paths.socket);
|
|
223
223
|
const timer = setTimeout(() => finish(handedOff ? acceptedFailure("timeout") : null), timeoutMs);
|
|
@@ -603,12 +603,12 @@ function irregularName(st) {
|
|
|
603
603
|
function meta(st) {
|
|
604
604
|
return { mode: st.mode, size: st.size, mtimeMs: st.mtimeMs };
|
|
605
605
|
}
|
|
606
|
-
function readRegular(
|
|
606
|
+
function readRegular(abs, st) {
|
|
607
607
|
let fd;
|
|
608
608
|
try {
|
|
609
|
-
fd = openSync(
|
|
609
|
+
fd = openSync(abs, O_READ);
|
|
610
610
|
} catch (e) {
|
|
611
|
-
if (errCode(e) === "ELOOP") return inspectOnce(
|
|
611
|
+
if (errCode(e) === "ELOOP") return inspectOnce(abs);
|
|
612
612
|
return { kind: "unreadable", content: null, ...meta(st), detail: errText(e) };
|
|
613
613
|
}
|
|
614
614
|
try {
|
|
@@ -629,10 +629,10 @@ function readRegular(abs2, st) {
|
|
|
629
629
|
closeSync(fd);
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
|
-
function classify(
|
|
632
|
+
function classify(abs, st) {
|
|
633
633
|
if (st.isSymbolicLink()) {
|
|
634
634
|
try {
|
|
635
|
-
const target = readlinkSync(
|
|
635
|
+
const target = readlinkSync(abs);
|
|
636
636
|
return { kind: "symlink", content: Buffer.from(target, "utf8"), ...meta(st), detail: target };
|
|
637
637
|
} catch (e) {
|
|
638
638
|
return { kind: "unreadable", content: null, ...meta(st), detail: errText(e) };
|
|
@@ -641,38 +641,38 @@ function classify(abs2, st) {
|
|
|
641
641
|
if (st.isDirectory()) return { kind: "directory", content: null, ...meta(st), detail: "directory" };
|
|
642
642
|
if (!st.isFile()) return { kind: "irregular", content: null, ...meta(st), detail: irregularName(st) };
|
|
643
643
|
if (st.size > READ_CAP) return { kind: "oversize", content: null, ...meta(st), detail: `${st.size} bytes` };
|
|
644
|
-
return readRegular(
|
|
644
|
+
return readRegular(abs, st);
|
|
645
645
|
}
|
|
646
|
-
function inspectOnce(
|
|
646
|
+
function inspectOnce(abs) {
|
|
647
647
|
let st;
|
|
648
648
|
try {
|
|
649
|
-
st = lstatSync2(
|
|
649
|
+
st = lstatSync2(abs);
|
|
650
650
|
} catch (e) {
|
|
651
651
|
const code2 = errCode(e);
|
|
652
652
|
if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
|
|
653
653
|
return { kind: "unreadable", content: null, ...NO_META, detail: errText(e) };
|
|
654
654
|
}
|
|
655
|
-
if (st.isSymbolicLink() || !st.isFile()) return classify(
|
|
655
|
+
if (st.isSymbolicLink() || !st.isFile()) return classify(abs, st);
|
|
656
656
|
return { kind: "unreadable", content: null, ...meta(st), detail: "the path changed shape while it was being read" };
|
|
657
657
|
}
|
|
658
|
-
function inspectPath(
|
|
658
|
+
function inspectPath(abs) {
|
|
659
659
|
let st;
|
|
660
660
|
try {
|
|
661
|
-
st = lstatSync2(
|
|
661
|
+
st = lstatSync2(abs);
|
|
662
662
|
} catch (e) {
|
|
663
663
|
const code2 = errCode(e);
|
|
664
664
|
if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
|
|
665
665
|
return { kind: "unreadable", content: null, ...NO_META, detail: errText(e) };
|
|
666
666
|
}
|
|
667
|
-
return classify(
|
|
667
|
+
return classify(abs, st);
|
|
668
668
|
}
|
|
669
669
|
function inspectRel(cwd, rel) {
|
|
670
670
|
return inspectPath(join3(cwd, rel));
|
|
671
671
|
}
|
|
672
|
-
function inspectResolved(
|
|
672
|
+
function inspectResolved(abs) {
|
|
673
673
|
let real;
|
|
674
674
|
try {
|
|
675
|
-
real = realpathSync2(
|
|
675
|
+
real = realpathSync2(abs);
|
|
676
676
|
} catch (e) {
|
|
677
677
|
const code2 = errCode(e);
|
|
678
678
|
if (code2 === "ENOENT" || code2 === "ENOTDIR") return { kind: "absent", content: null, ...NO_META, detail: "" };
|
|
@@ -1021,11 +1021,55 @@ var init_trusted = __esm({
|
|
|
1021
1021
|
}
|
|
1022
1022
|
});
|
|
1023
1023
|
|
|
1024
|
-
// src/
|
|
1024
|
+
// src/repo-context.ts
|
|
1025
1025
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
1026
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1027
|
+
import { isAbsolute as isAbsolute3, resolve } from "node:path";
|
|
1028
|
+
function rev(cwd, flag) {
|
|
1029
|
+
try {
|
|
1030
|
+
const out3 = execFileSync2("git", ["rev-parse", flag], {
|
|
1031
|
+
cwd,
|
|
1032
|
+
encoding: "utf8",
|
|
1033
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1034
|
+
}).trim();
|
|
1035
|
+
return out3 ? isAbsolute3(out3) ? out3 : resolve(cwd, out3) : null;
|
|
1036
|
+
} catch {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function repoContext(cwd) {
|
|
1041
|
+
const key2 = resolve(cwd);
|
|
1042
|
+
const hit = cache.get(key2);
|
|
1043
|
+
if (hit && existsSync2(hit.gitDir) && existsSync2(hit.root)) return hit;
|
|
1044
|
+
cache.delete(key2);
|
|
1045
|
+
const root = rev(key2, "--show-toplevel");
|
|
1046
|
+
const gitDir3 = root ? rev(key2, "--absolute-git-dir") : null;
|
|
1047
|
+
if (!root || !gitDir3) return null;
|
|
1048
|
+
const ctx = { root, gitDir: gitDir3 };
|
|
1049
|
+
if (cache.size >= CACHE_MAX) {
|
|
1050
|
+
const oldest = cache.keys().next().value;
|
|
1051
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
1052
|
+
}
|
|
1053
|
+
cache.set(key2, ctx);
|
|
1054
|
+
return ctx;
|
|
1055
|
+
}
|
|
1056
|
+
function repoRoot(cwd) {
|
|
1057
|
+
return repoContext(cwd)?.root ?? cwd;
|
|
1058
|
+
}
|
|
1059
|
+
var cache, CACHE_MAX;
|
|
1060
|
+
var init_repo_context = __esm({
|
|
1061
|
+
"src/repo-context.ts"() {
|
|
1062
|
+
"use strict";
|
|
1063
|
+
cache = /* @__PURE__ */ new Map();
|
|
1064
|
+
CACHE_MAX = 256;
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
// src/git/build.ts
|
|
1069
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1026
1070
|
function git(args, cwd) {
|
|
1027
1071
|
try {
|
|
1028
|
-
return
|
|
1072
|
+
return execFileSync3("git", args, {
|
|
1029
1073
|
cwd,
|
|
1030
1074
|
encoding: "utf8",
|
|
1031
1075
|
// 256 MiB, matching the hook adapter. Every diff below runs with `--text`, so a
|
|
@@ -1047,15 +1091,15 @@ function git(args, cwd) {
|
|
|
1047
1091
|
throw new Error(detail ? `git ${args[0]}: ${detail}` : failure.message || `git ${args[0]} failed`);
|
|
1048
1092
|
}
|
|
1049
1093
|
}
|
|
1050
|
-
function blobAt(
|
|
1094
|
+
function blobAt(rev2, path, cwd) {
|
|
1051
1095
|
try {
|
|
1052
|
-
return git(["show", `${
|
|
1096
|
+
return git(["show", `${rev2}:${path}`], cwd);
|
|
1053
1097
|
} catch {
|
|
1054
1098
|
return null;
|
|
1055
1099
|
}
|
|
1056
1100
|
}
|
|
1057
1101
|
function fromDisk(path, cwd) {
|
|
1058
|
-
return textOf2(inspectRel(cwd ?? process.cwd(), path));
|
|
1102
|
+
return textOf2(inspectRel(repoRoot(cwd ?? process.cwd()), path));
|
|
1059
1103
|
}
|
|
1060
1104
|
function enrich(c, beforeReader, afterReader) {
|
|
1061
1105
|
if (c.kind !== "file") return c;
|
|
@@ -1063,9 +1107,9 @@ function enrich(c, beforeReader, afterReader) {
|
|
|
1063
1107
|
const after = c.op !== "delete" ? afterReader(c.path) : null;
|
|
1064
1108
|
return { ...c, before, after };
|
|
1065
1109
|
}
|
|
1066
|
-
function assertRev(
|
|
1067
|
-
if (
|
|
1068
|
-
return
|
|
1110
|
+
function assertRev(rev2) {
|
|
1111
|
+
if (rev2.startsWith("-")) throw new Error(`refusing a revision that git would read as an option: ${rev2}`);
|
|
1112
|
+
return rev2;
|
|
1069
1113
|
}
|
|
1070
1114
|
function mergeBaseOf(base, head, opts = {}) {
|
|
1071
1115
|
try {
|
|
@@ -1074,8 +1118,8 @@ function mergeBaseOf(base, head, opts = {}) {
|
|
|
1074
1118
|
return base;
|
|
1075
1119
|
}
|
|
1076
1120
|
}
|
|
1077
|
-
function fileAt(
|
|
1078
|
-
return blobAt(
|
|
1121
|
+
function fileAt(rev2, path, opts = {}) {
|
|
1122
|
+
return blobAt(rev2, path, opts.cwd);
|
|
1079
1123
|
}
|
|
1080
1124
|
function fileOnDisk(path, opts = {}) {
|
|
1081
1125
|
return fromDisk(path, opts.cwd);
|
|
@@ -1102,10 +1146,10 @@ function headSha(cwd) {
|
|
|
1102
1146
|
return null;
|
|
1103
1147
|
}
|
|
1104
1148
|
}
|
|
1105
|
-
function diffSince(
|
|
1106
|
-
const raw = git([...DIFF_ARGS,
|
|
1149
|
+
function diffSince(rev2, opts = {}) {
|
|
1150
|
+
const raw = git([...DIFF_ARGS, rev2], opts.cwd);
|
|
1107
1151
|
return parseDiff(raw).map(
|
|
1108
|
-
(c) => enrich(c, (p) => blobAt(
|
|
1152
|
+
(c) => enrich(c, (p) => blobAt(rev2, p, opts.cwd), (p) => fromDisk(p, opts.cwd))
|
|
1109
1153
|
);
|
|
1110
1154
|
}
|
|
1111
1155
|
function diffRange(base, head, opts = {}) {
|
|
@@ -1195,6 +1239,7 @@ var init_build = __esm({
|
|
|
1195
1239
|
init_synth();
|
|
1196
1240
|
init_trusted();
|
|
1197
1241
|
init_narrow();
|
|
1242
|
+
init_repo_context();
|
|
1198
1243
|
DIFF_ARGS = ["diff", "--no-color", "--text", "-M"];
|
|
1199
1244
|
SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".hg", ".svn"]);
|
|
1200
1245
|
IGNORED_LS = ["ls-files", "--others", "--ignored", "--exclude-standard", "-z"];
|
|
@@ -1202,22 +1247,19 @@ var init_build = __esm({
|
|
|
1202
1247
|
});
|
|
1203
1248
|
|
|
1204
1249
|
// src/adapters/claude/changes.ts
|
|
1205
|
-
import { execFileSync as
|
|
1250
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
1206
1251
|
import { writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
1207
1252
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
1208
|
-
import { isAbsolute as
|
|
1253
|
+
import { isAbsolute as isAbsolute4, join as join5, relative, resolve as resolve2 } from "node:path";
|
|
1209
1254
|
function asStr(v) {
|
|
1210
1255
|
return typeof v === "string" ? v : "";
|
|
1211
1256
|
}
|
|
1212
1257
|
function readDisk(path) {
|
|
1213
1258
|
return textOf2(inspectResolved(path));
|
|
1214
1259
|
}
|
|
1215
|
-
function abs(path, cwd) {
|
|
1216
|
-
return resolve(cwd, path);
|
|
1217
|
-
}
|
|
1218
1260
|
function relForDisplay(path, cwd) {
|
|
1219
1261
|
const rel = relative(cwd, path);
|
|
1220
|
-
return rel && !rel.startsWith("..") && !
|
|
1262
|
+
return rel && !rel.startsWith("..") && !isAbsolute4(rel) ? rel : path;
|
|
1221
1263
|
}
|
|
1222
1264
|
function applyEdit(content, oldStr, newStr) {
|
|
1223
1265
|
if (content === null) return newStr;
|
|
@@ -1238,7 +1280,7 @@ function synthFileChange(displayPath, before, after) {
|
|
|
1238
1280
|
writeFileSync(a, before ?? "");
|
|
1239
1281
|
writeFileSync(b, after ?? "");
|
|
1240
1282
|
try {
|
|
1241
|
-
raw =
|
|
1283
|
+
raw = execFileSync4("git", ["diff", "--no-index", "--no-color", "--text", a, b], {
|
|
1242
1284
|
encoding: "utf8",
|
|
1243
1285
|
maxBuffer: 256 * 1024 * 1024
|
|
1244
1286
|
});
|
|
@@ -1256,8 +1298,9 @@ function synthFileChange(displayPath, before, after) {
|
|
|
1256
1298
|
const hunks = first && first.kind === "file" ? first.hunks : [];
|
|
1257
1299
|
return [{ kind: "file", path: displayPath, oldPath: null, op, before, after, binary: false, hunks }];
|
|
1258
1300
|
}
|
|
1259
|
-
function changesFromClaudeHook(input, cwd) {
|
|
1301
|
+
function changesFromClaudeHook(input, cwd, base = cwd) {
|
|
1260
1302
|
const ti = input.tool_input ?? {};
|
|
1303
|
+
const abs = (path) => resolve2(base, path);
|
|
1261
1304
|
switch (input.tool_name) {
|
|
1262
1305
|
case "Bash": {
|
|
1263
1306
|
const raw = asStr(ti.command);
|
|
@@ -1266,33 +1309,33 @@ function changesFromClaudeHook(input, cwd) {
|
|
|
1266
1309
|
case "Write": {
|
|
1267
1310
|
const fp = asStr(ti.file_path);
|
|
1268
1311
|
if (!fp) return [];
|
|
1269
|
-
const before = readDisk(abs(fp
|
|
1270
|
-
return synthFileChange(relForDisplay(abs(fp
|
|
1312
|
+
const before = readDisk(abs(fp));
|
|
1313
|
+
return synthFileChange(relForDisplay(abs(fp), cwd), before, asStr(ti.content));
|
|
1271
1314
|
}
|
|
1272
1315
|
case "Edit": {
|
|
1273
1316
|
const fp = asStr(ti.file_path);
|
|
1274
1317
|
if (!fp) return [];
|
|
1275
|
-
const before = readDisk(abs(fp
|
|
1318
|
+
const before = readDisk(abs(fp));
|
|
1276
1319
|
const after = applyEdit(before, asStr(ti.old_string), asStr(ti.new_string));
|
|
1277
|
-
return synthFileChange(relForDisplay(abs(fp
|
|
1320
|
+
return synthFileChange(relForDisplay(abs(fp), cwd), before, after);
|
|
1278
1321
|
}
|
|
1279
1322
|
case "MultiEdit": {
|
|
1280
1323
|
const fp = asStr(ti.file_path);
|
|
1281
1324
|
if (!fp) return [];
|
|
1282
|
-
const before = readDisk(abs(fp
|
|
1325
|
+
const before = readDisk(abs(fp));
|
|
1283
1326
|
let after = before;
|
|
1284
1327
|
const edits = Array.isArray(ti.edits) ? ti.edits : [];
|
|
1285
1328
|
for (const raw of edits) {
|
|
1286
1329
|
const ed = isRecord(raw) ? raw : {};
|
|
1287
1330
|
after = applyEdit(after, asStr(ed.old_string), asStr(ed.new_string));
|
|
1288
1331
|
}
|
|
1289
|
-
return synthFileChange(relForDisplay(abs(fp
|
|
1332
|
+
return synthFileChange(relForDisplay(abs(fp), cwd), before, after);
|
|
1290
1333
|
}
|
|
1291
1334
|
case "NotebookEdit": {
|
|
1292
1335
|
const fp = asStr(ti.notebook_path);
|
|
1293
1336
|
const src = asStr(ti.new_source);
|
|
1294
1337
|
if (!fp || !src) return [];
|
|
1295
|
-
return synthFileChange(relForDisplay(abs(fp
|
|
1338
|
+
return synthFileChange(relForDisplay(abs(fp), cwd), "", src);
|
|
1296
1339
|
}
|
|
1297
1340
|
default:
|
|
1298
1341
|
return [];
|
|
@@ -2506,18 +2549,18 @@ var init_test_skip = __esm({
|
|
|
2506
2549
|
});
|
|
2507
2550
|
|
|
2508
2551
|
// src/detectors/repo.ts
|
|
2509
|
-
import { execFileSync as
|
|
2552
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
2510
2553
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
2511
2554
|
import { join as join6 } from "node:path";
|
|
2512
2555
|
function trackedFiles(ctx) {
|
|
2513
2556
|
if (!ctx) return null;
|
|
2514
2557
|
if (ctx.trackedFiles) return ctx.trackedFiles;
|
|
2515
2558
|
if (!ctx.cwd) return null;
|
|
2516
|
-
const hit =
|
|
2559
|
+
const hit = cache2.get(ctx.cwd);
|
|
2517
2560
|
if (hit !== void 0) return hit;
|
|
2518
2561
|
let files = null;
|
|
2519
2562
|
try {
|
|
2520
|
-
files =
|
|
2563
|
+
files = execFileSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
2521
2564
|
cwd: ctx.cwd,
|
|
2522
2565
|
encoding: "utf8",
|
|
2523
2566
|
maxBuffer: 64 * 1024 * 1024,
|
|
@@ -2526,7 +2569,7 @@ function trackedFiles(ctx) {
|
|
|
2526
2569
|
} catch {
|
|
2527
2570
|
files = null;
|
|
2528
2571
|
}
|
|
2529
|
-
|
|
2572
|
+
cache2.set(ctx.cwd, files);
|
|
2530
2573
|
return files;
|
|
2531
2574
|
}
|
|
2532
2575
|
function trackedContent(path, ctx) {
|
|
@@ -2551,7 +2594,7 @@ function branchRefs(ctx) {
|
|
|
2551
2594
|
if (hit !== void 0) return hit;
|
|
2552
2595
|
let refs = null;
|
|
2553
2596
|
try {
|
|
2554
|
-
const out3 =
|
|
2597
|
+
const out3 = execFileSync5("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads", "refs/remotes"], {
|
|
2555
2598
|
cwd: ctx.cwd,
|
|
2556
2599
|
encoding: "utf8",
|
|
2557
2600
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2570,11 +2613,11 @@ function branchExists(name, ctx) {
|
|
|
2570
2613
|
for (const r of refs) if (r.endsWith("/" + name)) return true;
|
|
2571
2614
|
return false;
|
|
2572
2615
|
}
|
|
2573
|
-
function revIsHead(
|
|
2574
|
-
if (!ctx?.cwd ||
|
|
2616
|
+
function revIsHead(rev2, ctx) {
|
|
2617
|
+
if (!ctx?.cwd || rev2.startsWith("-")) return null;
|
|
2575
2618
|
const parse = (r) => {
|
|
2576
2619
|
try {
|
|
2577
|
-
return
|
|
2620
|
+
return execFileSync5("git", ["rev-parse", "--verify", "--quiet", "--end-of-options", `${r}^{commit}`], {
|
|
2578
2621
|
cwd: ctx.cwd,
|
|
2579
2622
|
encoding: "utf8",
|
|
2580
2623
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2584,7 +2627,7 @@ function revIsHead(rev, ctx) {
|
|
|
2584
2627
|
}
|
|
2585
2628
|
};
|
|
2586
2629
|
const head = parse("HEAD");
|
|
2587
|
-
const target = parse(
|
|
2630
|
+
const target = parse(rev2);
|
|
2588
2631
|
if (!head || !target) return null;
|
|
2589
2632
|
return head === target;
|
|
2590
2633
|
}
|
|
@@ -2594,7 +2637,7 @@ function defaultBranch(ctx) {
|
|
|
2594
2637
|
if (hit !== void 0) return hit;
|
|
2595
2638
|
let name = null;
|
|
2596
2639
|
try {
|
|
2597
|
-
const ref =
|
|
2640
|
+
const ref = execFileSync5("git", ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"], {
|
|
2598
2641
|
cwd: ctx.cwd,
|
|
2599
2642
|
encoding: "utf8",
|
|
2600
2643
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2619,12 +2662,12 @@ function containsProtected(token, policy, category, ctx, except) {
|
|
|
2619
2662
|
const base = dir.split("/").pop() ?? "";
|
|
2620
2663
|
return category === "tests" && /^(?:test|tests|__tests__|spec|specs)$/i.test(base);
|
|
2621
2664
|
}
|
|
2622
|
-
var
|
|
2665
|
+
var cache2, contentCache, branchCache, refsCache, dirOf;
|
|
2623
2666
|
var init_repo = __esm({
|
|
2624
2667
|
"src/detectors/repo.ts"() {
|
|
2625
2668
|
"use strict";
|
|
2626
2669
|
init_policy();
|
|
2627
|
-
|
|
2670
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
2628
2671
|
contentCache = /* @__PURE__ */ new Map();
|
|
2629
2672
|
branchCache = /* @__PURE__ */ new Map();
|
|
2630
2673
|
refsCache = /* @__PURE__ */ new Map();
|
|
@@ -2645,8 +2688,8 @@ function numericOf(e) {
|
|
|
2645
2688
|
}
|
|
2646
2689
|
return void 0;
|
|
2647
2690
|
}
|
|
2648
|
-
function metricsOf(expr,
|
|
2649
|
-
const obj =
|
|
2691
|
+
function metricsOf(expr, resolve16) {
|
|
2692
|
+
const obj = resolve16(expr);
|
|
2650
2693
|
if (!ts.isObjectLiteralExpression(obj)) return void 0;
|
|
2651
2694
|
const out3 = { values: {}, opaque: false };
|
|
2652
2695
|
let any = false;
|
|
@@ -2660,7 +2703,7 @@ function metricsOf(expr, resolve15) {
|
|
|
2660
2703
|
const k = keyName(p.name);
|
|
2661
2704
|
if (k === null || !isMetric(k)) continue;
|
|
2662
2705
|
any = true;
|
|
2663
|
-
const n = numericOf(
|
|
2706
|
+
const n = numericOf(resolve16(p.initializer));
|
|
2664
2707
|
if (n === void 0) out3.opaque = true;
|
|
2665
2708
|
else out3.values[k] = n;
|
|
2666
2709
|
}
|
|
@@ -2706,17 +2749,17 @@ function parseThresholds(src) {
|
|
|
2706
2749
|
const res = { paths: /* @__PURE__ */ new Map(), present: false, opaque: false };
|
|
2707
2750
|
try {
|
|
2708
2751
|
const sf = sourceOf(src);
|
|
2709
|
-
const
|
|
2752
|
+
const resolve16 = resolverFor(sf);
|
|
2710
2753
|
const visit = (node) => {
|
|
2711
2754
|
if (ts.isPropertyAssignment(node)) {
|
|
2712
2755
|
const key2 = keyName(node.name);
|
|
2713
2756
|
if (key2 === "coverageThreshold" || key2 === "thresholds" && underKey(node, "coverage")) {
|
|
2714
2757
|
res.present = true;
|
|
2715
|
-
const init =
|
|
2758
|
+
const init = resolve16(node.initializer);
|
|
2716
2759
|
if (!ts.isObjectLiteralExpression(init)) {
|
|
2717
2760
|
res.opaque = true;
|
|
2718
2761
|
} else {
|
|
2719
|
-
const flat = metricsOf(init,
|
|
2762
|
+
const flat = metricsOf(init, resolve16);
|
|
2720
2763
|
if (flat) res.global = merge(res.global, flat);
|
|
2721
2764
|
for (const p of init.properties) {
|
|
2722
2765
|
if (ts.isSpreadAssignment(p)) {
|
|
@@ -2730,7 +2773,7 @@ function parseThresholds(src) {
|
|
|
2730
2773
|
res.global = merge(res.global, { values: { branches: 100, functions: 100, lines: 100, statements: 100 }, opaque: false });
|
|
2731
2774
|
continue;
|
|
2732
2775
|
}
|
|
2733
|
-
const m = metricsOf(p.initializer,
|
|
2776
|
+
const m = metricsOf(p.initializer, resolve16);
|
|
2734
2777
|
if (!m) continue;
|
|
2735
2778
|
if (k === "global") res.global = merge(res.global, m);
|
|
2736
2779
|
else res.paths.set(norm(k), m);
|
|
@@ -2763,18 +2806,18 @@ function parseLists(src) {
|
|
|
2763
2806
|
const res = { collectFrom: null, collectKey: "collectCoverageFrom", ignore: [], opaqueCollect: false, opaqueIgnore: false };
|
|
2764
2807
|
try {
|
|
2765
2808
|
const sf = sourceOf(src);
|
|
2766
|
-
const
|
|
2809
|
+
const resolve16 = resolverFor(sf);
|
|
2767
2810
|
const visit = (node) => {
|
|
2768
2811
|
if (ts.isPropertyAssignment(node)) {
|
|
2769
2812
|
const k = keyName(node.name);
|
|
2770
2813
|
const vitestCoverage = ownerKey(node) === "coverage";
|
|
2771
2814
|
if (k === "collectCoverageFrom" || k === "include" && vitestCoverage) {
|
|
2772
|
-
const { items, opaque } = literals(
|
|
2815
|
+
const { items, opaque } = literals(resolve16(node.initializer));
|
|
2773
2816
|
res.collectFrom = items;
|
|
2774
2817
|
res.collectKey = k === "include" ? "coverage.include" : k;
|
|
2775
2818
|
if (opaque) res.opaqueCollect = true;
|
|
2776
2819
|
} else if (k === "coveragePathIgnorePatterns" || k === "exclude" && vitestCoverage) {
|
|
2777
|
-
const { items, opaque } = literals(
|
|
2820
|
+
const { items, opaque } = literals(resolve16(node.initializer));
|
|
2778
2821
|
res.ignore.push(...items);
|
|
2779
2822
|
if (opaque) res.opaqueIgnore = true;
|
|
2780
2823
|
}
|
|
@@ -4047,8 +4090,8 @@ var init_policy_diff = __esm({
|
|
|
4047
4090
|
});
|
|
4048
4091
|
|
|
4049
4092
|
// src/detectors/hook-wiring.ts
|
|
4050
|
-
import { existsSync as
|
|
4051
|
-
import { isAbsolute as
|
|
4093
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
4094
|
+
import { isAbsolute as isAbsolute5, resolve as resolve3 } from "node:path";
|
|
4052
4095
|
function stripComment(line) {
|
|
4053
4096
|
let single = false;
|
|
4054
4097
|
let double = false;
|
|
@@ -5066,7 +5109,7 @@ function expandShellPath(t, env = process.env) {
|
|
|
5066
5109
|
}
|
|
5067
5110
|
function hookTests(policy, ctx) {
|
|
5068
5111
|
const cwd = ctx?.cwd;
|
|
5069
|
-
const diskCwd = cwd !== void 0 &&
|
|
5112
|
+
const diskCwd = cwd !== void 0 && existsSync3(cwd) ? cwd : void 0;
|
|
5070
5113
|
const onDisk = diskCwd !== void 0;
|
|
5071
5114
|
const root = diskCwd !== void 0 ? canonicalPath(diskCwd) : cwd;
|
|
5072
5115
|
const memo = /* @__PURE__ */ new Map();
|
|
@@ -5074,10 +5117,10 @@ function hookTests(policy, ctx) {
|
|
|
5074
5117
|
const hit = memo.get(t);
|
|
5075
5118
|
if (hit !== void 0) return hit;
|
|
5076
5119
|
let p = expandShellPath(t.replace(/^["']|["']$/g, ""));
|
|
5077
|
-
const pathy =
|
|
5078
|
-
const
|
|
5079
|
-
if (pathy &&
|
|
5080
|
-
p = canonicalPath(
|
|
5120
|
+
const pathy = isAbsolute5(p) || p.includes("/") && !/^[-:]/.test(p);
|
|
5121
|
+
const abs = isAbsolute5(p) ? p : diskCwd !== void 0 ? resolve3(diskCwd, p) : null;
|
|
5122
|
+
if (pathy && abs !== null) {
|
|
5123
|
+
p = canonicalPath(abs);
|
|
5081
5124
|
if (root !== void 0) {
|
|
5082
5125
|
if (p === root) p = ".";
|
|
5083
5126
|
else if (p.startsWith(root + "/")) p = p.slice(root.length + 1);
|
|
@@ -5350,8 +5393,8 @@ var init_hook_wiring = __esm({
|
|
|
5350
5393
|
});
|
|
5351
5394
|
|
|
5352
5395
|
// src/detectors/hook-tampering.ts
|
|
5353
|
-
import { existsSync as
|
|
5354
|
-
import { isAbsolute as
|
|
5396
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
5397
|
+
import { isAbsolute as isAbsolute6, join as join7, relative as relative2 } from "node:path";
|
|
5355
5398
|
function parseSettings(src) {
|
|
5356
5399
|
try {
|
|
5357
5400
|
const v = JSON.parse(src);
|
|
@@ -5723,7 +5766,7 @@ function lefthookBase(c, changes, ctx) {
|
|
|
5723
5766
|
for (const n of names) {
|
|
5724
5767
|
try {
|
|
5725
5768
|
const p = join7(ctx.cwd, n);
|
|
5726
|
-
if (
|
|
5769
|
+
if (existsSync4(p)) return parseDoc(readFileSync4(p, "utf8"));
|
|
5727
5770
|
} catch {
|
|
5728
5771
|
}
|
|
5729
5772
|
}
|
|
@@ -5793,9 +5836,9 @@ function updateIndexHiding(toks, policy) {
|
|
|
5793
5836
|
return null;
|
|
5794
5837
|
}
|
|
5795
5838
|
function repoRelative(path, ctx) {
|
|
5796
|
-
if (!ctx?.cwd || !
|
|
5839
|
+
if (!ctx?.cwd || !isAbsolute6(path)) return path;
|
|
5797
5840
|
const rel = relative2(ctx.cwd, path);
|
|
5798
|
-
return rel && !rel.startsWith("..") && !
|
|
5841
|
+
return rel && !rel.startsWith("..") && !isAbsolute6(rel) ? rel : path;
|
|
5799
5842
|
}
|
|
5800
5843
|
function huskyRuntimeWrite(path, content, ctx) {
|
|
5801
5844
|
const m = path.match(/(?:^|\/)\.husky\/_\/([^/]+)$/);
|
|
@@ -6133,7 +6176,7 @@ They WILL FAIL in v10.0.0
|
|
|
6133
6176
|
if (why) {
|
|
6134
6177
|
const target = shellHookTarget(seg, toks, policy, ctx) ?? shellHookTarget("", fed, policy, ctx);
|
|
6135
6178
|
const file = target ? repoRelative(target, ctx) : void 0;
|
|
6136
|
-
const outside = file !== void 0 && (
|
|
6179
|
+
const outside = file !== void 0 && (isAbsolute6(file) || /^[A-Za-z]:\//.test(file)) && isClaudeSettings(file) ? ` \u2014 ${file} is a Claude Code settings file the runtime reads hooks from` : "";
|
|
6137
6180
|
out3.push(
|
|
6138
6181
|
makeFinding(RULE7, policy, {
|
|
6139
6182
|
...file !== void 0 ? { file } : {},
|
|
@@ -6941,8 +6984,8 @@ var init_test_deletion = __esm({
|
|
|
6941
6984
|
const gitRestoresOld = cmd === "git" && /^git\s+(?:checkout|restore)\b/.test(seg) && (() => {
|
|
6942
6985
|
const src = seg.match(/--source[= ]\s*(\S+)/)?.[1];
|
|
6943
6986
|
const revIdx = toks.findIndex((t, i) => i >= 2 && !t.startsWith("-") && !named.includes(t) && t !== "--");
|
|
6944
|
-
const
|
|
6945
|
-
if (!
|
|
6987
|
+
const rev2 = src ?? (revIdx >= 0 ? toks[revIdx] : void 0);
|
|
6988
|
+
if (!rev2 || rev2 === "HEAD" || rev2 === "@" || revIsHead(rev2, ctx) === true) return false;
|
|
6946
6989
|
if (src || seg.includes(" -- ")) return true;
|
|
6947
6990
|
return revIdx >= 0 && toks.some((t, i) => i > revIdx && named.includes(t));
|
|
6948
6991
|
})();
|
|
@@ -7556,8 +7599,8 @@ var init_engine = __esm({
|
|
|
7556
7599
|
});
|
|
7557
7600
|
|
|
7558
7601
|
// src/policy-load.ts
|
|
7559
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
7560
|
-
import { isAbsolute as
|
|
7602
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
|
|
7603
|
+
import { isAbsolute as isAbsolute7, join as join8, posix as posix2 } from "node:path";
|
|
7561
7604
|
function normalizeVersion(v, where = POLICY_FILE) {
|
|
7562
7605
|
if (v === void 0 || v === null) return 1;
|
|
7563
7606
|
if (typeof v !== "number" || !Number.isInteger(v) || v < 1) {
|
|
@@ -7566,7 +7609,7 @@ function normalizeVersion(v, where = POLICY_FILE) {
|
|
|
7566
7609
|
return v;
|
|
7567
7610
|
}
|
|
7568
7611
|
function ledgerInsideRepo(ledger) {
|
|
7569
|
-
if (!ledger.trim() ||
|
|
7612
|
+
if (!ledger.trim() || isAbsolute7(ledger) || /^[A-Za-z]:[\\/]/.test(ledger)) return false;
|
|
7570
7613
|
const norm2 = posix2.normalize(ledger.replace(/\\/g, "/"));
|
|
7571
7614
|
return norm2 !== ".." && !norm2.startsWith("../") && !norm2.startsWith("/");
|
|
7572
7615
|
}
|
|
@@ -7717,14 +7760,14 @@ function parseOrThrow(src, where) {
|
|
|
7717
7760
|
return parsePolicy(raw, where);
|
|
7718
7761
|
}
|
|
7719
7762
|
function loadPolicy(cwd = process.cwd()) {
|
|
7720
|
-
const path = join8(cwd, POLICY_FILE);
|
|
7721
|
-
if (!
|
|
7763
|
+
const path = join8(repoRoot(cwd), POLICY_FILE);
|
|
7764
|
+
if (!existsSync5(path)) return defaultPolicy();
|
|
7722
7765
|
return parseOrThrow(readFileSync5(path, "utf8"), POLICY_FILE);
|
|
7723
7766
|
}
|
|
7724
|
-
function loadPolicyAt(
|
|
7725
|
-
const src = fileAt(
|
|
7767
|
+
function loadPolicyAt(rev2, cwd) {
|
|
7768
|
+
const src = fileAt(rev2, POLICY_FILE, { cwd: cwd === void 0 ? void 0 : repoRoot(cwd) });
|
|
7726
7769
|
if (src == null) return null;
|
|
7727
|
-
return parseOrThrow(src, `${POLICY_FILE} at ${
|
|
7770
|
+
return parseOrThrow(src, `${POLICY_FILE} at ${rev2}`);
|
|
7728
7771
|
}
|
|
7729
7772
|
var PolicyError, SEVERITIES, isSeverity, isStringList, isMapping, TOP_LEVEL_KEYS;
|
|
7730
7773
|
var init_policy_load = __esm({
|
|
@@ -7734,6 +7777,7 @@ var init_policy_load = __esm({
|
|
|
7734
7777
|
init_policy();
|
|
7735
7778
|
init_build();
|
|
7736
7779
|
init_narrow();
|
|
7780
|
+
init_repo_context();
|
|
7737
7781
|
PolicyError = class extends Error {
|
|
7738
7782
|
};
|
|
7739
7783
|
SEVERITIES = ["block", "warn"];
|
|
@@ -7746,7 +7790,7 @@ var init_policy_load = __esm({
|
|
|
7746
7790
|
|
|
7747
7791
|
// src/signoff.ts
|
|
7748
7792
|
import { createHash as createHash2 } from "node:crypto";
|
|
7749
|
-
import { appendFileSync, mkdirSync, readFileSync as readFileSync6, existsSync as
|
|
7793
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync6, existsSync as existsSync6 } from "node:fs";
|
|
7750
7794
|
import { dirname as dirname2, join as join9 } from "node:path";
|
|
7751
7795
|
function ledgerEntryFrom(value) {
|
|
7752
7796
|
if (!isRecord(value)) return null;
|
|
@@ -7768,7 +7812,7 @@ function ledgerPath(cwd, policy) {
|
|
|
7768
7812
|
}
|
|
7769
7813
|
function readLedger(cwd, policy) {
|
|
7770
7814
|
const p = ledgerPath(cwd, policy);
|
|
7771
|
-
if (!
|
|
7815
|
+
if (!existsSync6(p)) return [];
|
|
7772
7816
|
const out3 = [];
|
|
7773
7817
|
for (const line of readFileSync6(p, "utf8").split("\n")) {
|
|
7774
7818
|
if (!line.trim()) continue;
|
|
@@ -8131,27 +8175,28 @@ var init_report = __esm({
|
|
|
8131
8175
|
|
|
8132
8176
|
// src/cli/check.ts
|
|
8133
8177
|
function check(opts) {
|
|
8134
|
-
const
|
|
8135
|
-
if (!isGitRepo(
|
|
8136
|
-
process.stderr.write(`tamperward: ${
|
|
8178
|
+
const requested = opts.cwd ?? process.cwd();
|
|
8179
|
+
if (!isGitRepo(requested)) {
|
|
8180
|
+
process.stderr.write(`tamperward: ${requested} is not inside a git repository \u2014 nothing to check
|
|
8137
8181
|
`);
|
|
8138
8182
|
return 2;
|
|
8139
8183
|
}
|
|
8140
|
-
|
|
8184
|
+
const cwd = repoRoot(requested);
|
|
8185
|
+
let policy = opts.policyOverride ?? loadPolicy(cwd);
|
|
8141
8186
|
let changes;
|
|
8142
8187
|
let layer;
|
|
8143
8188
|
let view;
|
|
8144
8189
|
if (opts.staged) {
|
|
8145
|
-
changes = diffStaged({ cwd
|
|
8190
|
+
changes = diffStaged({ cwd });
|
|
8146
8191
|
layer = "local";
|
|
8147
8192
|
view = "staged";
|
|
8148
8193
|
} else if (opts.worktree) {
|
|
8149
8194
|
const p = policy;
|
|
8150
|
-
changes = opts.includeUntracked ? diffWorktreeWithUntracked({ cwd
|
|
8195
|
+
changes = opts.includeUntracked ? diffWorktreeWithUntracked({ cwd }, (rel) => isProtected(rel, p)) : diffWorktree({ cwd });
|
|
8151
8196
|
const seen = new Set(changes.filter((c) => c.kind === "file").map((c) => c.path));
|
|
8152
|
-
for (const rel of hiddenTrackedPaths({ cwd
|
|
8197
|
+
for (const rel of hiddenTrackedPaths({ cwd })) {
|
|
8153
8198
|
if (seen.has(rel) || !isProtected(rel, p)) continue;
|
|
8154
|
-
changes.push(...synthFileChange(rel, fileAt("HEAD", rel, { cwd
|
|
8199
|
+
changes.push(...synthFileChange(rel, fileAt("HEAD", rel, { cwd }), fileOnDisk(rel, { cwd })));
|
|
8155
8200
|
}
|
|
8156
8201
|
layer = opts.ciLayer ? "ci" : "local";
|
|
8157
8202
|
view = "worktree";
|
|
@@ -8163,7 +8208,7 @@ function check(opts) {
|
|
|
8163
8208
|
`);
|
|
8164
8209
|
return 2;
|
|
8165
8210
|
}
|
|
8166
|
-
changes = diffRange(base, head, { cwd
|
|
8211
|
+
changes = diffRange(base, head, { cwd });
|
|
8167
8212
|
if (changes.length === 0) {
|
|
8168
8213
|
process.stderr.write(
|
|
8169
8214
|
`tamperward: note: the range ${base}...${head} contains no changes \u2014 if this is not an empty pull request, check that the range resolves to the intended commits
|
|
@@ -8172,7 +8217,7 @@ function check(opts) {
|
|
|
8172
8217
|
}
|
|
8173
8218
|
layer = "ci";
|
|
8174
8219
|
view = "range";
|
|
8175
|
-
policy = opts.policyOverride ?? loadPolicyAt(mergeBaseOf(base, head, { cwd
|
|
8220
|
+
policy = opts.policyOverride ?? loadPolicyAt(mergeBaseOf(base, head, { cwd }), cwd) ?? defaultPolicy();
|
|
8176
8221
|
} else {
|
|
8177
8222
|
process.stderr.write("tamperward: specify --staged, --worktree, or --diff <base>...<head>\n");
|
|
8178
8223
|
return 2;
|
|
@@ -8215,6 +8260,7 @@ var init_check = __esm({
|
|
|
8215
8260
|
init_disk();
|
|
8216
8261
|
init_signoff();
|
|
8217
8262
|
init_report();
|
|
8263
|
+
init_repo_context();
|
|
8218
8264
|
}
|
|
8219
8265
|
});
|
|
8220
8266
|
|
|
@@ -8244,7 +8290,7 @@ var init_deny = __esm({
|
|
|
8244
8290
|
});
|
|
8245
8291
|
|
|
8246
8292
|
// src/session.ts
|
|
8247
|
-
import { existsSync as
|
|
8293
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8248
8294
|
import { dirname as dirname3, join as join10 } from "node:path";
|
|
8249
8295
|
function baselinePath(cwd, sessionId) {
|
|
8250
8296
|
const gd = gitDir(cwd);
|
|
@@ -8256,7 +8302,7 @@ function turnBaseline(cwd, sessionId) {
|
|
|
8256
8302
|
try {
|
|
8257
8303
|
const p = baselinePath(cwd, sessionId);
|
|
8258
8304
|
if (!p) return null;
|
|
8259
|
-
if (
|
|
8305
|
+
if (existsSync7(p)) {
|
|
8260
8306
|
const v = readFileSync7(p, "utf8").trim();
|
|
8261
8307
|
if (SHA.test(v)) return v;
|
|
8262
8308
|
}
|
|
@@ -8292,9 +8338,8 @@ var init_session = __esm({
|
|
|
8292
8338
|
|
|
8293
8339
|
// src/effect.ts
|
|
8294
8340
|
import { createHash as createHash3 } from "node:crypto";
|
|
8295
|
-
import { existsSync as
|
|
8296
|
-
import {
|
|
8297
|
-
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
8341
|
+
import { existsSync as existsSync8, lstatSync as lstatSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync8, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8342
|
+
import { join as join11 } from "node:path";
|
|
8298
8343
|
function ptreeFrom(value) {
|
|
8299
8344
|
if (!isRecord(value)) return null;
|
|
8300
8345
|
const out3 = {};
|
|
@@ -8307,17 +8352,7 @@ function ptreeFrom(value) {
|
|
|
8307
8352
|
return out3;
|
|
8308
8353
|
}
|
|
8309
8354
|
function gitDir2(cwd) {
|
|
8310
|
-
|
|
8311
|
-
const d = execFileSync5("git", ["rev-parse", "--git-dir"], {
|
|
8312
|
-
cwd,
|
|
8313
|
-
encoding: "utf8",
|
|
8314
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
8315
|
-
}).trim();
|
|
8316
|
-
if (!d) return null;
|
|
8317
|
-
return isAbsolute7(d) ? d : join11(cwd, d);
|
|
8318
|
-
} catch {
|
|
8319
|
-
return null;
|
|
8320
|
-
}
|
|
8355
|
+
return repoContext(cwd)?.gitDir ?? null;
|
|
8321
8356
|
}
|
|
8322
8357
|
function statePath(cwd, sessionId, kind) {
|
|
8323
8358
|
if (!sessionId) return null;
|
|
@@ -8339,10 +8374,11 @@ function entryOf(e) {
|
|
|
8339
8374
|
const hash = e.content != null ? sha(e.content) : e.kind === "unreadable" ? "unreadable" : `${e.kind}:${e.detail}`;
|
|
8340
8375
|
return { hash, mode: e.mode, size: e.size, mtimeMs: e.mtimeMs };
|
|
8341
8376
|
}
|
|
8342
|
-
function snapshotProtected(cwd, policy, prev,
|
|
8377
|
+
function snapshotProtected(cwd, policy, prev, cache3) {
|
|
8343
8378
|
void prev;
|
|
8344
|
-
|
|
8345
|
-
|
|
8379
|
+
cwd = repoRoot(cwd);
|
|
8380
|
+
cache3?.beginSnapshot();
|
|
8381
|
+
const entryAt = (rel) => cache3 ? cache3.entry(cwd, rel, () => entryOf(inspectRel(cwd, rel))) : entryOf(inspectRel(cwd, rel));
|
|
8346
8382
|
const out3 = {};
|
|
8347
8383
|
const keep = (rel) => isProtected(rel, policy);
|
|
8348
8384
|
let pruned = /* @__PURE__ */ new Set();
|
|
@@ -8354,10 +8390,10 @@ function snapshotProtected(cwd, policy, prev, cache2) {
|
|
|
8354
8390
|
} catch {
|
|
8355
8391
|
}
|
|
8356
8392
|
const walk2 = (rel) => {
|
|
8357
|
-
const
|
|
8393
|
+
const abs = rel ? join11(cwd, rel) : cwd;
|
|
8358
8394
|
let names;
|
|
8359
8395
|
try {
|
|
8360
|
-
names = readdirSync(
|
|
8396
|
+
names = readdirSync(abs);
|
|
8361
8397
|
} catch {
|
|
8362
8398
|
return;
|
|
8363
8399
|
}
|
|
@@ -8394,7 +8430,7 @@ function driftBetween(expected, current) {
|
|
|
8394
8430
|
return { changed, deleted, added };
|
|
8395
8431
|
}
|
|
8396
8432
|
function loadTree(p) {
|
|
8397
|
-
if (!p || !
|
|
8433
|
+
if (!p || !existsSync8(p)) return null;
|
|
8398
8434
|
try {
|
|
8399
8435
|
return ptreeFrom(JSON.parse(readFileSync8(p, "utf8")));
|
|
8400
8436
|
} catch {
|
|
@@ -8437,6 +8473,7 @@ var UNSAFE2, SKIP_DIRS2, sha;
|
|
|
8437
8473
|
var init_effect = __esm({
|
|
8438
8474
|
"src/effect.ts"() {
|
|
8439
8475
|
"use strict";
|
|
8476
|
+
init_repo_context();
|
|
8440
8477
|
init_policy();
|
|
8441
8478
|
init_disk();
|
|
8442
8479
|
init_build();
|
|
@@ -8451,8 +8488,8 @@ var init_effect = __esm({
|
|
|
8451
8488
|
import { appendFileSync as appendFileSync3, lstatSync as lstatSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync9, readdirSync as readdirSync2, watch, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8452
8489
|
import { join as join12 } from "node:path";
|
|
8453
8490
|
import { createHash as createHash4 } from "node:crypto";
|
|
8454
|
-
function snap(
|
|
8455
|
-
const e = inspectPath(
|
|
8491
|
+
function snap(abs) {
|
|
8492
|
+
const e = inspectPath(abs);
|
|
8456
8493
|
if (e.kind === "absent" || e.kind === "unreadable") return { mode: null, size: null, hash: null };
|
|
8457
8494
|
if (e.kind !== "file" || e.content == null) return { mode: e.mode, size: null, hash: null };
|
|
8458
8495
|
const hash = createHash4("sha256").update(e.content).digest("hex").slice(0, 16);
|
|
@@ -8968,10 +9005,10 @@ var init_fs_events = __esm({
|
|
|
8968
9005
|
});
|
|
8969
9006
|
|
|
8970
9007
|
// src/cli/hook.ts
|
|
8971
|
-
import { readFileSync as readFileSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync5, existsSync as
|
|
9008
|
+
import { readFileSync as readFileSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "node:fs";
|
|
8972
9009
|
import { createHash as createHash5 } from "node:crypto";
|
|
8973
|
-
function setSnapshotCache(
|
|
8974
|
-
snapshotCache =
|
|
9010
|
+
function setSnapshotCache(cache3) {
|
|
9011
|
+
snapshotCache = cache3;
|
|
8975
9012
|
}
|
|
8976
9013
|
function readStdin() {
|
|
8977
9014
|
try {
|
|
@@ -9101,8 +9138,8 @@ function uncoveredDrift(cwd, policy, expected, current, covered, base, turnTree)
|
|
|
9101
9138
|
}
|
|
9102
9139
|
const want = expected[rel]?.hash;
|
|
9103
9140
|
let before = null;
|
|
9104
|
-
for (const
|
|
9105
|
-
const blob = fileAt(
|
|
9141
|
+
for (const rev2 of candidates) {
|
|
9142
|
+
const blob = fileAt(rev2, rel, { cwd });
|
|
9106
9143
|
if (blob != null && contentHash(blob) === want) {
|
|
9107
9144
|
before = blob;
|
|
9108
9145
|
break;
|
|
@@ -9213,12 +9250,13 @@ function sanctionPredictedWrites(cwd, sessionId, policy, changes) {
|
|
|
9213
9250
|
}
|
|
9214
9251
|
function preToolUseVerdict(input, defaultCwd) {
|
|
9215
9252
|
try {
|
|
9216
|
-
const
|
|
9253
|
+
const sessionCwd = input.cwd ?? defaultCwd ?? process.cwd();
|
|
9254
|
+
const cwd = repoRoot(sessionCwd);
|
|
9217
9255
|
turnBaseline(cwd, input.session_id);
|
|
9218
9256
|
const policy = loadPolicy(cwd);
|
|
9219
9257
|
const driftBlocks = effectDriftBlocks(cwd, input.session_id, policy);
|
|
9220
9258
|
if (driftBlocks) return verdict(driftBlocks, "PreToolUse");
|
|
9221
|
-
const changes = changesFromClaudeHook(input, cwd);
|
|
9259
|
+
const changes = changesFromClaudeHook(input, cwd, sessionCwd);
|
|
9222
9260
|
const blocks2 = evaluate(changes, policy, void 0, "tool-call", { cwd }).filter((f) => f.severity === "block");
|
|
9223
9261
|
if (blocks2.length === 0) sanctionPredictedWrites(cwd, input.session_id, policy, changes);
|
|
9224
9262
|
return verdict(blocks2, "PreToolUse");
|
|
@@ -9250,9 +9288,9 @@ function turnTransientBlocks(cwd, sessionId, policy, changes) {
|
|
|
9250
9288
|
recordObserverHealth(telemetry.state, telemetry.reason);
|
|
9251
9289
|
}
|
|
9252
9290
|
const cp = cursorPath(cwd, sessionId);
|
|
9253
|
-
if (!
|
|
9291
|
+
if (!existsSync9(log)) return none;
|
|
9254
9292
|
let offset = 0;
|
|
9255
|
-
if (cp &&
|
|
9293
|
+
if (cp && existsSync9(cp)) {
|
|
9256
9294
|
const n = Number(readFileSync10(cp, "utf8"));
|
|
9257
9295
|
offset = Number.isFinite(n) ? n : 0;
|
|
9258
9296
|
}
|
|
@@ -9321,8 +9359,9 @@ function recordObserverHealth(state, reason) {
|
|
|
9321
9359
|
}
|
|
9322
9360
|
function stopVerdict(input, defaultCwd) {
|
|
9323
9361
|
if (input.stop_hook_active) return { exitCode: 0, stdout: "" };
|
|
9324
|
-
const
|
|
9325
|
-
if (!isGitRepo(
|
|
9362
|
+
const sessionCwd = input.cwd ?? defaultCwd ?? process.cwd();
|
|
9363
|
+
if (!isGitRepo(sessionCwd)) return { exitCode: 0, stdout: "" };
|
|
9364
|
+
const cwd = repoRoot(sessionCwd);
|
|
9326
9365
|
let blocks2;
|
|
9327
9366
|
let commitCursor = () => {
|
|
9328
9367
|
};
|
|
@@ -9410,6 +9449,7 @@ var init_hook = __esm({
|
|
|
9410
9449
|
init_policy();
|
|
9411
9450
|
init_disk();
|
|
9412
9451
|
init_narrow();
|
|
9452
|
+
init_repo_context();
|
|
9413
9453
|
snapshotCache = null;
|
|
9414
9454
|
HookInputError = class extends Error {
|
|
9415
9455
|
};
|
|
@@ -9468,22 +9508,22 @@ var init_ptree_cache = __esm({
|
|
|
9468
9508
|
* time, the way the in-process path treats them.
|
|
9469
9509
|
*/
|
|
9470
9510
|
entry(cwd, rel, compute) {
|
|
9471
|
-
const
|
|
9511
|
+
const abs = join13(cwd, rel);
|
|
9472
9512
|
let st;
|
|
9473
9513
|
try {
|
|
9474
|
-
st = lstatSync5(
|
|
9514
|
+
st = lstatSync5(abs, { bigint: true });
|
|
9475
9515
|
} catch {
|
|
9476
|
-
this.map.delete(
|
|
9516
|
+
this.map.delete(abs);
|
|
9477
9517
|
this.stats.misses++;
|
|
9478
9518
|
return compute();
|
|
9479
9519
|
}
|
|
9480
9520
|
if (!st.isFile()) {
|
|
9481
|
-
this.map.delete(
|
|
9521
|
+
this.map.delete(abs);
|
|
9482
9522
|
this.stats.misses++;
|
|
9483
9523
|
return compute();
|
|
9484
9524
|
}
|
|
9485
9525
|
const key2 = keyOf(st);
|
|
9486
|
-
const hit = this.map.get(
|
|
9526
|
+
const hit = this.map.get(abs);
|
|
9487
9527
|
if (hit && hit.key === key2 && hit.recordedAtNs - hit.ctimeNs >= this.racyMarginNs) {
|
|
9488
9528
|
this.stats.hits++;
|
|
9489
9529
|
return hit.entry;
|
|
@@ -9493,17 +9533,17 @@ var init_ptree_cache = __esm({
|
|
|
9493
9533
|
const entry = compute();
|
|
9494
9534
|
let after;
|
|
9495
9535
|
try {
|
|
9496
|
-
after = lstatSync5(
|
|
9536
|
+
after = lstatSync5(abs, { bigint: true });
|
|
9497
9537
|
} catch {
|
|
9498
|
-
this.map.delete(
|
|
9538
|
+
this.map.delete(abs);
|
|
9499
9539
|
return entry;
|
|
9500
9540
|
}
|
|
9501
9541
|
if (!after.isFile() || keyOf(after) !== key2) {
|
|
9502
|
-
this.map.delete(
|
|
9542
|
+
this.map.delete(abs);
|
|
9503
9543
|
return entry;
|
|
9504
9544
|
}
|
|
9505
9545
|
if (this.map.size >= this.maxEntries) this.clear();
|
|
9506
|
-
this.map.set(
|
|
9546
|
+
this.map.set(abs, { key: key2, ctimeNs: st.ctimeNs, recordedAtNs, entry });
|
|
9507
9547
|
return entry;
|
|
9508
9548
|
}
|
|
9509
9549
|
};
|
|
@@ -9511,7 +9551,7 @@ var init_ptree_cache = __esm({
|
|
|
9511
9551
|
});
|
|
9512
9552
|
|
|
9513
9553
|
// src/cli/hook-service.ts
|
|
9514
|
-
import { chmodSync, existsSync as
|
|
9554
|
+
import { chmodSync, existsSync as existsSync10, lstatSync as lstatSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
9515
9555
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
9516
9556
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
9517
9557
|
import { sep } from "node:path";
|
|
@@ -9536,12 +9576,12 @@ function pidAlive2(pid) {
|
|
|
9536
9576
|
}
|
|
9537
9577
|
}
|
|
9538
9578
|
function listening(socket) {
|
|
9539
|
-
return new Promise((
|
|
9579
|
+
return new Promise((resolve16) => {
|
|
9540
9580
|
const sock = createConnection2(socket);
|
|
9541
9581
|
const done = (v) => {
|
|
9542
9582
|
clearTimeout(timer);
|
|
9543
9583
|
sock.destroy();
|
|
9544
|
-
|
|
9584
|
+
resolve16(v);
|
|
9545
9585
|
};
|
|
9546
9586
|
const timer = setTimeout(() => done(false), 1e3);
|
|
9547
9587
|
sock.once("connect", () => done(true));
|
|
@@ -9599,7 +9639,7 @@ function removeQuietly(p) {
|
|
|
9599
9639
|
}
|
|
9600
9640
|
}
|
|
9601
9641
|
function ensurePrivateDir(dir, uid) {
|
|
9602
|
-
if (!
|
|
9642
|
+
if (!existsSync10(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
9603
9643
|
const st = lstatSync6(dir);
|
|
9604
9644
|
if (!st.isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
9605
9645
|
if (st.uid !== uid) throw new Error(`${dir} is owned by uid ${st.uid}, not ${uid}; refusing to serve from it`);
|
|
@@ -9616,7 +9656,7 @@ async function startHookService(opts) {
|
|
|
9616
9656
|
if (uid === void 0) throw new Error("no uid on this platform; refusing to start");
|
|
9617
9657
|
let root;
|
|
9618
9658
|
try {
|
|
9619
|
-
root = realpathSync3(opts.root);
|
|
9659
|
+
root = realpathSync3(repoRoot(realpathSync3(opts.root)));
|
|
9620
9660
|
} catch {
|
|
9621
9661
|
throw new Error(`cannot resolve ${opts.root}`);
|
|
9622
9662
|
}
|
|
@@ -9627,7 +9667,7 @@ async function startHookService(opts) {
|
|
|
9627
9667
|
}
|
|
9628
9668
|
removeQuietly(paths.socket);
|
|
9629
9669
|
removeQuietly(paths.state);
|
|
9630
|
-
const
|
|
9670
|
+
const cache3 = opts.cache ?? new SnapshotCache();
|
|
9631
9671
|
let served = 0;
|
|
9632
9672
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9633
9673
|
const respond = (sock, body) => {
|
|
@@ -9653,7 +9693,7 @@ async function startHookService(opts) {
|
|
|
9653
9693
|
root,
|
|
9654
9694
|
started_at: startedAt,
|
|
9655
9695
|
served,
|
|
9656
|
-
cache: { hits:
|
|
9696
|
+
cache: { hits: cache3.stats.hits, misses: cache3.stats.misses, size: cache3.size }
|
|
9657
9697
|
});
|
|
9658
9698
|
}
|
|
9659
9699
|
if (!within(root, req2.cwd)) return respond(sock, { refused: "cwd outside the bound repository" });
|
|
@@ -9694,11 +9734,11 @@ async function startHookService(opts) {
|
|
|
9694
9734
|
});
|
|
9695
9735
|
const umask = process.umask(63);
|
|
9696
9736
|
try {
|
|
9697
|
-
await new Promise((
|
|
9737
|
+
await new Promise((resolve16, reject) => {
|
|
9698
9738
|
server.once("error", reject);
|
|
9699
9739
|
server.listen(paths.socket, () => {
|
|
9700
9740
|
server.off("error", reject);
|
|
9701
|
-
|
|
9741
|
+
resolve16();
|
|
9702
9742
|
});
|
|
9703
9743
|
});
|
|
9704
9744
|
} finally {
|
|
@@ -9707,7 +9747,7 @@ async function startHookService(opts) {
|
|
|
9707
9747
|
chmodSync(paths.socket, 384);
|
|
9708
9748
|
const state = { pid: process.pid, version: TW_VERSION, root, started_at: startedAt };
|
|
9709
9749
|
writeFileSync6(paths.state, JSON.stringify(state) + "\n", { mode: 384 });
|
|
9710
|
-
setSnapshotCache(
|
|
9750
|
+
setSnapshotCache(cache3);
|
|
9711
9751
|
let closed = false;
|
|
9712
9752
|
return {
|
|
9713
9753
|
paths,
|
|
@@ -9715,14 +9755,14 @@ async function startHookService(opts) {
|
|
|
9715
9755
|
get served() {
|
|
9716
9756
|
return served;
|
|
9717
9757
|
},
|
|
9718
|
-
close: () => new Promise((
|
|
9719
|
-
if (closed) return
|
|
9758
|
+
close: () => new Promise((resolve16) => {
|
|
9759
|
+
if (closed) return resolve16();
|
|
9720
9760
|
closed = true;
|
|
9721
9761
|
setSnapshotCache(null);
|
|
9722
9762
|
server.close(() => {
|
|
9723
9763
|
removeQuietly(paths.socket);
|
|
9724
9764
|
removeQuietly(paths.state);
|
|
9725
|
-
|
|
9765
|
+
resolve16();
|
|
9726
9766
|
});
|
|
9727
9767
|
})
|
|
9728
9768
|
};
|
|
@@ -9751,7 +9791,7 @@ async function stopHookService(paths) {
|
|
|
9751
9791
|
process.kill(state.pid, "SIGTERM");
|
|
9752
9792
|
} catch {
|
|
9753
9793
|
}
|
|
9754
|
-
const gone = () => !pidAlive2(state.pid) || !
|
|
9794
|
+
const gone = () => !pidAlive2(state.pid) || !existsSync10(paths.socket);
|
|
9755
9795
|
const until = Date.now() + STOP_WAIT_MS;
|
|
9756
9796
|
while (!gone() && Date.now() < until) sleepSync(50);
|
|
9757
9797
|
if (!gone()) throw new Error(`hook service pid ${state.pid} did not exit within ${STOP_WAIT_MS / 1e3}s`);
|
|
@@ -9819,9 +9859,9 @@ function runHookService(args) {
|
|
|
9819
9859
|
`);
|
|
9820
9860
|
process.exit(0);
|
|
9821
9861
|
}
|
|
9822
|
-
const
|
|
9862
|
+
const cache3 = isRecord(res.cache) ? res.cache : {};
|
|
9823
9863
|
process.stdout.write(
|
|
9824
|
-
`tamperward hook-service: running (pid ${state.pid}, tamperward@${state.version}, root ${state.root}, since ${state.started_at}); served: ${res.served}; snapshot cache hits/misses/entries: ${String(
|
|
9864
|
+
`tamperward hook-service: running (pid ${state.pid}, tamperward@${state.version}, root ${state.root}, since ${state.started_at}); served: ${res.served}; snapshot cache hits/misses/entries: ${String(cache3.hits ?? 0)}/${String(cache3.misses ?? 0)}/${String(cache3.size ?? 0)}; ${optIn}
|
|
9825
9865
|
`
|
|
9826
9866
|
);
|
|
9827
9867
|
process.exit(0);
|
|
@@ -9835,6 +9875,7 @@ var init_hook_service = __esm({
|
|
|
9835
9875
|
init_narrow();
|
|
9836
9876
|
init_wiring();
|
|
9837
9877
|
init_ptree_cache();
|
|
9878
|
+
init_repo_context();
|
|
9838
9879
|
init_hook();
|
|
9839
9880
|
init_hook_client();
|
|
9840
9881
|
MAX_REQUEST_BYTES = 64 * 1024 * 1024;
|
|
@@ -9852,7 +9893,7 @@ function runAllow(opts) {
|
|
|
9852
9893
|
process.stderr.write("tamperward: a --reason is required \u2014 sign-offs must be justified.\n");
|
|
9853
9894
|
return 2;
|
|
9854
9895
|
}
|
|
9855
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
9896
|
+
const cwd = repoRoot(opts.cwd ?? process.cwd());
|
|
9856
9897
|
const policy = loadPolicy(cwd);
|
|
9857
9898
|
let findings;
|
|
9858
9899
|
try {
|
|
@@ -9894,6 +9935,7 @@ var init_allow = __esm({
|
|
|
9894
9935
|
init_engine();
|
|
9895
9936
|
init_policy_load();
|
|
9896
9937
|
init_signoff();
|
|
9938
|
+
init_repo_context();
|
|
9897
9939
|
}
|
|
9898
9940
|
});
|
|
9899
9941
|
|
|
@@ -9934,8 +9976,8 @@ var init_verifier_limits = __esm({
|
|
|
9934
9976
|
// src/cli/init.ts
|
|
9935
9977
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
9936
9978
|
import { createHash as createHash6 } from "node:crypto";
|
|
9937
|
-
import { existsSync as
|
|
9938
|
-
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute8, join as join14, relative as relative3, resolve as
|
|
9979
|
+
import { existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, writeFileSync as writeFileSync7, chmodSync as chmodSync2, statSync as statSync2 } from "node:fs";
|
|
9980
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute8, join as join14, relative as relative3, resolve as resolve4 } from "node:path";
|
|
9939
9981
|
function gitConfig(cwd, key2) {
|
|
9940
9982
|
try {
|
|
9941
9983
|
return execFileSync6("git", ["config", "--get", key2], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
@@ -10038,7 +10080,7 @@ function verifierCandidates(cwd) {
|
|
|
10038
10080
|
if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
|
|
10039
10081
|
} catch {
|
|
10040
10082
|
}
|
|
10041
|
-
if (
|
|
10083
|
+
if (existsSync11(join14(cwd, "pytest.ini"))) add("pytest");
|
|
10042
10084
|
try {
|
|
10043
10085
|
const setupCfg = readFileSync12(join14(cwd, "setup.cfg"), "utf8");
|
|
10044
10086
|
if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
|
|
@@ -10096,7 +10138,7 @@ function verifierSetupMessage(cwd) {
|
|
|
10096
10138
|
}
|
|
10097
10139
|
function planPolicy(cwd) {
|
|
10098
10140
|
const path = join14(cwd, POLICY_FILE);
|
|
10099
|
-
if (
|
|
10141
|
+
if (existsSync11(path)) {
|
|
10100
10142
|
try {
|
|
10101
10143
|
loadPolicy(cwd);
|
|
10102
10144
|
} catch (e) {
|
|
@@ -10115,9 +10157,9 @@ function planPolicy(cwd) {
|
|
|
10115
10157
|
function planGitignore(cwd) {
|
|
10116
10158
|
const rel = ".gitignore";
|
|
10117
10159
|
const path = join14(cwd, rel);
|
|
10118
|
-
if (!
|
|
10160
|
+
if (!existsSync11(join14(cwd, "node_modules"))) return null;
|
|
10119
10161
|
let existing = "";
|
|
10120
|
-
if (
|
|
10162
|
+
if (existsSync11(path)) {
|
|
10121
10163
|
let st;
|
|
10122
10164
|
try {
|
|
10123
10165
|
st = lstatSync7(path);
|
|
@@ -10166,7 +10208,7 @@ function planGitignore(cwd) {
|
|
|
10166
10208
|
return {
|
|
10167
10209
|
item: "gitignore",
|
|
10168
10210
|
path: rel,
|
|
10169
|
-
status:
|
|
10211
|
+
status: existsSync11(path) ? "update" : "create",
|
|
10170
10212
|
detail: "exclude installed Node dependencies from repository diffs",
|
|
10171
10213
|
apply: () => writeFileSync7(path, next)
|
|
10172
10214
|
};
|
|
@@ -10175,7 +10217,7 @@ function planClaudeHooks(cwd) {
|
|
|
10175
10217
|
const rel = ".claude/settings.json";
|
|
10176
10218
|
const path = join14(cwd, rel);
|
|
10177
10219
|
let settings = {};
|
|
10178
|
-
if (
|
|
10220
|
+
if (existsSync11(path)) {
|
|
10179
10221
|
let parsed;
|
|
10180
10222
|
try {
|
|
10181
10223
|
parsed = JSON.parse(readFileSync12(path, "utf8"));
|
|
@@ -10213,7 +10255,7 @@ function planClaudeHooks(cwd) {
|
|
|
10213
10255
|
return {
|
|
10214
10256
|
item: "agent",
|
|
10215
10257
|
path: rel,
|
|
10216
|
-
status:
|
|
10258
|
+
status: existsSync11(path) ? "update" : "create",
|
|
10217
10259
|
detail: [
|
|
10218
10260
|
needPre && "wire PreToolUse deny",
|
|
10219
10261
|
needStop && "wire Stop sweep",
|
|
@@ -10247,10 +10289,10 @@ function planClaudeHooks(cwd) {
|
|
|
10247
10289
|
function hooksDir(cwd) {
|
|
10248
10290
|
try {
|
|
10249
10291
|
const out3 = execFileSync6("git", ["rev-parse", "--git-path", "hooks"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
10250
|
-
if (out3) return
|
|
10292
|
+
if (out3) return resolve4(cwd, out3);
|
|
10251
10293
|
} catch {
|
|
10252
10294
|
}
|
|
10253
|
-
return
|
|
10295
|
+
return existsSync11(join14(cwd, ".git")) ? join14(cwd, ".git", "hooks") : null;
|
|
10254
10296
|
}
|
|
10255
10297
|
function display(cwd, path) {
|
|
10256
10298
|
const rel = relative3(cwd, path);
|
|
@@ -10277,11 +10319,11 @@ function planPreCommit(cwd) {
|
|
|
10277
10319
|
const skip = (detail) => ({ item: "pre-commit", path: "(none)", status: "skip", detail });
|
|
10278
10320
|
let path;
|
|
10279
10321
|
let note;
|
|
10280
|
-
if (
|
|
10322
|
+
if (existsSync11(join14(cwd, ".husky"))) {
|
|
10281
10323
|
path = join14(cwd, ".husky", "pre-commit");
|
|
10282
10324
|
note = "husky";
|
|
10283
10325
|
} else {
|
|
10284
|
-
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) =>
|
|
10326
|
+
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) => existsSync11(join14(cwd, c))));
|
|
10285
10327
|
if (managed) {
|
|
10286
10328
|
return skip(`${managed.name} manages this repository's hooks and regenerates them on install, so a line in the hooks directory would not last \u2014 ${managed.hint}`);
|
|
10287
10329
|
}
|
|
@@ -10297,10 +10339,10 @@ function planPreCommit(cwd) {
|
|
|
10297
10339
|
}
|
|
10298
10340
|
}
|
|
10299
10341
|
const rel = display(cwd, path);
|
|
10300
|
-
if (
|
|
10342
|
+
if (existsSync11(path) && !statSync2(path).isFile()) {
|
|
10301
10343
|
return { item: "pre-commit", path: rel, status: "error", detail: "exists but is not a regular file \u2014 refusing to write" };
|
|
10302
10344
|
}
|
|
10303
|
-
const existing =
|
|
10345
|
+
const existing = existsSync11(path) ? readFileSync12(path, "utf8") : null;
|
|
10304
10346
|
if (existing === null) {
|
|
10305
10347
|
return {
|
|
10306
10348
|
item: "pre-commit",
|
|
@@ -10389,7 +10431,7 @@ ${block.join("\n")}
|
|
|
10389
10431
|
function planCodeowners(cwd) {
|
|
10390
10432
|
const rel = ".github/CODEOWNERS";
|
|
10391
10433
|
const existingRel = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"].find(
|
|
10392
|
-
(r) =>
|
|
10434
|
+
(r) => existsSync11(join14(cwd, r))
|
|
10393
10435
|
);
|
|
10394
10436
|
const path = join14(cwd, existingRel ?? rel);
|
|
10395
10437
|
const existing = existingRel ? readFileSync12(path, "utf8") : null;
|
|
@@ -10430,7 +10472,7 @@ function planWorkflow(cwd, force) {
|
|
|
10430
10472
|
mkdirSync6(dirname4(path), { recursive: true });
|
|
10431
10473
|
writeFileSync7(path, workflowFile(WORKFLOW_CONTENT));
|
|
10432
10474
|
};
|
|
10433
|
-
if (!
|
|
10475
|
+
if (!existsSync11(path)) {
|
|
10434
10476
|
return {
|
|
10435
10477
|
item: "ci",
|
|
10436
10478
|
path: rel,
|
|
@@ -10482,6 +10524,13 @@ function planned(item, path, plan) {
|
|
|
10482
10524
|
return { item, path, status: "error", detail: `cannot plan this item \u2014 ${errText3(e)}` };
|
|
10483
10525
|
}
|
|
10484
10526
|
}
|
|
10527
|
+
function sameDir(a, b) {
|
|
10528
|
+
try {
|
|
10529
|
+
return realpathSync4(a) === realpathSync4(b);
|
|
10530
|
+
} catch {
|
|
10531
|
+
return false;
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10485
10534
|
function planInit(cwd, opts = {}) {
|
|
10486
10535
|
requireShippedVersion();
|
|
10487
10536
|
const gitignore = planGitignore(cwd);
|
|
@@ -10495,9 +10544,14 @@ function planInit(cwd, opts = {}) {
|
|
|
10495
10544
|
];
|
|
10496
10545
|
}
|
|
10497
10546
|
function runInit(opts) {
|
|
10498
|
-
const
|
|
10499
|
-
const
|
|
10547
|
+
const requested = resolve4(opts.cwd ?? process.cwd());
|
|
10548
|
+
const cwd = repoRoot(requested);
|
|
10500
10549
|
const w = opts.quiet ? { write: (_text) => true } : process.stdout;
|
|
10550
|
+
if (cwd !== requested && !sameDir(cwd, requested)) {
|
|
10551
|
+
process.stderr.write(`tamperward init: ${requested} is inside the repository rooted at ${cwd}; wiring the root.
|
|
10552
|
+
`);
|
|
10553
|
+
}
|
|
10554
|
+
const plan = planInit(cwd, { forceWorkflow: opts.forceWorkflow });
|
|
10501
10555
|
let applied = 0;
|
|
10502
10556
|
for (const a of plan) {
|
|
10503
10557
|
if (!opts.dryRun && a.apply) {
|
|
@@ -10552,6 +10606,7 @@ var init_init = __esm({
|
|
|
10552
10606
|
init_verifier_limits();
|
|
10553
10607
|
init_wiring();
|
|
10554
10608
|
init_narrow();
|
|
10609
|
+
init_repo_context();
|
|
10555
10610
|
errText3 = (e) => (e instanceof Error ? e.message : String(e)).replace(/\s+/g, " ").trim();
|
|
10556
10611
|
isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
10557
10612
|
HOOK_RE = /\btamperward(?:@\S+)?\s+hook\s+claude\b/;
|
|
@@ -10720,12 +10775,12 @@ var init_fingerprint = __esm({
|
|
|
10720
10775
|
// src/dependency-env.ts
|
|
10721
10776
|
import { createHash as createHash8 } from "node:crypto";
|
|
10722
10777
|
import {
|
|
10723
|
-
existsSync as
|
|
10778
|
+
existsSync as existsSync12,
|
|
10724
10779
|
lstatSync as lstatSync8,
|
|
10725
10780
|
readdirSync as readdirSync3,
|
|
10726
10781
|
readFileSync as readFileSync13,
|
|
10727
10782
|
readlinkSync as readlinkSync2,
|
|
10728
|
-
realpathSync as
|
|
10783
|
+
realpathSync as realpathSync5,
|
|
10729
10784
|
statSync as statSync3
|
|
10730
10785
|
} from "node:fs";
|
|
10731
10786
|
import {
|
|
@@ -10735,14 +10790,14 @@ import {
|
|
|
10735
10790
|
isAbsolute as isAbsolute9,
|
|
10736
10791
|
join as join15,
|
|
10737
10792
|
relative as relative4,
|
|
10738
|
-
resolve as
|
|
10793
|
+
resolve as resolve5,
|
|
10739
10794
|
sep as sep2
|
|
10740
10795
|
} from "node:path";
|
|
10741
10796
|
function metricsFor(descriptor) {
|
|
10742
10797
|
return descriptor.metrics ??= { fullSnapshots: 0, reusedSnapshots: 0, totalMs: 0 };
|
|
10743
10798
|
}
|
|
10744
10799
|
function inside(root, candidate) {
|
|
10745
|
-
const rel = relative4(
|
|
10800
|
+
const rel = relative4(resolve5(root), resolve5(candidate));
|
|
10746
10801
|
return rel === "" || rel !== ".." && !rel.startsWith(".." + sep2) && !isAbsolute9(rel);
|
|
10747
10802
|
}
|
|
10748
10803
|
function modeKind(path) {
|
|
@@ -10757,7 +10812,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
10757
10812
|
let currentReal;
|
|
10758
10813
|
try {
|
|
10759
10814
|
rootStat = lstatSync8(root.path);
|
|
10760
|
-
currentReal =
|
|
10815
|
+
currentReal = realpathSync5(root.path);
|
|
10761
10816
|
} catch (e) {
|
|
10762
10817
|
throw new Unattestable(
|
|
10763
10818
|
root.kind + " root " + JSON.stringify(root.path) + " can no longer be resolved (" + (e instanceof Error ? e.message : String(e)) + ")"
|
|
@@ -10773,7 +10828,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
10773
10828
|
}
|
|
10774
10829
|
h.update("root\0" + root.kind + "\0" + root.path + "\0" + root.realPath + "\0" + String(rootStat.mode) + "\0");
|
|
10775
10830
|
if (rootStat.isSymbolicLink()) h.update(readlinkSync2(root.path) + "\0");
|
|
10776
|
-
const cwdReal =
|
|
10831
|
+
const cwdReal = realpathSync5(cwd);
|
|
10777
10832
|
const visited = /* @__PURE__ */ new Set();
|
|
10778
10833
|
const hashFile = (path, label) => {
|
|
10779
10834
|
let st;
|
|
@@ -10790,7 +10845,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
10790
10845
|
}
|
|
10791
10846
|
};
|
|
10792
10847
|
const walk2 = (dir, namespace) => {
|
|
10793
|
-
const realDir =
|
|
10848
|
+
const realDir = realpathSync5(dir);
|
|
10794
10849
|
if (visited.has(realDir)) return;
|
|
10795
10850
|
visited.add(realDir);
|
|
10796
10851
|
let entries;
|
|
@@ -10840,7 +10895,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
10840
10895
|
h.update(target + "\0");
|
|
10841
10896
|
let resolved;
|
|
10842
10897
|
try {
|
|
10843
|
-
resolved =
|
|
10898
|
+
resolved = realpathSync5(path);
|
|
10844
10899
|
} catch {
|
|
10845
10900
|
continue;
|
|
10846
10901
|
}
|
|
@@ -10874,7 +10929,7 @@ function snapshot(roots, probes, cwd) {
|
|
|
10874
10929
|
const h = createHash8("sha256");
|
|
10875
10930
|
for (const probe of probes) {
|
|
10876
10931
|
h.update(
|
|
10877
|
-
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (
|
|
10932
|
+
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (existsSync12(probe.path) ? "present" : "absent") + "\0"
|
|
10878
10933
|
);
|
|
10879
10934
|
}
|
|
10880
10935
|
for (const root of roots) fingerprintRoot(root, cwd, h);
|
|
@@ -10895,7 +10950,7 @@ function freezeRoot(kind, path) {
|
|
|
10895
10950
|
if (!statSync3(path).isDirectory()) {
|
|
10896
10951
|
throw new Error("not a directory");
|
|
10897
10952
|
}
|
|
10898
|
-
return { kind, path:
|
|
10953
|
+
return { kind, path: resolve5(path), realPath: realpathSync5(path) };
|
|
10899
10954
|
} catch (e) {
|
|
10900
10955
|
throw new Unattestable(
|
|
10901
10956
|
kind + " root " + JSON.stringify(path) + " is not resolvable (" + (e instanceof Error ? e.message : String(e)) + ")"
|
|
@@ -10931,17 +10986,17 @@ function firstExecutable(command) {
|
|
|
10931
10986
|
function resolveExecutable(token, cwd, env) {
|
|
10932
10987
|
if (!token) return null;
|
|
10933
10988
|
if (token.includes("/") || token.includes("\\")) {
|
|
10934
|
-
const path = isAbsolute9(token) ? token :
|
|
10989
|
+
const path = isAbsolute9(token) ? token : resolve5(cwd, token);
|
|
10935
10990
|
try {
|
|
10936
|
-
return
|
|
10991
|
+
return existsSync12(path) ? realpathSync5(path) : null;
|
|
10937
10992
|
} catch {
|
|
10938
10993
|
return null;
|
|
10939
10994
|
}
|
|
10940
10995
|
}
|
|
10941
10996
|
for (const entry of (env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
10942
|
-
const candidate =
|
|
10997
|
+
const candidate = resolve5(entry, token);
|
|
10943
10998
|
try {
|
|
10944
|
-
if (
|
|
10999
|
+
if (existsSync12(candidate) && statSync3(candidate).isFile()) return realpathSync5(candidate);
|
|
10945
11000
|
} catch {
|
|
10946
11001
|
}
|
|
10947
11002
|
}
|
|
@@ -10958,13 +11013,13 @@ function looksPython(command, executable, token) {
|
|
|
10958
11013
|
}
|
|
10959
11014
|
function inferredVenvFromToken(token, cwd) {
|
|
10960
11015
|
if (!token || !token.includes("/") && !token.includes("\\")) return null;
|
|
10961
|
-
const path = isAbsolute9(token) ? token :
|
|
11016
|
+
const path = isAbsolute9(token) ? token : resolve5(cwd, token);
|
|
10962
11017
|
if (!pythonName(basename3(path))) return null;
|
|
10963
11018
|
const parent = dirname5(path);
|
|
10964
11019
|
const parentName = basename3(parent).toLowerCase();
|
|
10965
11020
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
10966
11021
|
const root = dirname5(parent);
|
|
10967
|
-
return
|
|
11022
|
+
return existsSync12(join15(root, "pyvenv.cfg")) ? root : null;
|
|
10968
11023
|
}
|
|
10969
11024
|
function unsupportedEcosystem(command, token) {
|
|
10970
11025
|
const name = token ? basename3(token).toLowerCase() : "";
|
|
@@ -10980,11 +11035,11 @@ function inferredVenv(executable) {
|
|
|
10980
11035
|
const parentName = basename3(parent).toLowerCase();
|
|
10981
11036
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
10982
11037
|
const root = dirname5(parent);
|
|
10983
|
-
return
|
|
11038
|
+
return existsSync12(join15(root, "pyvenv.cfg")) ? root : null;
|
|
10984
11039
|
}
|
|
10985
11040
|
function venvProblem(root) {
|
|
10986
11041
|
const cfg = join15(root, "pyvenv.cfg");
|
|
10987
|
-
if (!
|
|
11042
|
+
if (!existsSync12(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
|
|
10988
11043
|
try {
|
|
10989
11044
|
const body = readFileSync13(cfg, "utf8");
|
|
10990
11045
|
if (/^\s*include-system-site-packages\s*=\s*true\s*$/im.test(body)) {
|
|
@@ -10996,7 +11051,7 @@ function venvProblem(root) {
|
|
|
10996
11051
|
return null;
|
|
10997
11052
|
}
|
|
10998
11053
|
function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
10999
|
-
const cwd =
|
|
11054
|
+
const cwd = resolve5(cwdInput);
|
|
11000
11055
|
const roots = [];
|
|
11001
11056
|
const probes = [];
|
|
11002
11057
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11011,7 +11066,7 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
11011
11066
|
};
|
|
11012
11067
|
try {
|
|
11013
11068
|
const nodeModules = join15(cwd, "node_modules");
|
|
11014
|
-
if (
|
|
11069
|
+
if (existsSync12(nodeModules)) addRoot("node_modules", nodeModules);
|
|
11015
11070
|
else probes.push({ kind: "node_modules", path: nodeModules });
|
|
11016
11071
|
const token = firstExecutable(command);
|
|
11017
11072
|
const executable = resolveExecutable(token, cwd, env);
|
|
@@ -11020,10 +11075,10 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
11020
11075
|
if (!isAbsolute9(env.VIRTUAL_ENV)) {
|
|
11021
11076
|
throw new Unattestable("VIRTUAL_ENV is relative (" + JSON.stringify(env.VIRTUAL_ENV) + ")");
|
|
11022
11077
|
}
|
|
11023
|
-
venv =
|
|
11078
|
+
venv = resolve5(env.VIRTUAL_ENV);
|
|
11024
11079
|
} else {
|
|
11025
11080
|
venv = inferredVenv(executable) ?? inferredVenvFromToken(token, cwd);
|
|
11026
|
-
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) &&
|
|
11081
|
+
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) && existsSync12(join15(cwd, ".venv", "pyvenv.cfg"))) {
|
|
11027
11082
|
venv = join15(cwd, ".venv");
|
|
11028
11083
|
}
|
|
11029
11084
|
}
|
|
@@ -11060,7 +11115,7 @@ function checkDependencyEnvironment(cwdInput, descriptor) {
|
|
|
11060
11115
|
const fingerprint2 = measuredSnapshot(
|
|
11061
11116
|
descriptor.roots,
|
|
11062
11117
|
descriptor.probes,
|
|
11063
|
-
|
|
11118
|
+
resolve5(cwdInput),
|
|
11064
11119
|
metricsFor(descriptor)
|
|
11065
11120
|
);
|
|
11066
11121
|
if (fingerprint2 !== descriptor.fingerprint) {
|
|
@@ -11073,7 +11128,7 @@ function checkDependencyEnvironment(cwdInput, descriptor) {
|
|
|
11073
11128
|
}
|
|
11074
11129
|
function attestDependencyEnvironment(cwdInput, descriptor) {
|
|
11075
11130
|
const attestation = {
|
|
11076
|
-
cwd:
|
|
11131
|
+
cwd: resolve5(cwdInput),
|
|
11077
11132
|
descriptor,
|
|
11078
11133
|
check: checkDependencyEnvironment(cwdInput, descriptor)
|
|
11079
11134
|
};
|
|
@@ -11081,7 +11136,7 @@ function attestDependencyEnvironment(cwdInput, descriptor) {
|
|
|
11081
11136
|
return attestation;
|
|
11082
11137
|
}
|
|
11083
11138
|
function reuseDependencyEnvironmentAttestation(cwdInput, descriptor, attestation) {
|
|
11084
|
-
if (!attestation || !issuedAttestations.has(attestation) || attestation.descriptor !== descriptor || attestation.cwd !==
|
|
11139
|
+
if (!attestation || !issuedAttestations.has(attestation) || attestation.descriptor !== descriptor || attestation.cwd !== resolve5(cwdInput)) return null;
|
|
11085
11140
|
metricsFor(descriptor).reusedSnapshots += 1;
|
|
11086
11141
|
return attestation.check;
|
|
11087
11142
|
}
|
|
@@ -11447,8 +11502,8 @@ if (child) {
|
|
|
11447
11502
|
// src/verifier-backend.ts
|
|
11448
11503
|
import { createHash as createHash9, randomUUID } from "node:crypto";
|
|
11449
11504
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
11450
|
-
import { accessSync, constants as constants2, readFileSync as readFileSync15, realpathSync as
|
|
11451
|
-
import { delimiter as delimiter2, dirname as dirname6, join as join17, resolve as
|
|
11505
|
+
import { accessSync, constants as constants2, readFileSync as readFileSync15, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
|
|
11506
|
+
import { delimiter as delimiter2, dirname as dirname6, join as join17, resolve as resolve6 } from "node:path";
|
|
11452
11507
|
function identity() {
|
|
11453
11508
|
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
11454
11509
|
const groups = typeof process.getgroups === "function" ? process.getgroups() : [];
|
|
@@ -11468,7 +11523,7 @@ function writableByCaller(path) {
|
|
|
11468
11523
|
}
|
|
11469
11524
|
}
|
|
11470
11525
|
function replaceableByCaller(path) {
|
|
11471
|
-
let current =
|
|
11526
|
+
let current = resolve6(path);
|
|
11472
11527
|
if (writableByCaller(current)) return true;
|
|
11473
11528
|
current = dirname6(current);
|
|
11474
11529
|
while (true) {
|
|
@@ -11485,7 +11540,7 @@ function findExecutable(name) {
|
|
|
11485
11540
|
const candidate = join17(raw, name);
|
|
11486
11541
|
try {
|
|
11487
11542
|
accessSync(candidate, constants2.X_OK);
|
|
11488
|
-
return
|
|
11543
|
+
return realpathSync6(candidate);
|
|
11489
11544
|
} catch {
|
|
11490
11545
|
}
|
|
11491
11546
|
}
|
|
@@ -11560,7 +11615,7 @@ function inspectImageMetadata(enginePath, host, image) {
|
|
|
11560
11615
|
function engineStable(backend) {
|
|
11561
11616
|
if (!backend.enginePath || !backend.engineSha256) return false;
|
|
11562
11617
|
try {
|
|
11563
|
-
if (
|
|
11618
|
+
if (realpathSync6(backend.enginePath) !== backend.enginePath) return false;
|
|
11564
11619
|
if (replaceableByCaller(backend.enginePath)) return false;
|
|
11565
11620
|
return fileSha256(backend.enginePath) === backend.engineSha256;
|
|
11566
11621
|
} catch {
|
|
@@ -11814,7 +11869,7 @@ function runContainerStage(backend, dir, command, budgetSecs) {
|
|
|
11814
11869
|
const args = containerRunArgs({
|
|
11815
11870
|
image: backend.image,
|
|
11816
11871
|
name,
|
|
11817
|
-
workspace:
|
|
11872
|
+
workspace: resolve6(dir),
|
|
11818
11873
|
command,
|
|
11819
11874
|
uid,
|
|
11820
11875
|
gid,
|
|
@@ -11926,9 +11981,9 @@ var init_verifier_backend = __esm({
|
|
|
11926
11981
|
// src/cli/verify.ts
|
|
11927
11982
|
import { execFileSync as execFileSync8, spawnSync as spawnSync3 } from "node:child_process";
|
|
11928
11983
|
import { createHash as createHash10 } from "node:crypto";
|
|
11929
|
-
import { chmodSync as chmodSync3, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9 } from "node:fs";
|
|
11984
|
+
import { chmodSync as chmodSync3, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync13 } from "node:fs";
|
|
11930
11985
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
11931
|
-
import { dirname as dirname7, isAbsolute as isAbsolute10, join as join18, relative as relative5, resolve as
|
|
11986
|
+
import { dirname as dirname7, isAbsolute as isAbsolute10, join as join18, relative as relative5, resolve as resolve7, sep as sep3 } from "node:path";
|
|
11932
11987
|
function oracleAssuranceReport() {
|
|
11933
11988
|
return {
|
|
11934
11989
|
level: "suite-exit-only",
|
|
@@ -11942,11 +11997,11 @@ function git2(args, cwd) {
|
|
|
11942
11997
|
}
|
|
11943
11998
|
function resolveBase(base, cwd) {
|
|
11944
11999
|
assertRev(base);
|
|
11945
|
-
const
|
|
12000
|
+
const rev2 = git2(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
|
|
11946
12001
|
try {
|
|
11947
|
-
return git2(["merge-base",
|
|
12002
|
+
return git2(["merge-base", rev2, "HEAD"], cwd).trim();
|
|
11948
12003
|
} catch {
|
|
11949
|
-
return
|
|
12004
|
+
return rev2;
|
|
11950
12005
|
}
|
|
11951
12006
|
}
|
|
11952
12007
|
function baseIsAncestorOfHead(base, cwd) {
|
|
@@ -11954,7 +12009,7 @@ function baseIsAncestorOfHead(base, cwd) {
|
|
|
11954
12009
|
return r.status === 0;
|
|
11955
12010
|
}
|
|
11956
12011
|
function inside2(root, path) {
|
|
11957
|
-
const rel = relative5(
|
|
12012
|
+
const rel = relative5(resolve7(root), resolve7(path));
|
|
11958
12013
|
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute10(rel);
|
|
11959
12014
|
}
|
|
11960
12015
|
function linkParts(path) {
|
|
@@ -11971,7 +12026,7 @@ function rootedLinkTarget(target) {
|
|
|
11971
12026
|
return isAbsolute10(target) || driveRelativeLinkTarget(target);
|
|
11972
12027
|
}
|
|
11973
12028
|
function safeSymlink(target, out3, root, label) {
|
|
11974
|
-
if (rootedLinkTarget(target) || !inside2(root,
|
|
12029
|
+
if (rootedLinkTarget(target) || !inside2(root, resolve7(dirname7(out3), target))) {
|
|
11975
12030
|
throw linkEscape(label, target);
|
|
11976
12031
|
}
|
|
11977
12032
|
rmSync5(out3, { force: true });
|
|
@@ -12022,7 +12077,7 @@ function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
|
|
|
12022
12077
|
throw linkEscape(label, target);
|
|
12023
12078
|
}
|
|
12024
12079
|
stack = [];
|
|
12025
|
-
pending.unshift(...linkParts(relative5(dependencyRoot,
|
|
12080
|
+
pending.unshift(...linkParts(relative5(dependencyRoot, resolve7(next))));
|
|
12026
12081
|
} else {
|
|
12027
12082
|
pending.unshift(...linkParts(next));
|
|
12028
12083
|
}
|
|
@@ -12045,7 +12100,7 @@ function rejectLinkedParent(cwd, rel) {
|
|
|
12045
12100
|
}
|
|
12046
12101
|
}
|
|
12047
12102
|
function materialize(cwd, dest, dependencyRoot) {
|
|
12048
|
-
const listed =
|
|
12103
|
+
const listed = workingTreePaths(cwd);
|
|
12049
12104
|
const links = [];
|
|
12050
12105
|
for (const rel of listed) {
|
|
12051
12106
|
const src = join18(cwd, rel);
|
|
@@ -12075,6 +12130,32 @@ function materialize(cwd, dest, dependencyRoot) {
|
|
|
12075
12130
|
}
|
|
12076
12131
|
if (dependencyRoot) symlinkSync(dependencyRoot, join18(dest, "node_modules"), "dir");
|
|
12077
12132
|
}
|
|
12133
|
+
function workingTreePaths(cwd) {
|
|
12134
|
+
return git2(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
|
|
12135
|
+
}
|
|
12136
|
+
function probeFilesystemCaseSensitivity(dir) {
|
|
12137
|
+
const upper = join18(dir, ".tw-case-probe-A");
|
|
12138
|
+
const lower = join18(dir, ".tw-case-probe-a");
|
|
12139
|
+
try {
|
|
12140
|
+
writeFileSync9(upper, "", { flag: "wx" });
|
|
12141
|
+
return !existsSync13(lower);
|
|
12142
|
+
} finally {
|
|
12143
|
+
rmSync5(upper, { force: true });
|
|
12144
|
+
}
|
|
12145
|
+
}
|
|
12146
|
+
function foldCase(p) {
|
|
12147
|
+
return p.toLowerCase();
|
|
12148
|
+
}
|
|
12149
|
+
function caseFoldCollision(paths) {
|
|
12150
|
+
const seen = /* @__PURE__ */ new Map();
|
|
12151
|
+
for (const p of paths) {
|
|
12152
|
+
const key2 = foldCase(p);
|
|
12153
|
+
const prior = seen.get(key2);
|
|
12154
|
+
if (prior !== void 0 && prior !== p) return [prior, p];
|
|
12155
|
+
if (prior === void 0) seen.set(key2, p);
|
|
12156
|
+
}
|
|
12157
|
+
return null;
|
|
12158
|
+
}
|
|
12078
12159
|
function dropSymlink(p) {
|
|
12079
12160
|
try {
|
|
12080
12161
|
if (lstatSync9(p).isSymbolicLink()) rmSync5(p, { force: true });
|
|
@@ -12129,21 +12210,24 @@ function overlayDigest(dest, paths) {
|
|
|
12129
12210
|
}
|
|
12130
12211
|
return h.digest("hex");
|
|
12131
12212
|
}
|
|
12132
|
-
function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot) {
|
|
12213
|
+
function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensitive) {
|
|
12133
12214
|
const entries = baseEntries(base, cwd);
|
|
12134
12215
|
const atBase = entries.map((e) => e.path);
|
|
12216
|
+
const fold = caseSensitive ? (p) => p : foldCase;
|
|
12135
12217
|
const verifierGlobs = policy.verify?.inputs ?? [];
|
|
12136
12218
|
const coveredBaseInputs = verifierCoveredInputs(cmd, atBase, policy);
|
|
12137
|
-
const
|
|
12219
|
+
const isOverlayExact = (p) => coveredBaseInputs.has(p) || // Candidate-added files do not exist in atBase and therefore cannot appear
|
|
12138
12220
|
// in the precomputed set. They still must be removed when they land on a
|
|
12139
12221
|
// protected class or the built-in verification surface.
|
|
12140
12222
|
OVERLAY_CLASSES.some((category) => isProtected(p, policy, category)) || matchesAny(p, VERIFICATION_SURFACE) || verifierGlobs.length > 0 && matchesAny(p, verifierGlobs);
|
|
12223
|
+
const coveredFolded = new Set(caseSensitive ? [] : [...coveredBaseInputs].map(foldCase));
|
|
12224
|
+
const isOverlay = caseSensitive ? isOverlayExact : (p) => isOverlayExact(p) || isOverlayExact(foldCase(p)) || coveredFolded.has(foldCase(p));
|
|
12141
12225
|
const restored = [];
|
|
12142
12226
|
const restoredLinks = [];
|
|
12143
12227
|
const baseProtected = /* @__PURE__ */ new Set();
|
|
12144
12228
|
for (const e of entries) {
|
|
12145
12229
|
if (!isOverlay(e.path)) continue;
|
|
12146
|
-
baseProtected.add(e.path);
|
|
12230
|
+
baseProtected.add(fold(e.path));
|
|
12147
12231
|
if (e.type === "commit") {
|
|
12148
12232
|
throw new Error(`the base's ${e.path} is a submodule; the pristine copy cannot reproduce it`);
|
|
12149
12233
|
}
|
|
@@ -12162,9 +12246,9 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot) {
|
|
|
12162
12246
|
restored.push(e.path);
|
|
12163
12247
|
}
|
|
12164
12248
|
let removed = 0;
|
|
12165
|
-
const inCopy =
|
|
12249
|
+
const inCopy = workingTreePaths(cwd);
|
|
12166
12250
|
for (const rel of inCopy) {
|
|
12167
|
-
if (!isOverlay(rel) || baseProtected.has(rel)) continue;
|
|
12251
|
+
if (!isOverlay(rel) || baseProtected.has(fold(rel))) continue;
|
|
12168
12252
|
rmSync5(join18(dest, rel), { force: true });
|
|
12169
12253
|
removed++;
|
|
12170
12254
|
}
|
|
@@ -12285,6 +12369,8 @@ function runVerify(opts) {
|
|
|
12285
12369
|
const cwd = opts.cwd ?? process.cwd();
|
|
12286
12370
|
const out3 = opts.silent ? (_s) => {
|
|
12287
12371
|
} : (s) => void process.stdout.write(s + "\n");
|
|
12372
|
+
let filesystemCaseSensitive;
|
|
12373
|
+
const caseReport = () => filesystemCaseSensitive === void 0 ? {} : { filesystem_case_sensitive: filesystemCaseSensitive };
|
|
12288
12374
|
const cannotVerify = (reason, detail, extra = {}) => {
|
|
12289
12375
|
opts.onVerdict?.({ verdict: "CANNOT_VERIFY", reason, ...detail ? { detail } : {} });
|
|
12290
12376
|
if (opts.json) {
|
|
@@ -12293,6 +12379,7 @@ function runVerify(opts) {
|
|
|
12293
12379
|
verdict: "CANNOT_VERIFY",
|
|
12294
12380
|
reason,
|
|
12295
12381
|
...detail ? { detail } : {},
|
|
12382
|
+
...caseReport(),
|
|
12296
12383
|
...extra
|
|
12297
12384
|
}));
|
|
12298
12385
|
} else if (detail) {
|
|
@@ -12404,6 +12491,26 @@ function runVerify(opts) {
|
|
|
12404
12491
|
if (opts.keep) return;
|
|
12405
12492
|
for (const d of dirs) rmSync5(d, { recursive: true, force: true });
|
|
12406
12493
|
};
|
|
12494
|
+
filesystemCaseSensitive = (opts.probeCaseSensitivity ?? probeFilesystemCaseSensitivity)(visRoot);
|
|
12495
|
+
if (!filesystemCaseSensitive) {
|
|
12496
|
+
const present = (rel) => {
|
|
12497
|
+
try {
|
|
12498
|
+
lstatSync9(join18(cwd, rel));
|
|
12499
|
+
return true;
|
|
12500
|
+
} catch {
|
|
12501
|
+
return false;
|
|
12502
|
+
}
|
|
12503
|
+
};
|
|
12504
|
+
const collision = caseFoldCollision(workingTreePaths(cwd).filter(present)) ?? caseFoldCollision(baseEntries(base, cwd).map((e) => e.path));
|
|
12505
|
+
if (collision) {
|
|
12506
|
+
cleanup([visRoot]);
|
|
12507
|
+
return cannotVerify(
|
|
12508
|
+
"PATH_CASE_COLLISION",
|
|
12509
|
+
`${collision[0]} and ${collision[1]} are distinct paths that the case-insensitive filesystem the copies are materialised on cannot tell apart; the copy could not be the tree under verification`,
|
|
12510
|
+
{ verifier_backend: backendReport(), dependency_environment: dependencyReport(), oracle_assurance: oracleAssuranceReport() }
|
|
12511
|
+
);
|
|
12512
|
+
}
|
|
12513
|
+
}
|
|
12407
12514
|
const protectedIgnored = (rel) => isProtected(rel, policy);
|
|
12408
12515
|
const treeBefore = treeFingerprint(cwd, protectedIgnored);
|
|
12409
12516
|
const dependencyAtEntry = dependencyEnvironment ? reuseDependencyEnvironmentAttestation(
|
|
@@ -12441,6 +12548,7 @@ function runVerify(opts) {
|
|
|
12441
12548
|
verdict: "CANNOT_VERIFY",
|
|
12442
12549
|
reason: exhausted ? "VERIFIER_RESOURCE_EXHAUSTED" : "VERIFIER_BACKEND_RUNTIME_FAILURE",
|
|
12443
12550
|
stage: "visible",
|
|
12551
|
+
...caseReport(),
|
|
12444
12552
|
...visible.resource ? { resource: visible.resource } : {},
|
|
12445
12553
|
detail: visible.reason,
|
|
12446
12554
|
diagnostics: diagnosticsJson(visible.diagnostics, true),
|
|
@@ -12486,7 +12594,8 @@ function runVerify(opts) {
|
|
|
12486
12594
|
priDir,
|
|
12487
12595
|
policy,
|
|
12488
12596
|
cmd,
|
|
12489
|
-
frozenNodeModules
|
|
12597
|
+
frozenNodeModules,
|
|
12598
|
+
filesystemCaseSensitive
|
|
12490
12599
|
));
|
|
12491
12600
|
} catch (e) {
|
|
12492
12601
|
cleanup([visRoot, priRoot]);
|
|
@@ -12508,6 +12617,7 @@ function runVerify(opts) {
|
|
|
12508
12617
|
verdict: "CANNOT_VERIFY",
|
|
12509
12618
|
reason: exhausted ? "VERIFIER_RESOURCE_EXHAUSTED" : "VERIFIER_BACKEND_RUNTIME_FAILURE",
|
|
12510
12619
|
stage: "pristine",
|
|
12620
|
+
...caseReport(),
|
|
12511
12621
|
...pristine.resource ? { resource: pristine.resource } : {},
|
|
12512
12622
|
detail: pristine.reason,
|
|
12513
12623
|
diagnostics: diagnosticsJson(pristine.diagnostics, true),
|
|
@@ -12572,6 +12682,7 @@ function runVerify(opts) {
|
|
|
12572
12682
|
pristine: stageJson(pristine),
|
|
12573
12683
|
protected_restored: restored.length,
|
|
12574
12684
|
added_protected_removed: removedAdded,
|
|
12685
|
+
filesystem_case_sensitive: filesystemCaseSensitive,
|
|
12575
12686
|
verifier_backend: backendReport(),
|
|
12576
12687
|
dependency_environment: dependencyReport(),
|
|
12577
12688
|
oracle_assurance: oracleAssuranceReport(),
|
|
@@ -12745,9 +12856,9 @@ var init_verify = __esm({
|
|
|
12745
12856
|
// src/cli/run.ts
|
|
12746
12857
|
import { execFileSync as execFileSync9, spawn, spawnSync as spawnSync4 } from "node:child_process";
|
|
12747
12858
|
import { randomBytes } from "node:crypto";
|
|
12748
|
-
import { accessSync as accessSync2, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as
|
|
12859
|
+
import { accessSync as accessSync2, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync10 } from "node:fs";
|
|
12749
12860
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
12750
|
-
import { dirname as dirname8, join as join19, resolve as
|
|
12861
|
+
import { dirname as dirname8, join as join19, resolve as resolve8 } from "node:path";
|
|
12751
12862
|
function authoritativeRunLifecyclePlatform(platform = process.platform) {
|
|
12752
12863
|
return platform === "linux";
|
|
12753
12864
|
}
|
|
@@ -12800,7 +12911,7 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
|
|
|
12800
12911
|
}
|
|
12801
12912
|
for (const candidate of candidates) {
|
|
12802
12913
|
try {
|
|
12803
|
-
const real =
|
|
12914
|
+
const real = realpathSync8(candidate);
|
|
12804
12915
|
const st = statSync5(real);
|
|
12805
12916
|
if (!st.isFile() || writableByCaller2(real)) continue;
|
|
12806
12917
|
const probe = spawnSync4(
|
|
@@ -12966,7 +13077,7 @@ function startSupervisedObserver(cwd, base, entry) {
|
|
|
12966
13077
|
try {
|
|
12967
13078
|
child = spawn(
|
|
12968
13079
|
process.execPath,
|
|
12969
|
-
[
|
|
13080
|
+
[resolve8(cliEntry), "watch", "--dir", cwd, "--log", log, "--base", base],
|
|
12970
13081
|
{
|
|
12971
13082
|
cwd,
|
|
12972
13083
|
stdio: ["ignore", "ignore", "inherit"],
|
|
@@ -13074,7 +13185,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
13074
13185
|
const out3 = [];
|
|
13075
13186
|
let real;
|
|
13076
13187
|
try {
|
|
13077
|
-
real =
|
|
13188
|
+
real = realpathSync8(cwd);
|
|
13078
13189
|
} catch {
|
|
13079
13190
|
return out3;
|
|
13080
13191
|
}
|
|
@@ -13103,7 +13214,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
13103
13214
|
for (const link of procLinks) {
|
|
13104
13215
|
try {
|
|
13105
13216
|
const raw = readlinkSync4(link).replace(/ \(deleted\)$/, "");
|
|
13106
|
-
const held = raw.startsWith("/") ?
|
|
13217
|
+
const held = raw.startsWith("/") ? resolve8(raw) : raw;
|
|
13107
13218
|
if (held === real || held.startsWith(real + "/")) {
|
|
13108
13219
|
holds = true;
|
|
13109
13220
|
break;
|
|
@@ -13116,7 +13227,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
13116
13227
|
return out3;
|
|
13117
13228
|
}
|
|
13118
13229
|
function runEnvelope(opts) {
|
|
13119
|
-
const cwd =
|
|
13230
|
+
const cwd = resolve8(opts.cwd ?? process.cwd());
|
|
13120
13231
|
const say = opts.json ? (_s) => {
|
|
13121
13232
|
} : out;
|
|
13122
13233
|
if (!opts.argv.length) {
|
|
@@ -13660,8 +13771,8 @@ raise SystemExit(0)
|
|
|
13660
13771
|
|
|
13661
13772
|
// src/cli/doctor.ts
|
|
13662
13773
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
13663
|
-
import { existsSync as
|
|
13664
|
-
import { join as join20, resolve as
|
|
13774
|
+
import { existsSync as existsSync14, readFileSync as readFileSync18, readdirSync as readdirSync5 } from "node:fs";
|
|
13775
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
13665
13776
|
function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
|
|
13666
13777
|
if (platform === "linux") {
|
|
13667
13778
|
if (linuxPython?.path) {
|
|
@@ -13716,7 +13827,7 @@ function wiringState(status, authority) {
|
|
|
13716
13827
|
return authority ? "BROKEN" : "WARN";
|
|
13717
13828
|
}
|
|
13718
13829
|
function pinsInFile(path) {
|
|
13719
|
-
if (!
|
|
13830
|
+
if (!existsSync14(path)) return [];
|
|
13720
13831
|
let src = "";
|
|
13721
13832
|
try {
|
|
13722
13833
|
src = readFileSync18(path, "utf8");
|
|
@@ -13739,8 +13850,8 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
|
|
|
13739
13850
|
const broken = [];
|
|
13740
13851
|
const warnings = [];
|
|
13741
13852
|
for (const rel of workflowRels) {
|
|
13742
|
-
const path =
|
|
13743
|
-
if (!
|
|
13853
|
+
const path = resolve9(cwd, rel);
|
|
13854
|
+
if (!existsSync14(path)) {
|
|
13744
13855
|
broken.push(`${rel} is missing`);
|
|
13745
13856
|
continue;
|
|
13746
13857
|
}
|
|
@@ -14086,7 +14197,7 @@ function verifyJobs(doc) {
|
|
|
14086
14197
|
return found;
|
|
14087
14198
|
}
|
|
14088
14199
|
function diagnose(opts = {}) {
|
|
14089
|
-
const cwd =
|
|
14200
|
+
const cwd = repoRoot(resolve9(opts.cwd ?? process.cwd()));
|
|
14090
14201
|
let policy;
|
|
14091
14202
|
try {
|
|
14092
14203
|
policy = policyFor(opts, cwd);
|
|
@@ -14110,8 +14221,8 @@ function diagnose(opts = {}) {
|
|
|
14110
14221
|
workflowRels.push(opts.workflow);
|
|
14111
14222
|
} else {
|
|
14112
14223
|
const workflowDirRel = ".github/workflows";
|
|
14113
|
-
const workflowDir =
|
|
14114
|
-
if (!
|
|
14224
|
+
const workflowDir = resolve9(cwd, workflowDirRel);
|
|
14225
|
+
if (!existsSync14(workflowDir)) {
|
|
14115
14226
|
return fail("ci-verifier", `${workflowDirRel}: workflow directory does not exist`);
|
|
14116
14227
|
}
|
|
14117
14228
|
let entries;
|
|
@@ -14130,8 +14241,8 @@ function diagnose(opts = {}) {
|
|
|
14130
14241
|
let verifyJobCount = 0;
|
|
14131
14242
|
const authorityWorkflows = [];
|
|
14132
14243
|
for (const workflowRel of workflowRels) {
|
|
14133
|
-
const workflowPath =
|
|
14134
|
-
if (!
|
|
14244
|
+
const workflowPath = resolve9(cwd, workflowRel);
|
|
14245
|
+
if (!existsSync14(workflowPath)) {
|
|
14135
14246
|
return fail("ci-verifier", `${workflowRel}: workflow does not exist`);
|
|
14136
14247
|
}
|
|
14137
14248
|
let doc;
|
|
@@ -14273,6 +14384,7 @@ var init_doctor = __esm({
|
|
|
14273
14384
|
init_run();
|
|
14274
14385
|
init_machine_output();
|
|
14275
14386
|
init_narrow();
|
|
14387
|
+
init_repo_context();
|
|
14276
14388
|
VERIFY_COMMAND = /\btamperward(?:@\S+)?\s+verify\b/;
|
|
14277
14389
|
GITHUB_API_SCRIPT = [
|
|
14278
14390
|
"const endpoint = process.argv[1];",
|
|
@@ -14291,16 +14403,16 @@ var init_doctor = __esm({
|
|
|
14291
14403
|
// src/cli/trace-verify.ts
|
|
14292
14404
|
import { execFileSync as execFileSync11, spawnSync as spawnSync5 } from "node:child_process";
|
|
14293
14405
|
import {
|
|
14294
|
-
existsSync as
|
|
14406
|
+
existsSync as existsSync15,
|
|
14295
14407
|
mkdtempSync as mkdtempSync5,
|
|
14296
14408
|
readFileSync as readFileSync19,
|
|
14297
14409
|
readdirSync as readdirSync6,
|
|
14298
|
-
realpathSync as
|
|
14410
|
+
realpathSync as realpathSync9,
|
|
14299
14411
|
rmSync as rmSync7,
|
|
14300
14412
|
symlinkSync as symlinkSync2
|
|
14301
14413
|
} from "node:fs";
|
|
14302
14414
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
14303
|
-
import { isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as
|
|
14415
|
+
import { isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as resolve10, sep as sep4 } from "node:path";
|
|
14304
14416
|
function unescapeStraceString(s) {
|
|
14305
14417
|
return s.replace(/\\([0-7]{1,3})/g, (_m, oct) => String.fromCharCode(parseInt(oct, 8))).replace(/\\x([0-9a-fA-F]{2})/g, (_m, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
14306
14418
|
}
|
|
@@ -14325,13 +14437,13 @@ function parseStraceFileAccess(raw) {
|
|
|
14325
14437
|
return out3;
|
|
14326
14438
|
}
|
|
14327
14439
|
function inside3(root, path) {
|
|
14328
|
-
const rel = relative6(
|
|
14440
|
+
const rel = relative6(resolve10(root), resolve10(path));
|
|
14329
14441
|
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep4}`) && !isAbsolute11(rel);
|
|
14330
14442
|
}
|
|
14331
14443
|
function normalRepoPath(root, raw) {
|
|
14332
|
-
const
|
|
14333
|
-
if (!inside3(root,
|
|
14334
|
-
const rel = relative6(root,
|
|
14444
|
+
const abs = isAbsolute11(raw) ? resolve10(raw) : resolve10(root, raw);
|
|
14445
|
+
if (!inside3(root, abs)) return null;
|
|
14446
|
+
const rel = relative6(root, abs).split(sep4).join("/");
|
|
14335
14447
|
return rel && rel !== ".." ? rel : null;
|
|
14336
14448
|
}
|
|
14337
14449
|
function likelyConfig(path) {
|
|
@@ -14349,7 +14461,7 @@ function summarizeTraceRuns(opts) {
|
|
|
14349
14461
|
for (const item of run) {
|
|
14350
14462
|
const rel = normalRepoPath(opts.root, item.path);
|
|
14351
14463
|
const isTracked = rel !== null && opts.tracked.has(rel);
|
|
14352
|
-
const keyPath = rel !== null ? opts.tracked.has(rel) ? rel : null :
|
|
14464
|
+
const keyPath = rel !== null ? opts.tracked.has(rel) ? rel : null : resolve10(opts.root, item.path);
|
|
14353
14465
|
if (keyPath === null) continue;
|
|
14354
14466
|
const target = isTracked ? repo : external;
|
|
14355
14467
|
const key2 = `${isTracked ? "repo" : "external"}\0${keyPath}`;
|
|
@@ -14428,22 +14540,22 @@ function materializeBase(base, cwd, dest) {
|
|
|
14428
14540
|
}
|
|
14429
14541
|
const hostModules = join21(cwd, "node_modules");
|
|
14430
14542
|
const tracedModules = join21(dest, "node_modules");
|
|
14431
|
-
if (
|
|
14432
|
-
symlinkSync2(
|
|
14543
|
+
if (existsSync15(hostModules) && !existsSync15(tracedModules)) {
|
|
14544
|
+
symlinkSync2(resolve10(hostModules), tracedModules, "dir");
|
|
14433
14545
|
}
|
|
14434
14546
|
}
|
|
14435
14547
|
function traceFiles(prefix) {
|
|
14436
|
-
const dir =
|
|
14548
|
+
const dir = resolve10(prefix, "..");
|
|
14437
14549
|
const base = prefix.split(sep4).at(-1) ?? prefix;
|
|
14438
14550
|
return readdirSync6(dir).filter((name) => name === base || name.startsWith(base + ".")).map((name) => join21(dir, name)).sort();
|
|
14439
14551
|
}
|
|
14440
14552
|
function rewriteTraceRoot(accesses, actualRoot) {
|
|
14441
14553
|
return accesses.map((item) => {
|
|
14442
|
-
const
|
|
14443
|
-
if (!inside3(actualRoot,
|
|
14444
|
-
const rel = relative6(actualRoot,
|
|
14554
|
+
const abs = isAbsolute11(item.path) ? resolve10(item.path) : resolve10(actualRoot, item.path);
|
|
14555
|
+
if (!inside3(actualRoot, abs)) return { ...item, path: abs };
|
|
14556
|
+
const rel = relative6(actualRoot, abs).split(sep4).join("/");
|
|
14445
14557
|
try {
|
|
14446
|
-
const real =
|
|
14558
|
+
const real = resolve10(realpathSync9(abs));
|
|
14447
14559
|
if (!inside3(actualRoot, real)) return { ...item, path: real };
|
|
14448
14560
|
} catch {
|
|
14449
14561
|
}
|
|
@@ -14532,7 +14644,7 @@ function renderText2(report2) {
|
|
|
14532
14644
|
for (const note of report2.notes) out3(`note: ${note}`);
|
|
14533
14645
|
}
|
|
14534
14646
|
function runTraceVerify(opts = {}) {
|
|
14535
|
-
const cwd =
|
|
14647
|
+
const cwd = resolve10(opts.cwd ?? process.cwd());
|
|
14536
14648
|
if (process.platform !== "linux") {
|
|
14537
14649
|
process.stderr.write("tamperward trace-verify: Linux only in this release; unsupported platform \u2014 no parity is implied.\n");
|
|
14538
14650
|
return 2;
|
|
@@ -14680,9 +14792,9 @@ var init_trace_verify = __esm({
|
|
|
14680
14792
|
// src/cli/onboard.ts
|
|
14681
14793
|
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
14682
14794
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
14683
|
-
import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as
|
|
14795
|
+
import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as realpathSync10, renameSync as renameSync2, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
14684
14796
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
14685
|
-
import { join as join22, relative as relative7, resolve as
|
|
14797
|
+
import { join as join22, relative as relative7, resolve as resolve11, sep as sep5 } from "node:path";
|
|
14686
14798
|
import { createInterface } from "node:readline";
|
|
14687
14799
|
import { parseDocument } from "yaml";
|
|
14688
14800
|
function readlineAsker(input, output) {
|
|
@@ -14691,9 +14803,9 @@ function readlineAsker(input, output) {
|
|
|
14691
14803
|
let pending = null;
|
|
14692
14804
|
rl.on("close", () => {
|
|
14693
14805
|
closed = true;
|
|
14694
|
-
const
|
|
14806
|
+
const resolve16 = pending;
|
|
14695
14807
|
pending = null;
|
|
14696
|
-
|
|
14808
|
+
resolve16?.(null);
|
|
14697
14809
|
});
|
|
14698
14810
|
rl.on("SIGINT", () => rl.close());
|
|
14699
14811
|
const ask = (question) => new Promise((resolvePromise) => {
|
|
@@ -14719,7 +14831,7 @@ function terminalText(text) {
|
|
|
14719
14831
|
return stripControl(text).replace(/\s+/g, " ").trim();
|
|
14720
14832
|
}
|
|
14721
14833
|
function committableSetupPath(cwd, path) {
|
|
14722
|
-
const rel = relative7(cwd,
|
|
14834
|
+
const rel = relative7(cwd, resolve11(cwd, path));
|
|
14723
14835
|
if (!rel || rel === ".." || rel.startsWith(".." + sep5)) return false;
|
|
14724
14836
|
return rel !== ".git" && !rel.startsWith(".git" + sep5);
|
|
14725
14837
|
}
|
|
@@ -14788,7 +14900,7 @@ function writeVerifyCommand(cwd, command) {
|
|
|
14788
14900
|
}
|
|
14789
14901
|
}
|
|
14790
14902
|
async function runOnboard(opts, io = {}) {
|
|
14791
|
-
const requestedCwd =
|
|
14903
|
+
const requestedCwd = resolve11(opts.cwd ?? process.cwd());
|
|
14792
14904
|
const out3 = io.out ?? ((line) => void process.stdout.write(line + "\n"));
|
|
14793
14905
|
const rawErr = (line) => void process.stderr.write(terminalText(line) + "\n");
|
|
14794
14906
|
const platform = io.platform ?? process.platform;
|
|
@@ -14824,10 +14936,10 @@ async function runOnboard(opts, io = {}) {
|
|
|
14824
14936
|
fail(requestedCwd + " is not inside a Git repository. Run `git init` first.");
|
|
14825
14937
|
return 2;
|
|
14826
14938
|
}
|
|
14827
|
-
const cwd =
|
|
14939
|
+
const cwd = resolve11(rootText);
|
|
14828
14940
|
let sameRoot = cwd === requestedCwd;
|
|
14829
14941
|
try {
|
|
14830
|
-
sameRoot =
|
|
14942
|
+
sameRoot = realpathSync10(cwd) === realpathSync10(requestedCwd);
|
|
14831
14943
|
} catch {
|
|
14832
14944
|
}
|
|
14833
14945
|
if (!sameRoot) {
|
|
@@ -15160,8 +15272,8 @@ function runDemo(cwd, head, runners, out3) {
|
|
|
15160
15272
|
let worktreeRoot;
|
|
15161
15273
|
let realTarget;
|
|
15162
15274
|
try {
|
|
15163
|
-
worktreeRoot =
|
|
15164
|
-
realTarget =
|
|
15275
|
+
worktreeRoot = realpathSync10(wt);
|
|
15276
|
+
realTarget = realpathSync10(targetPath);
|
|
15165
15277
|
} catch {
|
|
15166
15278
|
out3("Demo skipped: the selected test could not be resolved safely inside the disposable worktree.");
|
|
15167
15279
|
return;
|
|
@@ -15235,7 +15347,7 @@ var init_onboard = __esm({
|
|
|
15235
15347
|
});
|
|
15236
15348
|
|
|
15237
15349
|
// src/research/adapter.ts
|
|
15238
|
-
import { isAbsolute as isAbsolute12, resolve as
|
|
15350
|
+
import { isAbsolute as isAbsolute12, resolve as resolve12 } from "node:path";
|
|
15239
15351
|
function taskEnv(task) {
|
|
15240
15352
|
return {
|
|
15241
15353
|
TAMPERWARD_RESEARCH_TASK: task.id,
|
|
@@ -15251,7 +15363,7 @@ function normalizeCommandArgv(argv, cwd = process.cwd()) {
|
|
|
15251
15363
|
throw new ResearchError('the command adapter needs an agent command after "--"');
|
|
15252
15364
|
}
|
|
15253
15365
|
const head = argv[0];
|
|
15254
|
-
return head.includes("/") && !isAbsolute12(head) ? [
|
|
15366
|
+
return head.includes("/") && !isAbsolute12(head) ? [resolve12(cwd, head), ...argv.slice(1)] : [...argv];
|
|
15255
15367
|
}
|
|
15256
15368
|
function commandAdapter(argv, cwd = process.cwd()) {
|
|
15257
15369
|
const anchored = normalizeCommandArgv(argv, cwd);
|
|
@@ -15352,7 +15464,7 @@ var init_capture = __esm({
|
|
|
15352
15464
|
// src/research/manifest.ts
|
|
15353
15465
|
import { createHash as createHash12 } from "node:crypto";
|
|
15354
15466
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
15355
|
-
import { dirname as dirname9, resolve as
|
|
15467
|
+
import { dirname as dirname9, resolve as resolve13 } from "node:path";
|
|
15356
15468
|
function taskFrom(raw, index, manifestDir) {
|
|
15357
15469
|
const where = `task #${index + 1}`;
|
|
15358
15470
|
if (!isRecord(raw)) throw new ResearchError(`task manifest: ${where} is not an object`);
|
|
@@ -15375,40 +15487,40 @@ function taskFrom(raw, index, manifestDir) {
|
|
|
15375
15487
|
const isUrl = /^[a-z][a-z0-9+.-]*:\/\//i.test(repo) || /^[^/]+@[^:]+:/.test(repo);
|
|
15376
15488
|
return {
|
|
15377
15489
|
id,
|
|
15378
|
-
repo: isUrl ? repo :
|
|
15490
|
+
repo: isUrl ? repo : resolve13(manifestDir, repo),
|
|
15379
15491
|
base: base ?? "HEAD",
|
|
15380
15492
|
prompt,
|
|
15381
15493
|
verify: { command: verify.command, ...budget !== void 0 ? { budget } : {} }
|
|
15382
15494
|
};
|
|
15383
15495
|
}
|
|
15384
15496
|
function readManifest(path) {
|
|
15385
|
-
const
|
|
15497
|
+
const abs = resolve13(path);
|
|
15386
15498
|
let bytes;
|
|
15387
15499
|
try {
|
|
15388
|
-
bytes = readFileSync21(
|
|
15500
|
+
bytes = readFileSync21(abs);
|
|
15389
15501
|
} catch (e) {
|
|
15390
|
-
throw new ResearchError(`cannot read task manifest ${
|
|
15502
|
+
throw new ResearchError(`cannot read task manifest ${abs}: ${e instanceof Error ? e.message : String(e)}`);
|
|
15391
15503
|
}
|
|
15392
15504
|
let raw;
|
|
15393
15505
|
try {
|
|
15394
15506
|
raw = JSON.parse(bytes.toString("utf8"));
|
|
15395
15507
|
} catch {
|
|
15396
|
-
throw new ResearchError(`task manifest ${
|
|
15508
|
+
throw new ResearchError(`task manifest ${abs} is not valid JSON`);
|
|
15397
15509
|
}
|
|
15398
|
-
if (!isRecord(raw)) throw new ResearchError(`task manifest ${
|
|
15510
|
+
if (!isRecord(raw)) throw new ResearchError(`task manifest ${abs}: top level must be an object`);
|
|
15399
15511
|
if (raw.version !== MANIFEST_VERSION) {
|
|
15400
|
-
throw new ResearchError(`task manifest ${
|
|
15512
|
+
throw new ResearchError(`task manifest ${abs}: version must be ${MANIFEST_VERSION} (got ${JSON.stringify(raw.version)})`);
|
|
15401
15513
|
}
|
|
15402
15514
|
if (!Array.isArray(raw.tasks) || raw.tasks.length === 0) {
|
|
15403
|
-
throw new ResearchError(`task manifest ${
|
|
15515
|
+
throw new ResearchError(`task manifest ${abs}: no tasks (tasks must be a non-empty array)`);
|
|
15404
15516
|
}
|
|
15405
|
-
const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname9(
|
|
15517
|
+
const tasks = raw.tasks.map((t, i) => taskFrom(t, i, dirname9(abs)));
|
|
15406
15518
|
const seen = /* @__PURE__ */ new Set();
|
|
15407
15519
|
for (const t of tasks) {
|
|
15408
|
-
if (seen.has(t.id)) throw new ResearchError(`task manifest ${
|
|
15520
|
+
if (seen.has(t.id)) throw new ResearchError(`task manifest ${abs}: duplicate task id "${t.id}"`);
|
|
15409
15521
|
seen.add(t.id);
|
|
15410
15522
|
}
|
|
15411
|
-
return { path:
|
|
15523
|
+
return { path: abs, sha256: createHash12("sha256").update(bytes).digest("hex"), tasks };
|
|
15412
15524
|
}
|
|
15413
15525
|
var MANIFEST_VERSION;
|
|
15414
15526
|
var init_manifest = __esm({
|
|
@@ -15597,8 +15709,8 @@ var init_record = __esm({
|
|
|
15597
15709
|
|
|
15598
15710
|
// src/research/run.ts
|
|
15599
15711
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
15600
|
-
import { existsSync as
|
|
15601
|
-
import { join as join23, resolve as
|
|
15712
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync3, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
|
|
15713
|
+
import { join as join23, resolve as resolve14 } from "node:path";
|
|
15602
15714
|
function git6(args, cwd) {
|
|
15603
15715
|
return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
15604
15716
|
}
|
|
@@ -15661,9 +15773,9 @@ function freshWorkspace(ledger, task, pair, arm, sourceBase) {
|
|
|
15661
15773
|
git6(["config", "user.email", "research@tamperward.invalid"], ws2);
|
|
15662
15774
|
const requestedBase = sourceBase ?? task.base;
|
|
15663
15775
|
const candidates = sourceBase !== void 0 ? [sourceBase] : task.base === "HEAD" ? ["HEAD"] : [task.base, `origin/${task.base}`];
|
|
15664
|
-
for (const
|
|
15776
|
+
for (const rev2 of candidates) {
|
|
15665
15777
|
try {
|
|
15666
|
-
git6(["checkout", "-q", "--detach",
|
|
15778
|
+
git6(["checkout", "-q", "--detach", rev2], ws2);
|
|
15667
15779
|
return ws2;
|
|
15668
15780
|
} catch {
|
|
15669
15781
|
}
|
|
@@ -15897,7 +16009,7 @@ function runResearch(opts) {
|
|
|
15897
16009
|
err2("tamperward research: the gated arm cannot start here (see `tamperward doctor`); run as a non-root user on Linux.");
|
|
15898
16010
|
return 2;
|
|
15899
16011
|
}
|
|
15900
|
-
const ledger =
|
|
16012
|
+
const ledger = resolve14(opts.out);
|
|
15901
16013
|
const pairs = opts.pairs ?? 1;
|
|
15902
16014
|
mkdirSync8(join23(ledger, "pairs"), { recursive: true });
|
|
15903
16015
|
for (const task of tasks) {
|
|
@@ -15906,7 +16018,7 @@ function runResearch(opts) {
|
|
|
15906
16018
|
try {
|
|
15907
16019
|
for (let pair = 1; pair <= pairs; pair++) {
|
|
15908
16020
|
const path = pairRecordPath(ledger, task.id, pair);
|
|
15909
|
-
if (!
|
|
16021
|
+
if (!existsSync16(path)) continue;
|
|
15910
16022
|
const existing = resumableRecord(path, {
|
|
15911
16023
|
task: task.id,
|
|
15912
16024
|
pair,
|
|
@@ -16032,9 +16144,9 @@ var init_run2 = __esm({
|
|
|
16032
16144
|
|
|
16033
16145
|
// src/research/summarize.ts
|
|
16034
16146
|
import { readdirSync as readdirSync7, readFileSync as readFileSync23 } from "node:fs";
|
|
16035
|
-
import { join as join24, resolve as
|
|
16147
|
+
import { join as join24, resolve as resolve15 } from "node:path";
|
|
16036
16148
|
function readLedger2(dir) {
|
|
16037
|
-
const pairsDir = join24(
|
|
16149
|
+
const pairsDir = join24(resolve15(dir), "pairs");
|
|
16038
16150
|
let names;
|
|
16039
16151
|
try {
|
|
16040
16152
|
names = readdirSync7(pairsDir).filter((n) => n.endsWith(".json")).sort();
|