tamperward 2.40.0 → 2.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +579 -338
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -162,7 +162,39 @@ function runtimeWiringOf(path) {
|
|
|
162
162
|
function resolvesToRuntimeWiring(path) {
|
|
163
163
|
return runtimeWiringOf(path) ?? (isAbsolute(path) ? runtimeWiringOf(canonicalPath(path)) : null);
|
|
164
164
|
}
|
|
165
|
-
|
|
165
|
+
function codexWiringTables() {
|
|
166
|
+
const q = (cmd) => JSON.stringify(cmd);
|
|
167
|
+
return [
|
|
168
|
+
`# ${RUNTIME_WIRING_NOTE}`,
|
|
169
|
+
"[hooks]",
|
|
170
|
+
"[[hooks.PreToolUse]]",
|
|
171
|
+
'matcher = "*"',
|
|
172
|
+
"[[hooks.PreToolUse.hooks]]",
|
|
173
|
+
'type = "command"',
|
|
174
|
+
`command = ${q(CODEX_HOOK_CMD)}`,
|
|
175
|
+
"[[hooks.Stop]]",
|
|
176
|
+
"[[hooks.Stop.hooks]]",
|
|
177
|
+
'type = "command"',
|
|
178
|
+
`command = ${q(CODEX_SWEEP_CMD)}`,
|
|
179
|
+
""
|
|
180
|
+
].join("\n");
|
|
181
|
+
}
|
|
182
|
+
function copilotWiringDocument() {
|
|
183
|
+
return {
|
|
184
|
+
version: 1,
|
|
185
|
+
hooks: {
|
|
186
|
+
preToolUse: [{ type: "command", command: COPILOT_HOOK_CMD, timeout: 60 }],
|
|
187
|
+
agentStop: [{ type: "command", command: COPILOT_SWEEP_CMD, timeout: 60 }]
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function runtimeGatePins(text) {
|
|
192
|
+
return [...text.matchAll(RUNTIME_GATE_RE)].map((m) => m[1]);
|
|
193
|
+
}
|
|
194
|
+
function withoutRuntimeGatePins(text) {
|
|
195
|
+
return text.replace(RUNTIME_GATE_RE, "tamperward@<pin> $2 $3");
|
|
196
|
+
}
|
|
197
|
+
var PLAIN_SEMVER, RESOLVED_TW_VERSION, TW_VERSION, GIT_HEAD_RE, TW_COMMIT, PRE_TOOLS, PRE_MATCHER, MARKER, NPX_AUTHORITY, AUTHORITY_FAIL_CLOSED, HOOK_CMD, SWEEP_CMD, PRECOMMIT_CMD, OURS, INIT_SCRIPT, CLAUDE_SETTINGS, CODEX_HOOK_CONFIG, COPILOT_HOOK_CONFIG, CODEX_HOOK_CMD, CODEX_SWEEP_CMD, COPILOT_HOOK_CMD, COPILOT_SWEEP_CMD, RUNTIME_GATE_RE, RUNTIME_WIRING_NOTE, COPILOT_WIRING_NOTE;
|
|
166
198
|
var init_wiring = __esm({
|
|
167
199
|
"src/wiring.ts"() {
|
|
168
200
|
"use strict";
|
|
@@ -185,6 +217,13 @@ var init_wiring = __esm({
|
|
|
185
217
|
CLAUDE_SETTINGS = /(?:^|\/)(?:\.claude\/settings(?:\.local)?\.json|managed-settings\.json)$/;
|
|
186
218
|
CODEX_HOOK_CONFIG = /(?:^|\/)\.codex\/config\.toml$/;
|
|
187
219
|
COPILOT_HOOK_CONFIG = /(?:^|\/)\.github\/hooks\/tamperward\.json$/;
|
|
220
|
+
CODEX_HOOK_CMD = `${NPX_AUTHORITY} tamperward@${TW_VERSION} hook codex${AUTHORITY_FAIL_CLOSED}`;
|
|
221
|
+
CODEX_SWEEP_CMD = `${NPX_AUTHORITY} tamperward@${TW_VERSION} sweep codex${AUTHORITY_FAIL_CLOSED}`;
|
|
222
|
+
COPILOT_HOOK_CMD = `${NPX_AUTHORITY} tamperward@${TW_VERSION} hook copilot${AUTHORITY_FAIL_CLOSED}`;
|
|
223
|
+
COPILOT_SWEEP_CMD = `${NPX_AUTHORITY} tamperward@${TW_VERSION} sweep copilot${AUTHORITY_FAIL_CLOSED}`;
|
|
224
|
+
RUNTIME_GATE_RE = /\btamperward@(\S+) (hook|sweep) (codex|copilot)\b/g;
|
|
225
|
+
RUNTIME_WIRING_NOTE = "tamperward: experimental hook wiring. This runtime has not passed a pinned live qualification; run `tamperward runtime verify` to see what is proven. Editing these entries is editing the gate.";
|
|
226
|
+
COPILOT_WIRING_NOTE = RUNTIME_WIRING_NOTE;
|
|
188
227
|
}
|
|
189
228
|
});
|
|
190
229
|
|
|
@@ -2765,11 +2804,20 @@ function codexHooksTables(toml) {
|
|
|
2765
2804
|
}
|
|
2766
2805
|
return out3.join("\n");
|
|
2767
2806
|
}
|
|
2807
|
+
function runtimeGatePinRaise(was, now) {
|
|
2808
|
+
if (now === was) return "same";
|
|
2809
|
+
if (withoutRuntimeGatePins(now) !== withoutRuntimeGatePins(was)) return "changed";
|
|
2810
|
+
const before = runtimeGatePins(was);
|
|
2811
|
+
const after = runtimeGatePins(now);
|
|
2812
|
+
return after.every((pin, i) => pinNotBelow(pin, before[i])) ? "raised" : "lowered";
|
|
2813
|
+
}
|
|
2768
2814
|
function codexHooksWeakening(before, after) {
|
|
2769
2815
|
const was = codexHooksTables(before);
|
|
2770
2816
|
if (!was) return [];
|
|
2771
2817
|
const now = codexHooksTables(after);
|
|
2772
|
-
|
|
2818
|
+
const change = runtimeGatePinRaise(was, now);
|
|
2819
|
+
if (change === "same" || change === "raised") return [];
|
|
2820
|
+
if (change === "lowered") return ["the tamperward pin in the [hooks] tables was lowered or is no longer a plain version \u2014 the gate only ever raises its pin"];
|
|
2773
2821
|
if (GATE.test(was) && !GATE.test(now)) return ["the tamperward hook entries were removed from the [hooks] tables \u2014 Codex no longer runs the gate"];
|
|
2774
2822
|
return ["the [hooks] tables changed \u2014 the PreToolUse and Stop wiring Codex reads is no longer what it was"];
|
|
2775
2823
|
}
|
|
@@ -2788,7 +2836,9 @@ function copilotHooksWeakening(before, after) {
|
|
|
2788
2836
|
if (was === null || was.hooks === null) return [];
|
|
2789
2837
|
const now = hooksMember(after);
|
|
2790
2838
|
if (now === null) return ["the file no longer parses as JSON \u2014 the Copilot CLI loads no hook from it"];
|
|
2791
|
-
|
|
2839
|
+
const change = runtimeGatePinRaise(was.hooks, now.hooks ?? "");
|
|
2840
|
+
if (change === "same" || change === "raised") return [];
|
|
2841
|
+
if (change === "lowered") return ['the tamperward pin in "hooks" was lowered or is no longer a plain version \u2014 the gate only ever raises its pin'];
|
|
2792
2842
|
if (GATE.test(was.hooks) && !GATE.test(now.hooks ?? "")) return ['the tamperward hook entries were removed from "hooks" \u2014 the Copilot CLI no longer runs the gate'];
|
|
2793
2843
|
return ['the "hooks" entries changed \u2014 the preToolUse and agentStop wiring the Copilot CLI reads is no longer what it was'];
|
|
2794
2844
|
}
|
|
@@ -14012,11 +14062,200 @@ var init_verifier_limits = __esm({
|
|
|
14012
14062
|
}
|
|
14013
14063
|
});
|
|
14014
14064
|
|
|
14065
|
+
// src/immutable.ts
|
|
14066
|
+
function isPlainData(value) {
|
|
14067
|
+
if (value === null || typeof value !== "object") return false;
|
|
14068
|
+
const proto = Object.getPrototypeOf(value);
|
|
14069
|
+
return proto === Object.prototype || proto === Array.prototype || proto === null;
|
|
14070
|
+
}
|
|
14071
|
+
function deepFreeze(value) {
|
|
14072
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
14073
|
+
const walk2 = (node) => {
|
|
14074
|
+
if (!isPlainData(node) || seen.has(node)) return;
|
|
14075
|
+
seen.add(node);
|
|
14076
|
+
Object.freeze(node);
|
|
14077
|
+
for (const key2 of Reflect.ownKeys(node)) walk2(Reflect.get(node, key2));
|
|
14078
|
+
};
|
|
14079
|
+
walk2(value);
|
|
14080
|
+
return value;
|
|
14081
|
+
}
|
|
14082
|
+
var init_immutable = __esm({
|
|
14083
|
+
"src/immutable.ts"() {
|
|
14084
|
+
"use strict";
|
|
14085
|
+
}
|
|
14086
|
+
});
|
|
14087
|
+
|
|
14088
|
+
// src/runtimes.ts
|
|
14089
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
14090
|
+
import { join as join15 } from "node:path";
|
|
14091
|
+
function isRecord4(x) {
|
|
14092
|
+
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
14093
|
+
}
|
|
14094
|
+
function hooksBeyondTamperward(hooks) {
|
|
14095
|
+
if (!isRecord4(hooks)) return hooks != null;
|
|
14096
|
+
for (const entries of Object.values(hooks)) {
|
|
14097
|
+
if (!Array.isArray(entries)) {
|
|
14098
|
+
if (entries != null) return true;
|
|
14099
|
+
continue;
|
|
14100
|
+
}
|
|
14101
|
+
for (const entry of entries) {
|
|
14102
|
+
const hs = isRecord4(entry) ? entry.hooks : null;
|
|
14103
|
+
if (!Array.isArray(hs)) {
|
|
14104
|
+
if (hs != null) return true;
|
|
14105
|
+
continue;
|
|
14106
|
+
}
|
|
14107
|
+
for (const h of hs) {
|
|
14108
|
+
const cmd = isRecord4(h) ? String(h.command ?? "") : "";
|
|
14109
|
+
if (!/tamperward/.test(cmd)) return true;
|
|
14110
|
+
}
|
|
14111
|
+
}
|
|
14112
|
+
}
|
|
14113
|
+
return false;
|
|
14114
|
+
}
|
|
14115
|
+
function claudeSettingsBeyondTamperward(cwd) {
|
|
14116
|
+
const path = join15(cwd, ".claude", "settings.json");
|
|
14117
|
+
let raw;
|
|
14118
|
+
try {
|
|
14119
|
+
if (!existsSync7(path)) return false;
|
|
14120
|
+
raw = readFileSync8(path, "utf8");
|
|
14121
|
+
} catch {
|
|
14122
|
+
return false;
|
|
14123
|
+
}
|
|
14124
|
+
let parsed;
|
|
14125
|
+
try {
|
|
14126
|
+
parsed = JSON.parse(raw);
|
|
14127
|
+
} catch {
|
|
14128
|
+
return raw.trim().length > 0;
|
|
14129
|
+
}
|
|
14130
|
+
if (!isRecord4(parsed)) return raw.trim().length > 0;
|
|
14131
|
+
for (const [key2, value] of Object.entries(parsed)) {
|
|
14132
|
+
if (key2 === "disableAllHooks") continue;
|
|
14133
|
+
if (key2 === "hooks") {
|
|
14134
|
+
if (hooksBeyondTamperward(value)) return true;
|
|
14135
|
+
continue;
|
|
14136
|
+
}
|
|
14137
|
+
return true;
|
|
14138
|
+
}
|
|
14139
|
+
return false;
|
|
14140
|
+
}
|
|
14141
|
+
function codexDirBeyondTamperward(cwd) {
|
|
14142
|
+
const dir = join15(cwd, ".codex");
|
|
14143
|
+
let entries;
|
|
14144
|
+
try {
|
|
14145
|
+
if (!existsSync7(dir) || !statSync3(dir).isDirectory()) return false;
|
|
14146
|
+
entries = readdirSync3(dir);
|
|
14147
|
+
} catch {
|
|
14148
|
+
return false;
|
|
14149
|
+
}
|
|
14150
|
+
if (entries.some((e) => e !== "config.toml")) return true;
|
|
14151
|
+
if (!entries.includes("config.toml")) return false;
|
|
14152
|
+
let raw;
|
|
14153
|
+
try {
|
|
14154
|
+
raw = readFileSync8(join15(dir, "config.toml"), "utf8");
|
|
14155
|
+
} catch {
|
|
14156
|
+
return false;
|
|
14157
|
+
}
|
|
14158
|
+
return raw.split("\n").some((line) => {
|
|
14159
|
+
const t = line.trim();
|
|
14160
|
+
return t !== "" && !t.startsWith("#") && !/^\[\[?hooks/.test(t) && !codexWiringLines().has(t);
|
|
14161
|
+
});
|
|
14162
|
+
}
|
|
14163
|
+
function codexWiringLines() {
|
|
14164
|
+
if (!wiringLines) wiringLines = new Set(codexWiringTables().split("\n").map((l) => l.trim()).filter(Boolean));
|
|
14165
|
+
return wiringLines;
|
|
14166
|
+
}
|
|
14167
|
+
function detectRuntimes(cwd) {
|
|
14168
|
+
const found = [];
|
|
14169
|
+
for (const rt of KNOWN_RUNTIMES) {
|
|
14170
|
+
const marker = rt.markers.find((m) => {
|
|
14171
|
+
try {
|
|
14172
|
+
return existsSync7(join15(cwd, m));
|
|
14173
|
+
} catch {
|
|
14174
|
+
return false;
|
|
14175
|
+
}
|
|
14176
|
+
});
|
|
14177
|
+
const matchedMarker = marker ?? rt.detectExtra?.(cwd) ?? void 0;
|
|
14178
|
+
if (matchedMarker !== void 0) found.push({ ...rt, matchedMarker });
|
|
14179
|
+
}
|
|
14180
|
+
return found;
|
|
14181
|
+
}
|
|
14182
|
+
function hasInLoopRuntime(detected) {
|
|
14183
|
+
return detected.some((r) => r.steering === "in-loop");
|
|
14184
|
+
}
|
|
14185
|
+
function detectionHeadline(detected) {
|
|
14186
|
+
if (detected.length === 0) {
|
|
14187
|
+
return "No agent runtime detected. Wiring Claude Code in-loop steering plus the agent-neutral layers (pre-commit + CI) that protect any agent or human.";
|
|
14188
|
+
}
|
|
14189
|
+
if (detected.length === 1) {
|
|
14190
|
+
const r = detected[0];
|
|
14191
|
+
return `Detected ${r.label} \u2014 ${r.note}`;
|
|
14192
|
+
}
|
|
14193
|
+
const parts = detected.map((r) => `${r.label} (${r.steering === "in-loop" ? "in-loop" : "neutral layers"})`);
|
|
14194
|
+
return `Detected ${detected.length} agent runtimes: ${parts.join(", ")}.`;
|
|
14195
|
+
}
|
|
14196
|
+
function neutralOnlyCaveat(detected) {
|
|
14197
|
+
const neutral = detected.filter((r) => r.steering === "neutral");
|
|
14198
|
+
if (neutral.length === 0) return null;
|
|
14199
|
+
const names = neutral.map((r) => r.label).join(", ");
|
|
14200
|
+
const claudeAlso = hasInLoopRuntime(detected);
|
|
14201
|
+
const lead = claudeAlso ? `In-loop steering (deny-before-execute) ships for Claude Code only today: Claude Code here gets it, but on ${names} your live protection is ` : `In-loop steering (deny-before-execute) ships for Claude Code only today, so on ${names} your live protection is `;
|
|
14202
|
+
const tail = claudeAlso ? "" : " The Claude hook wiring is still written and is inert unless Claude Code runs in this repository.";
|
|
14203
|
+
return `${lead}the agent-neutral layers: pre-commit and CI.${tail}`;
|
|
14204
|
+
}
|
|
14205
|
+
var wiringLines, KNOWN_RUNTIMES;
|
|
14206
|
+
var init_runtimes = __esm({
|
|
14207
|
+
"src/runtimes.ts"() {
|
|
14208
|
+
"use strict";
|
|
14209
|
+
init_immutable();
|
|
14210
|
+
init_wiring();
|
|
14211
|
+
wiringLines = null;
|
|
14212
|
+
KNOWN_RUNTIMES = deepFreeze([
|
|
14213
|
+
{
|
|
14214
|
+
id: "claude-code",
|
|
14215
|
+
// Markers are Claude Code files `init` never writes, so detection never mistakes
|
|
14216
|
+
// TamperWard's own generated `.claude/settings.json` for evidence a human runs
|
|
14217
|
+
// Claude here (#526). `detectExtra` looks at that generated file's CONTENT for
|
|
14218
|
+
// real use (configuration beyond the two hooks init merges in).
|
|
14219
|
+
label: "Claude Code",
|
|
14220
|
+
markers: ["CLAUDE.md", ".claude/settings.local.json", ".claude/commands", ".claude/agents"],
|
|
14221
|
+
detectExtra: (cwd) => claudeSettingsBeyondTamperward(cwd) ? ".claude/settings.json" : null,
|
|
14222
|
+
steering: "in-loop",
|
|
14223
|
+
note: "native in-loop steering (PreToolUse deny + Stop sweep) plus the agent-neutral layers."
|
|
14224
|
+
},
|
|
14225
|
+
{
|
|
14226
|
+
id: "cursor",
|
|
14227
|
+
label: "Cursor",
|
|
14228
|
+
markers: [".cursor", ".cursorrules"],
|
|
14229
|
+
steering: "neutral",
|
|
14230
|
+
note: "agent-neutral layers (pre-commit + CI) today; no in-loop adapter ships for Cursor."
|
|
14231
|
+
},
|
|
14232
|
+
{
|
|
14233
|
+
id: "copilot",
|
|
14234
|
+
label: "GitHub Copilot",
|
|
14235
|
+
markers: [".github/copilot-instructions.md", ".github/copilot"],
|
|
14236
|
+
steering: "neutral",
|
|
14237
|
+
note: "agent-neutral layers (pre-commit + CI) today; the experimental Copilot adapters are not qualified for in-loop steering (see `tamperward runtime verify`)."
|
|
14238
|
+
},
|
|
14239
|
+
{
|
|
14240
|
+
id: "codex",
|
|
14241
|
+
label: "an AGENTS.md-aware agent (e.g. Codex)",
|
|
14242
|
+
// `.codex/config.toml` is a file `init` writes (the hook wiring), so the directory
|
|
14243
|
+
// counts only for what else it holds, or for configuration in that file beyond the
|
|
14244
|
+
// hooks tables init appends (#526).
|
|
14245
|
+
markers: ["AGENTS.md"],
|
|
14246
|
+
detectExtra: (cwd) => codexDirBeyondTamperward(cwd) ? ".codex" : null,
|
|
14247
|
+
steering: "neutral",
|
|
14248
|
+
note: "agent-neutral layers (pre-commit + CI) today; the experimental Codex adapter is not qualified for in-loop steering (see `tamperward runtime verify`)."
|
|
14249
|
+
}
|
|
14250
|
+
]);
|
|
14251
|
+
}
|
|
14252
|
+
});
|
|
14253
|
+
|
|
14015
14254
|
// src/cli/init.ts
|
|
14016
14255
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
14017
14256
|
import { createHash as createHash8 } from "node:crypto";
|
|
14018
|
-
import { existsSync as
|
|
14019
|
-
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute9, join as
|
|
14257
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, realpathSync as realpathSync5 } from "node:fs";
|
|
14258
|
+
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute9, join as join16, relative as relative4, resolve as resolve6 } from "node:path";
|
|
14020
14259
|
function gitConfig(cwd, key2) {
|
|
14021
14260
|
try {
|
|
14022
14261
|
return execFileSync6("git", ["config", "--get", key2], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
@@ -14109,34 +14348,34 @@ function verifierCandidates(cwd) {
|
|
|
14109
14348
|
if (!candidates.includes(command)) candidates.push(command);
|
|
14110
14349
|
};
|
|
14111
14350
|
try {
|
|
14112
|
-
const pkg = JSON.parse(
|
|
14351
|
+
const pkg = JSON.parse(readFileSync9(join16(cwd, "package.json"), "utf8"));
|
|
14113
14352
|
const test = isRecord(pkg) && isRecord(pkg.scripts) ? pkg.scripts.test : void 0;
|
|
14114
14353
|
if (typeof test === "string" && test.trim() !== "" && !/error:\s*no test specified/i.test(test)) add("npm test");
|
|
14115
14354
|
} catch {
|
|
14116
14355
|
}
|
|
14117
14356
|
try {
|
|
14118
|
-
const pyproject =
|
|
14357
|
+
const pyproject = readFileSync9(join16(cwd, "pyproject.toml"), "utf8");
|
|
14119
14358
|
if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
|
|
14120
14359
|
} catch {
|
|
14121
14360
|
}
|
|
14122
|
-
if (
|
|
14361
|
+
if (existsSync8(join16(cwd, "pytest.ini"))) add("pytest");
|
|
14123
14362
|
try {
|
|
14124
|
-
const setupCfg =
|
|
14363
|
+
const setupCfg = readFileSync9(join16(cwd, "setup.cfg"), "utf8");
|
|
14125
14364
|
if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
|
|
14126
14365
|
} catch {
|
|
14127
14366
|
}
|
|
14128
14367
|
try {
|
|
14129
|
-
const tox =
|
|
14368
|
+
const tox = readFileSync9(join16(cwd, "tox.ini"), "utf8");
|
|
14130
14369
|
if (/^\s*\[tox\]\s*$/m.test(tox)) add("tox");
|
|
14131
14370
|
} catch {
|
|
14132
14371
|
}
|
|
14133
14372
|
try {
|
|
14134
|
-
const cargo =
|
|
14373
|
+
const cargo = readFileSync9(join16(cwd, "Cargo.toml"), "utf8");
|
|
14135
14374
|
if (/^\s*\[(?:package|workspace)\]\s*(?:#.*)?$/m.test(cargo)) add("cargo test");
|
|
14136
14375
|
} catch {
|
|
14137
14376
|
}
|
|
14138
14377
|
try {
|
|
14139
|
-
const goMod =
|
|
14378
|
+
const goMod = readFileSync9(join16(cwd, "go.mod"), "utf8");
|
|
14140
14379
|
if (/^\s*module\s+\S+\s*(?:\/\/.*)?$/m.test(goMod)) add("go test ./...");
|
|
14141
14380
|
} catch {
|
|
14142
14381
|
}
|
|
@@ -14176,10 +14415,10 @@ function verifierSetupMessage(cwd) {
|
|
|
14176
14415
|
return lines.join("\n");
|
|
14177
14416
|
}
|
|
14178
14417
|
function planPolicy(cwd) {
|
|
14179
|
-
const path =
|
|
14418
|
+
const path = join16(cwd, POLICY_FILE);
|
|
14180
14419
|
const refused = refuseNonRegular(path);
|
|
14181
14420
|
if (refused) return { item: "policy", path: POLICY_FILE, status: "error", detail: refused };
|
|
14182
|
-
if (
|
|
14421
|
+
if (existsSync8(path)) {
|
|
14183
14422
|
try {
|
|
14184
14423
|
loadPolicy(cwd);
|
|
14185
14424
|
} catch (e) {
|
|
@@ -14197,8 +14436,8 @@ function planPolicy(cwd) {
|
|
|
14197
14436
|
}
|
|
14198
14437
|
function planGitignore(cwd) {
|
|
14199
14438
|
const rel = ".gitignore";
|
|
14200
|
-
const path =
|
|
14201
|
-
if (!
|
|
14439
|
+
const path = join16(cwd, rel);
|
|
14440
|
+
if (!existsSync8(join16(cwd, "node_modules"))) return null;
|
|
14202
14441
|
let existing = "";
|
|
14203
14442
|
let kind;
|
|
14204
14443
|
try {
|
|
@@ -14208,7 +14447,7 @@ function planGitignore(cwd) {
|
|
|
14208
14447
|
}
|
|
14209
14448
|
const refused = refuseNonRegular(path);
|
|
14210
14449
|
if (refused) return { item: "gitignore", path: rel, status: "error", detail: refused };
|
|
14211
|
-
if (kind === "file") existing =
|
|
14450
|
+
if (kind === "file") existing = readFileSync9(path, "utf8");
|
|
14212
14451
|
try {
|
|
14213
14452
|
const tracked = execFileSync6("git", ["ls-files", "--cached", "--", "node_modules"], {
|
|
14214
14453
|
cwd,
|
|
@@ -14259,14 +14498,14 @@ function gateVerdicts(event, groups, needle) {
|
|
|
14259
14498
|
}
|
|
14260
14499
|
function planClaudeHooks(cwd) {
|
|
14261
14500
|
const rel = ".claude/settings.json";
|
|
14262
|
-
const path =
|
|
14501
|
+
const path = join16(cwd, rel);
|
|
14263
14502
|
const refused = refuseNonRegular(path);
|
|
14264
14503
|
if (refused) return { item: "agent", path: rel, status: "error", detail: refused };
|
|
14265
14504
|
let settings = {};
|
|
14266
|
-
if (
|
|
14505
|
+
if (existsSync8(path)) {
|
|
14267
14506
|
let parsed;
|
|
14268
14507
|
try {
|
|
14269
|
-
parsed = JSON.parse(
|
|
14508
|
+
parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
14270
14509
|
} catch {
|
|
14271
14510
|
return {
|
|
14272
14511
|
item: "agent",
|
|
@@ -14315,7 +14554,7 @@ function planClaudeHooks(cwd) {
|
|
|
14315
14554
|
return {
|
|
14316
14555
|
item: "agent",
|
|
14317
14556
|
path: rel,
|
|
14318
|
-
status:
|
|
14557
|
+
status: existsSync8(path) ? "update" : "create",
|
|
14319
14558
|
detail: [
|
|
14320
14559
|
needPre && "wire PreToolUse deny",
|
|
14321
14560
|
needStop && "wire Stop sweep",
|
|
@@ -14359,7 +14598,7 @@ function hooksDir(cwd) {
|
|
|
14359
14598
|
if (out3) return resolve6(cwd, out3);
|
|
14360
14599
|
} catch {
|
|
14361
14600
|
}
|
|
14362
|
-
return
|
|
14601
|
+
return existsSync8(join16(cwd, ".git")) ? join16(cwd, ".git", "hooks") : null;
|
|
14363
14602
|
}
|
|
14364
14603
|
function display(cwd, path) {
|
|
14365
14604
|
const rel = relative4(cwd, path);
|
|
@@ -14386,29 +14625,29 @@ function planPreCommit(cwd) {
|
|
|
14386
14625
|
const skip = (detail) => ({ item: "pre-commit", path: "(none)", status: "skip", detail });
|
|
14387
14626
|
let path;
|
|
14388
14627
|
let note;
|
|
14389
|
-
if (
|
|
14390
|
-
path =
|
|
14628
|
+
if (existsSync8(join16(cwd, ".husky"))) {
|
|
14629
|
+
path = join16(cwd, ".husky", "pre-commit");
|
|
14391
14630
|
note = "husky";
|
|
14392
14631
|
} else {
|
|
14393
|
-
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) =>
|
|
14632
|
+
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) => existsSync8(join16(cwd, c))));
|
|
14394
14633
|
if (managed) {
|
|
14395
14634
|
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}`);
|
|
14396
14635
|
}
|
|
14397
14636
|
const dir = hooksDir(cwd);
|
|
14398
14637
|
if (!dir) return skip("not a git repo and no .husky/ \u2014 nothing to wire");
|
|
14399
14638
|
if (basename3(dir) === "_" && basename3(dirname5(dir)) === ".husky") {
|
|
14400
|
-
path =
|
|
14639
|
+
path = join16(dirname5(dir), "pre-commit");
|
|
14401
14640
|
note = "husky";
|
|
14402
14641
|
} else {
|
|
14403
|
-
path =
|
|
14642
|
+
path = join16(dir, "pre-commit");
|
|
14404
14643
|
const rel2 = display(cwd, dir);
|
|
14405
|
-
note = rel2 ===
|
|
14644
|
+
note = rel2 === join16(".git", "hooks") ? "plain git hook (local-only: .git/hooks is not committed \u2014 consider husky to share it)" : `git hooks directory ${rel2} (core.hooksPath)`;
|
|
14406
14645
|
}
|
|
14407
14646
|
}
|
|
14408
14647
|
const rel = display(cwd, path);
|
|
14409
14648
|
const refused = refuseNonRegular(path);
|
|
14410
14649
|
if (refused) return { item: "pre-commit", path: rel, status: "error", detail: refused };
|
|
14411
|
-
const existing =
|
|
14650
|
+
const existing = existsSync8(path) ? readFileSync9(path, "utf8") : null;
|
|
14412
14651
|
const hookMode = () => existingMode(path, 493) | 73;
|
|
14413
14652
|
if (existing === null) {
|
|
14414
14653
|
return {
|
|
@@ -14494,12 +14733,12 @@ ${block.join("\n")}
|
|
|
14494
14733
|
function planCodeowners(cwd) {
|
|
14495
14734
|
const rel = ".github/CODEOWNERS";
|
|
14496
14735
|
const existingRel = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"].find(
|
|
14497
|
-
(r) => writeTargetKind(
|
|
14736
|
+
(r) => writeTargetKind(join16(cwd, r)) !== "absent"
|
|
14498
14737
|
);
|
|
14499
|
-
const path =
|
|
14738
|
+
const path = join16(cwd, existingRel ?? rel);
|
|
14500
14739
|
const refused = refuseNonRegular(path);
|
|
14501
14740
|
if (refused) return { item: "codeowners", path: existingRel ?? rel, status: "error", detail: refused };
|
|
14502
|
-
const existing = existingRel ?
|
|
14741
|
+
const existing = existingRel ? readFileSync9(path, "utf8") : null;
|
|
14503
14742
|
const missing = CODEOWNERS_PATHS.filter((p) => existing === null || !coveredBy(existing, p));
|
|
14504
14743
|
if (existingRel !== void 0 && missing.length === 0) {
|
|
14505
14744
|
return { item: "codeowners", path: existingRel, status: "ok", detail: "gate paths already have code owners" };
|
|
@@ -14532,14 +14771,14 @@ ${CODEOWNERS_MARK}
|
|
|
14532
14771
|
}
|
|
14533
14772
|
function planWorkflow(cwd, force) {
|
|
14534
14773
|
const rel = ".github/workflows/tamperward.yml";
|
|
14535
|
-
const path =
|
|
14774
|
+
const path = join16(cwd, rel);
|
|
14536
14775
|
const write = () => {
|
|
14537
14776
|
mkdirSync7(dirname5(path), { recursive: true });
|
|
14538
14777
|
atomicReplaceFile(path, workflowFile(WORKFLOW_CONTENT), existingMode(path, 420));
|
|
14539
14778
|
};
|
|
14540
14779
|
const refused = refuseNonRegular(path);
|
|
14541
14780
|
if (refused) return { item: "ci", path: rel, status: "error", detail: refused };
|
|
14542
|
-
if (!
|
|
14781
|
+
if (!existsSync8(path)) {
|
|
14543
14782
|
return {
|
|
14544
14783
|
item: "ci",
|
|
14545
14784
|
path: rel,
|
|
@@ -14550,7 +14789,7 @@ function planWorkflow(cwd, force) {
|
|
|
14550
14789
|
}
|
|
14551
14790
|
let src;
|
|
14552
14791
|
try {
|
|
14553
|
-
src =
|
|
14792
|
+
src = readFileSync9(path, "utf8");
|
|
14554
14793
|
} catch {
|
|
14555
14794
|
return { item: "ci", path: rel, status: "error", detail: "exists but cannot be read \u2014 refusing to overwrite" };
|
|
14556
14795
|
}
|
|
@@ -14598,6 +14837,96 @@ function sameDir(a, b) {
|
|
|
14598
14837
|
return false;
|
|
14599
14838
|
}
|
|
14600
14839
|
}
|
|
14840
|
+
function planCodexHooks(cwd) {
|
|
14841
|
+
const rel = ".codex/config.toml";
|
|
14842
|
+
const path = join16(cwd, rel);
|
|
14843
|
+
const item = "codex-hooks";
|
|
14844
|
+
const refused = refuseNonRegular(path);
|
|
14845
|
+
if (refused) return { item, path: rel, status: "error", detail: refused };
|
|
14846
|
+
const warning = RUNTIME_WARNING("Codex", "codex");
|
|
14847
|
+
const tables = codexWiringTables();
|
|
14848
|
+
if (!existsSync8(path)) {
|
|
14849
|
+
return {
|
|
14850
|
+
item,
|
|
14851
|
+
path: rel,
|
|
14852
|
+
status: "create",
|
|
14853
|
+
detail: "wire the PreToolUse deny and Stop sweep [hooks] tables (experimental)",
|
|
14854
|
+
warning,
|
|
14855
|
+
apply: () => {
|
|
14856
|
+
mkdirSync7(dirname5(path), { recursive: true });
|
|
14857
|
+
atomicReplaceFile(path, tables, 420);
|
|
14858
|
+
}
|
|
14859
|
+
};
|
|
14860
|
+
}
|
|
14861
|
+
const current = readFileSync9(path, "utf8");
|
|
14862
|
+
const existing = codexHooksTables(current);
|
|
14863
|
+
if (!existing) {
|
|
14864
|
+
const next = current + (current.endsWith("\n") || current === "" ? "" : "\n") + "\n" + tables;
|
|
14865
|
+
return {
|
|
14866
|
+
item,
|
|
14867
|
+
path: rel,
|
|
14868
|
+
status: "update",
|
|
14869
|
+
detail: "append the PreToolUse deny and Stop sweep [hooks] tables (experimental)",
|
|
14870
|
+
warning,
|
|
14871
|
+
apply: () => atomicReplaceFile(path, next, existingMode(path, 420))
|
|
14872
|
+
};
|
|
14873
|
+
}
|
|
14874
|
+
if (existing === codexHooksTables(tables)) return { item, path: rel, status: "ok", detail: "PreToolUse + Stop hooks already wired" };
|
|
14875
|
+
if (withoutRuntimeGatePins(existing) === withoutRuntimeGatePins(codexHooksTables(tables))) {
|
|
14876
|
+
const pins = [...new Set(runtimeGatePins(existing))];
|
|
14877
|
+
const next = current.replace(/\btamperward@\S+ (hook|sweep) codex\b/g, `tamperward@${TW_VERSION} $1 codex`);
|
|
14878
|
+
return {
|
|
14879
|
+
item,
|
|
14880
|
+
path: rel,
|
|
14881
|
+
status: "update",
|
|
14882
|
+
detail: `re-pin the hook commands to tamperward@${TW_VERSION} (was ${pins.join(", ")})`,
|
|
14883
|
+
warning,
|
|
14884
|
+
apply: () => atomicReplaceFile(path, next, existingMode(path, 420))
|
|
14885
|
+
};
|
|
14886
|
+
}
|
|
14887
|
+
return {
|
|
14888
|
+
item,
|
|
14889
|
+
path: rel,
|
|
14890
|
+
status: "error",
|
|
14891
|
+
detail: "the [hooks] tables are not the shape init writes \u2014 remove them or add the tamperward entries by hand, then re-run init (refusing to rewrite a hand-written hooks block)"
|
|
14892
|
+
};
|
|
14893
|
+
}
|
|
14894
|
+
function planCopilotHooks(cwd) {
|
|
14895
|
+
const rel = ".github/hooks/tamperward.json";
|
|
14896
|
+
const path = join16(cwd, rel);
|
|
14897
|
+
const item = "copilot-hooks";
|
|
14898
|
+
const refused = refuseNonRegular(path);
|
|
14899
|
+
if (refused) return { item, path: rel, status: "error", detail: refused };
|
|
14900
|
+
const warning = RUNTIME_WARNING("Copilot CLI", "copilot");
|
|
14901
|
+
const document = copilotWiringDocument();
|
|
14902
|
+
const content = JSON.stringify({ $comment: COPILOT_WIRING_NOTE, ...document }, null, 2) + "\n";
|
|
14903
|
+
const write = () => {
|
|
14904
|
+
mkdirSync7(dirname5(path), { recursive: true });
|
|
14905
|
+
atomicReplaceFile(path, content, existingMode(path, 420));
|
|
14906
|
+
};
|
|
14907
|
+
if (!existsSync8(path)) {
|
|
14908
|
+
return { item, path: rel, status: "create", detail: "wire the preToolUse deny and agentStop sweep hooks (experimental)", warning, apply: write };
|
|
14909
|
+
}
|
|
14910
|
+
let parsed;
|
|
14911
|
+
try {
|
|
14912
|
+
parsed = JSON.parse(readFileSync9(path, "utf8"));
|
|
14913
|
+
} catch {
|
|
14914
|
+
return { item, path: rel, status: "error", detail: "exists but is not valid JSON \u2014 fix it, then re-run init (refusing to overwrite)" };
|
|
14915
|
+
}
|
|
14916
|
+
const hooks = isRecord(parsed) ? JSON.stringify(parsed.hooks ?? null) : "null";
|
|
14917
|
+
const wanted = JSON.stringify(document.hooks);
|
|
14918
|
+
if (hooks === wanted) return { item, path: rel, status: "ok", detail: "preToolUse + agentStop hooks already wired" };
|
|
14919
|
+
if (withoutRuntimeGatePins(hooks) === withoutRuntimeGatePins(wanted)) {
|
|
14920
|
+
const pins = [...new Set(runtimeGatePins(hooks))];
|
|
14921
|
+
return { item, path: rel, status: "update", detail: `re-pin the hook commands to tamperward@${TW_VERSION} (was ${pins.join(", ")})`, warning, apply: write };
|
|
14922
|
+
}
|
|
14923
|
+
return {
|
|
14924
|
+
item,
|
|
14925
|
+
path: rel,
|
|
14926
|
+
status: "error",
|
|
14927
|
+
detail: "the hooks are not the shape init writes \u2014 remove the file or restore the tamperward entries by hand, then re-run init (refusing to rewrite a hand-written hooks file)"
|
|
14928
|
+
};
|
|
14929
|
+
}
|
|
14601
14930
|
function planInit(cwd, opts = {}) {
|
|
14602
14931
|
requireShippedVersion();
|
|
14603
14932
|
const gitignore = planGitignore(cwd);
|
|
@@ -14605,11 +14934,24 @@ function planInit(cwd, opts = {}) {
|
|
|
14605
14934
|
planned("policy", POLICY_FILE, () => planPolicy(cwd)),
|
|
14606
14935
|
...gitignore ? [gitignore] : [],
|
|
14607
14936
|
planned("agent", ".claude/settings.json", () => planClaudeHooks(cwd)),
|
|
14937
|
+
...runtimeWiringPlans(cwd),
|
|
14608
14938
|
planned("pre-commit", "(hooks)", () => planPreCommit(cwd)),
|
|
14609
14939
|
planned("ci", ".github/workflows/tamperward.yml", () => planWorkflow(cwd, opts.forceWorkflow ?? false)),
|
|
14610
14940
|
planned("codeowners", ".github/CODEOWNERS", () => planCodeowners(cwd))
|
|
14611
14941
|
];
|
|
14612
14942
|
}
|
|
14943
|
+
function runtimeWiringPlans(cwd) {
|
|
14944
|
+
let detected;
|
|
14945
|
+
try {
|
|
14946
|
+
detected = new Set(detectRuntimes(cwd).map((r) => r.id));
|
|
14947
|
+
} catch {
|
|
14948
|
+
detected = /* @__PURE__ */ new Set();
|
|
14949
|
+
}
|
|
14950
|
+
return [
|
|
14951
|
+
...detected.has("codex") ? [planned("codex-hooks", ".codex/config.toml", () => planCodexHooks(cwd))] : [],
|
|
14952
|
+
...detected.has("copilot") ? [planned("copilot-hooks", ".github/hooks/tamperward.json", () => planCopilotHooks(cwd))] : []
|
|
14953
|
+
];
|
|
14954
|
+
}
|
|
14613
14955
|
function runInit(opts) {
|
|
14614
14956
|
const requested = resolve6(opts.cwd ?? process.cwd());
|
|
14615
14957
|
const cwd = repoRoot(requested);
|
|
@@ -14664,7 +15006,7 @@ tamperward init: ${changed} change(s) applied. Commit them so the gate travels w
|
|
|
14664
15006
|
);
|
|
14665
15007
|
return 0;
|
|
14666
15008
|
}
|
|
14667
|
-
var errText3, isPlainObject, HOOK_RE, SWEEP_RE, PRECOMMIT_RE, LEGACY_OURS, PRE_GLOBAL_OURS, CODEOWNERS_MARK, CODEOWNERS_PATHS, POLICY_CONTENT, WORKFLOW_CONTENT, WORKFLOW_MARK, WORKFLOW_MARK_RE, MANAGED_HOOKS;
|
|
15009
|
+
var errText3, isPlainObject, HOOK_RE, SWEEP_RE, PRECOMMIT_RE, LEGACY_OURS, PRE_GLOBAL_OURS, CODEOWNERS_MARK, CODEOWNERS_PATHS, POLICY_CONTENT, WORKFLOW_CONTENT, WORKFLOW_MARK, WORKFLOW_MARK_RE, MANAGED_HOOKS, RUNTIME_WARNING;
|
|
14668
15010
|
var init_init = __esm({
|
|
14669
15011
|
"src/cli/init.ts"() {
|
|
14670
15012
|
"use strict";
|
|
@@ -14672,6 +15014,8 @@ var init_init = __esm({
|
|
|
14672
15014
|
init_policy_load();
|
|
14673
15015
|
init_verifier_limits();
|
|
14674
15016
|
init_wiring();
|
|
15017
|
+
init_hook_wiring();
|
|
15018
|
+
init_runtimes();
|
|
14675
15019
|
init_narrow();
|
|
14676
15020
|
init_hook_tampering();
|
|
14677
15021
|
init_repo_context();
|
|
@@ -14857,6 +15201,7 @@ jobs:
|
|
|
14857
15201
|
always_run: true`
|
|
14858
15202
|
}
|
|
14859
15203
|
];
|
|
15204
|
+
RUNTIME_WARNING = (label, id) => `EXPERIMENTAL: the ${label} hook wiring was written because this repository shows ${label} markers, but the ${label} adapter has not passed a pinned live qualification. \`tamperward runtime verify --runtime ${id}\` grades what is proven; until it reports in-loop steering, the agent-neutral layers (pre-commit + CI) are the live protection.`;
|
|
14860
15205
|
}
|
|
14861
15206
|
});
|
|
14862
15207
|
|
|
@@ -14897,20 +15242,20 @@ var init_fingerprint = __esm({
|
|
|
14897
15242
|
// src/dependency-env.ts
|
|
14898
15243
|
import { createHash as createHash10 } from "node:crypto";
|
|
14899
15244
|
import {
|
|
14900
|
-
existsSync as
|
|
15245
|
+
existsSync as existsSync9,
|
|
14901
15246
|
lstatSync as lstatSync9,
|
|
14902
|
-
readdirSync as
|
|
14903
|
-
readFileSync as
|
|
15247
|
+
readdirSync as readdirSync4,
|
|
15248
|
+
readFileSync as readFileSync10,
|
|
14904
15249
|
readlinkSync as readlinkSync2,
|
|
14905
15250
|
realpathSync as realpathSync6,
|
|
14906
|
-
statSync as
|
|
15251
|
+
statSync as statSync4
|
|
14907
15252
|
} from "node:fs";
|
|
14908
15253
|
import {
|
|
14909
15254
|
basename as basename4,
|
|
14910
15255
|
delimiter,
|
|
14911
15256
|
dirname as dirname6,
|
|
14912
15257
|
isAbsolute as isAbsolute10,
|
|
14913
|
-
join as
|
|
15258
|
+
join as join17,
|
|
14914
15259
|
relative as relative5,
|
|
14915
15260
|
resolve as resolve7,
|
|
14916
15261
|
sep as sep3
|
|
@@ -14945,7 +15290,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14945
15290
|
root.kind + " root moved from " + JSON.stringify(root.realPath) + " to " + JSON.stringify(currentReal)
|
|
14946
15291
|
);
|
|
14947
15292
|
}
|
|
14948
|
-
if (!
|
|
15293
|
+
if (!statSync4(root.path).isDirectory()) {
|
|
14949
15294
|
throw new Unattestable(root.kind + " root " + JSON.stringify(root.path) + " is not a directory");
|
|
14950
15295
|
}
|
|
14951
15296
|
h.update("root\0" + root.kind + "\0" + root.path + "\0" + root.realPath + "\0" + String(rootStat.mode) + "\0");
|
|
@@ -14955,10 +15300,10 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14955
15300
|
const hashFile = (path, label) => {
|
|
14956
15301
|
let st;
|
|
14957
15302
|
try {
|
|
14958
|
-
st =
|
|
15303
|
+
st = statSync4(path);
|
|
14959
15304
|
if (!st.isFile()) throw new Error("not a regular file");
|
|
14960
15305
|
h.update(label + "\0external-file\0" + path + "\0" + String(st.mode) + "\0");
|
|
14961
|
-
h.update(
|
|
15306
|
+
h.update(readFileSync10(path));
|
|
14962
15307
|
h.update("\0");
|
|
14963
15308
|
} catch (e) {
|
|
14964
15309
|
throw new Unattestable(
|
|
@@ -14972,14 +15317,14 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14972
15317
|
visited.add(realDir);
|
|
14973
15318
|
let entries;
|
|
14974
15319
|
try {
|
|
14975
|
-
entries =
|
|
15320
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
14976
15321
|
} catch (e) {
|
|
14977
15322
|
throw new Unattestable(
|
|
14978
15323
|
"dependency directory " + JSON.stringify(dir) + " cannot be read (" + (e instanceof Error ? e.message : String(e)) + ")"
|
|
14979
15324
|
);
|
|
14980
15325
|
}
|
|
14981
15326
|
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
14982
|
-
const path =
|
|
15327
|
+
const path = join17(dir, entry.name);
|
|
14983
15328
|
const rel = namespace ? namespace + "/" + entry.name : entry.name;
|
|
14984
15329
|
let ident;
|
|
14985
15330
|
try {
|
|
@@ -14992,7 +15337,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14992
15337
|
h.update(rel + "\0" + ident.kind + "\0" + String(ident.mode) + "\0");
|
|
14993
15338
|
if (ident.kind === "file") {
|
|
14994
15339
|
try {
|
|
14995
|
-
h.update(
|
|
15340
|
+
h.update(readFileSync10(path));
|
|
14996
15341
|
h.update("\0");
|
|
14997
15342
|
} catch (e) {
|
|
14998
15343
|
throw new Unattestable(
|
|
@@ -15021,7 +15366,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
15021
15366
|
} catch {
|
|
15022
15367
|
continue;
|
|
15023
15368
|
}
|
|
15024
|
-
const followed =
|
|
15369
|
+
const followed = statSync4(path);
|
|
15025
15370
|
if (followed.isFile()) {
|
|
15026
15371
|
hashFile(resolved, "@target:" + rel);
|
|
15027
15372
|
continue;
|
|
@@ -15051,7 +15396,7 @@ function snapshot(roots, probes, cwd) {
|
|
|
15051
15396
|
const h = createHash10("sha256");
|
|
15052
15397
|
for (const probe of probes) {
|
|
15053
15398
|
h.update(
|
|
15054
|
-
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (
|
|
15399
|
+
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (existsSync9(probe.path) ? "present" : "absent") + "\0"
|
|
15055
15400
|
);
|
|
15056
15401
|
}
|
|
15057
15402
|
for (const root of roots) fingerprintRoot(root, cwd, h);
|
|
@@ -15069,7 +15414,7 @@ function measuredSnapshot(roots, probes, cwd, metrics) {
|
|
|
15069
15414
|
}
|
|
15070
15415
|
function freezeRoot(kind, path) {
|
|
15071
15416
|
try {
|
|
15072
|
-
if (!
|
|
15417
|
+
if (!statSync4(path).isDirectory()) {
|
|
15073
15418
|
throw new Error("not a directory");
|
|
15074
15419
|
}
|
|
15075
15420
|
return { kind, path: resolve7(path), realPath: realpathSync6(path) };
|
|
@@ -15110,7 +15455,7 @@ function resolveExecutable(token, cwd, env) {
|
|
|
15110
15455
|
if (token.includes("/") || token.includes("\\")) {
|
|
15111
15456
|
const path = isAbsolute10(token) ? token : resolve7(cwd, token);
|
|
15112
15457
|
try {
|
|
15113
|
-
return
|
|
15458
|
+
return existsSync9(path) ? realpathSync6(path) : null;
|
|
15114
15459
|
} catch {
|
|
15115
15460
|
return null;
|
|
15116
15461
|
}
|
|
@@ -15118,7 +15463,7 @@ function resolveExecutable(token, cwd, env) {
|
|
|
15118
15463
|
for (const entry of (env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
15119
15464
|
const candidate = resolve7(entry, token);
|
|
15120
15465
|
try {
|
|
15121
|
-
if (
|
|
15466
|
+
if (existsSync9(candidate) && statSync4(candidate).isFile()) return realpathSync6(candidate);
|
|
15122
15467
|
} catch {
|
|
15123
15468
|
}
|
|
15124
15469
|
}
|
|
@@ -15141,7 +15486,7 @@ function inferredVenvFromToken(token, cwd) {
|
|
|
15141
15486
|
const parentName = basename4(parent).toLowerCase();
|
|
15142
15487
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
15143
15488
|
const root = dirname6(parent);
|
|
15144
|
-
return
|
|
15489
|
+
return existsSync9(join17(root, "pyvenv.cfg")) ? root : null;
|
|
15145
15490
|
}
|
|
15146
15491
|
function unsupportedEcosystem(command, token) {
|
|
15147
15492
|
const name = token ? basename4(token).toLowerCase() : "";
|
|
@@ -15157,13 +15502,13 @@ function inferredVenv(executable) {
|
|
|
15157
15502
|
const parentName = basename4(parent).toLowerCase();
|
|
15158
15503
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
15159
15504
|
const root = dirname6(parent);
|
|
15160
|
-
return
|
|
15505
|
+
return existsSync9(join17(root, "pyvenv.cfg")) ? root : null;
|
|
15161
15506
|
}
|
|
15162
15507
|
function venvProblem(root) {
|
|
15163
|
-
const cfg =
|
|
15164
|
-
if (!
|
|
15508
|
+
const cfg = join17(root, "pyvenv.cfg");
|
|
15509
|
+
if (!existsSync9(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
|
|
15165
15510
|
try {
|
|
15166
|
-
const body =
|
|
15511
|
+
const body = readFileSync10(cfg, "utf8");
|
|
15167
15512
|
if (/^\s*include-system-site-packages\s*=\s*true\s*$/im.test(body)) {
|
|
15168
15513
|
return "Python environment " + JSON.stringify(root) + " enables system site-packages; those external package roots are not bounded";
|
|
15169
15514
|
}
|
|
@@ -15187,8 +15532,8 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
15187
15532
|
}
|
|
15188
15533
|
};
|
|
15189
15534
|
try {
|
|
15190
|
-
const nodeModules =
|
|
15191
|
-
if (
|
|
15535
|
+
const nodeModules = join17(cwd, "node_modules");
|
|
15536
|
+
if (existsSync9(nodeModules)) addRoot("node_modules", nodeModules);
|
|
15192
15537
|
else probes.push({ kind: "node_modules", path: nodeModules });
|
|
15193
15538
|
const token = firstExecutable(command);
|
|
15194
15539
|
const executable = resolveExecutable(token, cwd, env);
|
|
@@ -15200,8 +15545,8 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
15200
15545
|
venv = resolve7(env.VIRTUAL_ENV);
|
|
15201
15546
|
} else {
|
|
15202
15547
|
venv = inferredVenv(executable) ?? inferredVenvFromToken(token, cwd);
|
|
15203
|
-
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) &&
|
|
15204
|
-
venv =
|
|
15548
|
+
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) && existsSync9(join17(cwd, ".venv", "pyvenv.cfg"))) {
|
|
15549
|
+
venv = join17(cwd, ".venv");
|
|
15205
15550
|
}
|
|
15206
15551
|
}
|
|
15207
15552
|
if (venv) {
|
|
@@ -15307,7 +15652,7 @@ var init_dependency_env = __esm({
|
|
|
15307
15652
|
import { spawnSync } from "node:child_process";
|
|
15308
15653
|
import { mkdtempSync as mkdtempSync2, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
15309
15654
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
15310
|
-
import { join as
|
|
15655
|
+
import { join as join18 } from "node:path";
|
|
15311
15656
|
function emptySuiteDiagnostics() {
|
|
15312
15657
|
return {
|
|
15313
15658
|
stdout: { ...EMPTY_STREAM },
|
|
@@ -15387,8 +15732,8 @@ function parseCapturedSupervisorResult(stdoutText) {
|
|
|
15387
15732
|
};
|
|
15388
15733
|
}
|
|
15389
15734
|
function runCapturedProcessSync(executable, args, opts) {
|
|
15390
|
-
const stateDir2 = mkdtempSync2(
|
|
15391
|
-
const configFile =
|
|
15735
|
+
const stateDir2 = mkdtempSync2(join18(tmpdir3(), "tw-suite-capture-"));
|
|
15736
|
+
const configFile = join18(stateDir2, "config.json");
|
|
15392
15737
|
const backstop = opts.backstopMs ?? 3e4;
|
|
15393
15738
|
try {
|
|
15394
15739
|
writeFileSync7(
|
|
@@ -15679,8 +16024,8 @@ if (child) {
|
|
|
15679
16024
|
// src/verifier-backend.ts
|
|
15680
16025
|
import { createHash as createHash11, randomUUID } from "node:crypto";
|
|
15681
16026
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
15682
|
-
import { accessSync as accessSync2, constants as constants4, readFileSync as
|
|
15683
|
-
import { delimiter as delimiter2, dirname as dirname7, join as
|
|
16027
|
+
import { accessSync as accessSync2, constants as constants4, readFileSync as readFileSync11, realpathSync as realpathSync7, statSync as statSync5 } from "node:fs";
|
|
16028
|
+
import { delimiter as delimiter2, dirname as dirname7, join as join19, resolve as resolve8 } from "node:path";
|
|
15684
16029
|
function identity() {
|
|
15685
16030
|
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
15686
16031
|
const groups = typeof process.getgroups === "function" ? process.getgroups() : [];
|
|
@@ -15688,7 +16033,7 @@ function identity() {
|
|
|
15688
16033
|
}
|
|
15689
16034
|
function writableByCaller(path) {
|
|
15690
16035
|
try {
|
|
15691
|
-
const st =
|
|
16036
|
+
const st = statSync5(path);
|
|
15692
16037
|
const { uid, groups } = identity();
|
|
15693
16038
|
if (uid === 0) return true;
|
|
15694
16039
|
if ((st.mode & 2) !== 0) return true;
|
|
@@ -15714,7 +16059,7 @@ function replaceableByCaller(path) {
|
|
|
15714
16059
|
function findExecutable(name) {
|
|
15715
16060
|
for (const raw of (process.env.PATH ?? "").split(delimiter2)) {
|
|
15716
16061
|
if (!raw) continue;
|
|
15717
|
-
const candidate =
|
|
16062
|
+
const candidate = join19(raw, name);
|
|
15718
16063
|
try {
|
|
15719
16064
|
accessSync2(candidate, constants4.X_OK);
|
|
15720
16065
|
return realpathSync7(candidate);
|
|
@@ -15724,7 +16069,7 @@ function findExecutable(name) {
|
|
|
15724
16069
|
return null;
|
|
15725
16070
|
}
|
|
15726
16071
|
function fileSha256(path) {
|
|
15727
|
-
return createHash11("sha256").update(
|
|
16072
|
+
return createHash11("sha256").update(readFileSync11(path)).digest("hex");
|
|
15728
16073
|
}
|
|
15729
16074
|
function engineClientEnv() {
|
|
15730
16075
|
return {
|
|
@@ -16158,8 +16503,8 @@ var init_verifier_backend = __esm({
|
|
|
16158
16503
|
// src/verification-state.ts
|
|
16159
16504
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
16160
16505
|
import { createHash as createHash12 } from "node:crypto";
|
|
16161
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
16162
|
-
import { dirname as dirname8, join as
|
|
16506
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, rmSync as rmSync7 } from "node:fs";
|
|
16507
|
+
import { dirname as dirname8, join as join20 } from "node:path";
|
|
16163
16508
|
function git2(args, cwd) {
|
|
16164
16509
|
return execFileSync8("git", args, {
|
|
16165
16510
|
cwd,
|
|
@@ -16209,19 +16554,19 @@ function surfacePaths(base, cwd, policy) {
|
|
|
16209
16554
|
function interventionWiring(cwd) {
|
|
16210
16555
|
const dir = claudeConfigDir();
|
|
16211
16556
|
const sources = [
|
|
16212
|
-
["repo",
|
|
16213
|
-
["repo.local",
|
|
16214
|
-
["user",
|
|
16215
|
-
["user.local",
|
|
16557
|
+
["repo", join20(cwd, ".claude", "settings.json")],
|
|
16558
|
+
["repo.local", join20(cwd, ".claude", "settings.local.json")],
|
|
16559
|
+
["user", join20(dir, "settings.json")],
|
|
16560
|
+
["user.local", join20(dir, "settings.local.json")]
|
|
16216
16561
|
];
|
|
16217
16562
|
return sources.map(([source, path]) => {
|
|
16218
16563
|
let parsed;
|
|
16219
16564
|
try {
|
|
16220
|
-
parsed = JSON.parse(
|
|
16565
|
+
parsed = JSON.parse(readFileSync12(path, "utf8"));
|
|
16221
16566
|
} catch {
|
|
16222
16567
|
return { source, present: false };
|
|
16223
16568
|
}
|
|
16224
|
-
if (!
|
|
16569
|
+
if (!isRecord5(parsed)) return { source, present: true, shape: "non-object" };
|
|
16225
16570
|
return {
|
|
16226
16571
|
source,
|
|
16227
16572
|
present: true,
|
|
@@ -16279,11 +16624,11 @@ function stateDir(cwd) {
|
|
|
16279
16624
|
}
|
|
16280
16625
|
function verificationRecordPath(cwd) {
|
|
16281
16626
|
const dir = stateDir(cwd);
|
|
16282
|
-
return dir ?
|
|
16627
|
+
return dir ? join20(dir, "verification-state.json") : null;
|
|
16283
16628
|
}
|
|
16284
16629
|
function verifyingMarkerPath(cwd) {
|
|
16285
16630
|
const dir = stateDir(cwd);
|
|
16286
|
-
return dir ?
|
|
16631
|
+
return dir ? join20(dir, "verifying.json") : null;
|
|
16287
16632
|
}
|
|
16288
16633
|
function recordVerification(cwd, inputs, treeFingerprintValue) {
|
|
16289
16634
|
const path = verificationRecordPath(cwd);
|
|
@@ -16330,24 +16675,24 @@ function invalidateVerificationRecordIfCurrent(cwd) {
|
|
|
16330
16675
|
return false;
|
|
16331
16676
|
}
|
|
16332
16677
|
}
|
|
16333
|
-
function
|
|
16678
|
+
function isRecord5(v) {
|
|
16334
16679
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
16335
16680
|
}
|
|
16336
16681
|
function isBinding(v) {
|
|
16337
|
-
if (!
|
|
16682
|
+
if (!isRecord5(v)) return false;
|
|
16338
16683
|
return BINDING_INPUTS.every((k) => {
|
|
16339
16684
|
const val = v[k];
|
|
16340
16685
|
return typeof val === "string" && val.length > 0;
|
|
16341
16686
|
});
|
|
16342
16687
|
}
|
|
16343
16688
|
function parseVerificationRecord(value) {
|
|
16344
|
-
if (!
|
|
16689
|
+
if (!isRecord5(value)) return null;
|
|
16345
16690
|
if (value.schema_version !== VERIFICATION_STATE_SCHEMA_VERSION) return null;
|
|
16346
16691
|
if (value.verdict !== "VERIFIED") return null;
|
|
16347
16692
|
if (typeof value.verified_at !== "string") return null;
|
|
16348
16693
|
if (typeof value.tw_version !== "string") return null;
|
|
16349
16694
|
const inputs = value.inputs;
|
|
16350
|
-
if (!
|
|
16695
|
+
if (!isRecord5(inputs)) return null;
|
|
16351
16696
|
if (typeof inputs.base_ref !== "string" || inputs.base_ref.length === 0) return null;
|
|
16352
16697
|
if (typeof inputs.explicit_base !== "boolean") return null;
|
|
16353
16698
|
if (inputs.command_source !== "policy" && inputs.command_source !== "flag") return null;
|
|
@@ -16389,7 +16734,7 @@ function pidAlive3(pid) {
|
|
|
16389
16734
|
process.kill(pid, 0);
|
|
16390
16735
|
return true;
|
|
16391
16736
|
} catch (e) {
|
|
16392
|
-
return
|
|
16737
|
+
return isRecord5(e) && e.code === "EPERM";
|
|
16393
16738
|
}
|
|
16394
16739
|
}
|
|
16395
16740
|
function beginVerifying(cwd) {
|
|
@@ -16427,7 +16772,7 @@ function readVerifyingMarker(cwd) {
|
|
|
16427
16772
|
} catch {
|
|
16428
16773
|
return null;
|
|
16429
16774
|
}
|
|
16430
|
-
if (!
|
|
16775
|
+
if (!isRecord5(parsed)) return null;
|
|
16431
16776
|
if (parsed.schema_version !== VERIFICATION_STATE_SCHEMA_VERSION) return null;
|
|
16432
16777
|
if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
|
|
16433
16778
|
if (typeof parsed.started_at !== "string") return null;
|
|
@@ -16572,8 +16917,8 @@ var init_verification_state = __esm({
|
|
|
16572
16917
|
// src/verification-receipt.ts
|
|
16573
16918
|
import { createHash as createHash13 } from "node:crypto";
|
|
16574
16919
|
import { mkdirSync as mkdirSync9, rmSync as rmSync8 } from "node:fs";
|
|
16575
|
-
import { dirname as dirname9, join as
|
|
16576
|
-
function
|
|
16920
|
+
import { dirname as dirname9, join as join21 } from "node:path";
|
|
16921
|
+
function isRecord6(v) {
|
|
16577
16922
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
16578
16923
|
}
|
|
16579
16924
|
function canonicalJson2(value) {
|
|
@@ -16611,7 +16956,7 @@ function receiptFromRecord(record) {
|
|
|
16611
16956
|
}
|
|
16612
16957
|
function receiptPath(cwd) {
|
|
16613
16958
|
const ctx = repoContext(cwd);
|
|
16614
|
-
return ctx ?
|
|
16959
|
+
return ctx ? join21(stateDirectory(ctx.gitDir), "verification-receipt.json") : null;
|
|
16615
16960
|
}
|
|
16616
16961
|
function storeReceipt(cwd, receipt) {
|
|
16617
16962
|
try {
|
|
@@ -16636,10 +16981,10 @@ function isOneOf(list3, v) {
|
|
|
16636
16981
|
return typeof v === "string" && list3.includes(v);
|
|
16637
16982
|
}
|
|
16638
16983
|
function isStages(v) {
|
|
16639
|
-
return
|
|
16984
|
+
return isRecord6(v) && isOneOf(RECEIPT_STAGE_RESULTS, v.candidate) && isOneOf(RECEIPT_STAGE_RESULTS, v.pristine) && isOneOf(RECEIPT_INTEGRITY_RESULTS, v.integrity);
|
|
16640
16985
|
}
|
|
16641
16986
|
function isBinding2(v) {
|
|
16642
|
-
if (!
|
|
16987
|
+
if (!isRecord6(v)) return false;
|
|
16643
16988
|
return BINDING_INPUTS.every((k) => {
|
|
16644
16989
|
const val = v[k];
|
|
16645
16990
|
return typeof val === "string" && val.length > 0;
|
|
@@ -16649,7 +16994,7 @@ function classifyReceipt(value) {
|
|
|
16649
16994
|
if (value === null || value === void 0) {
|
|
16650
16995
|
return { disposition: "ABSENT", detail: "no local verification receipt was provided" };
|
|
16651
16996
|
}
|
|
16652
|
-
if (!
|
|
16997
|
+
if (!isRecord6(value)) {
|
|
16653
16998
|
return { disposition: "UNKNOWN_SCHEMA", detail: "receipt is not a JSON object" };
|
|
16654
16999
|
}
|
|
16655
17000
|
if (value.schema_version !== RECEIPT_SCHEMA_VERSION) {
|
|
@@ -16803,9 +17148,9 @@ var init_verification_receipt = __esm({
|
|
|
16803
17148
|
// src/cli/verify.ts
|
|
16804
17149
|
import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
|
|
16805
17150
|
import { createHash as createHash14 } from "node:crypto";
|
|
16806
|
-
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as
|
|
17151
|
+
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync13, readlinkSync as readlinkSync3, rmSync as rmSync9, symlinkSync, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "node:fs";
|
|
16807
17152
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
16808
|
-
import { dirname as dirname10, isAbsolute as isAbsolute11, join as
|
|
17153
|
+
import { dirname as dirname10, isAbsolute as isAbsolute11, join as join22, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
|
|
16809
17154
|
function oracleAssuranceReport() {
|
|
16810
17155
|
return {
|
|
16811
17156
|
level: "suite-exit-only",
|
|
@@ -16894,7 +17239,7 @@ function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
|
|
|
16894
17239
|
continue;
|
|
16895
17240
|
}
|
|
16896
17241
|
const base = domain === "deps" && dependencyRoot ? dependencyRoot : root;
|
|
16897
|
-
const current =
|
|
17242
|
+
const current = join22(base, ...stack);
|
|
16898
17243
|
let st;
|
|
16899
17244
|
try {
|
|
16900
17245
|
st = lstatSync10(current);
|
|
@@ -16936,7 +17281,7 @@ function rejectLinkedParent(cwd, rel) {
|
|
|
16936
17281
|
const parts = rel.split("/").slice(0, -1);
|
|
16937
17282
|
let current = cwd;
|
|
16938
17283
|
for (const part of parts) {
|
|
16939
|
-
current =
|
|
17284
|
+
current = join22(current, part);
|
|
16940
17285
|
let st;
|
|
16941
17286
|
try {
|
|
16942
17287
|
st = lstatSync10(current);
|
|
@@ -16961,7 +17306,7 @@ function materialize(cwd, dest, dependencyRoot) {
|
|
|
16961
17306
|
}
|
|
16962
17307
|
const links = [];
|
|
16963
17308
|
for (const rel of listed) {
|
|
16964
|
-
const src =
|
|
17309
|
+
const src = join22(cwd, rel);
|
|
16965
17310
|
rejectLinkedParent(cwd, rel);
|
|
16966
17311
|
let st;
|
|
16967
17312
|
try {
|
|
@@ -16969,7 +17314,7 @@ function materialize(cwd, dest, dependencyRoot) {
|
|
|
16969
17314
|
} catch {
|
|
16970
17315
|
continue;
|
|
16971
17316
|
}
|
|
16972
|
-
const out3 =
|
|
17317
|
+
const out3 = join22(dest, rel);
|
|
16973
17318
|
mkdirSync10(dirname10(out3), { recursive: true });
|
|
16974
17319
|
dropSymlink(out3);
|
|
16975
17320
|
if (st.isSymbolicLink()) {
|
|
@@ -16989,17 +17334,17 @@ function materialize(cwd, dest, dependencyRoot) {
|
|
|
16989
17334
|
for (const link of links) {
|
|
16990
17335
|
validateSymlinkGraph(link.target, link.out, dest, dependencyRoot, link.rel);
|
|
16991
17336
|
}
|
|
16992
|
-
if (dependencyRoot) symlinkSync(dependencyRoot,
|
|
17337
|
+
if (dependencyRoot) symlinkSync(dependencyRoot, join22(dest, "node_modules"), "dir");
|
|
16993
17338
|
}
|
|
16994
17339
|
function workingTreePaths(cwd) {
|
|
16995
17340
|
return git3(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], cwd).split("\0").filter(Boolean);
|
|
16996
17341
|
}
|
|
16997
17342
|
function probeFilesystemCaseSensitivity(dir) {
|
|
16998
|
-
const upper =
|
|
16999
|
-
const lower =
|
|
17343
|
+
const upper = join22(dir, ".tw-case-probe-A");
|
|
17344
|
+
const lower = join22(dir, ".tw-case-probe-a");
|
|
17000
17345
|
try {
|
|
17001
17346
|
writeFileSync8(upper, "", { flag: "wx" });
|
|
17002
|
-
return !
|
|
17347
|
+
return !existsSync10(lower);
|
|
17003
17348
|
} finally {
|
|
17004
17349
|
rmSync9(upper, { force: true });
|
|
17005
17350
|
}
|
|
@@ -17059,11 +17404,11 @@ function overlayDigest(dest, paths) {
|
|
|
17059
17404
|
for (const rel of [...paths].sort()) {
|
|
17060
17405
|
h.update(rel);
|
|
17061
17406
|
h.update("\0");
|
|
17062
|
-
const p =
|
|
17407
|
+
const p = join22(dest, rel);
|
|
17063
17408
|
try {
|
|
17064
17409
|
const st = lstatSync10(p);
|
|
17065
17410
|
h.update(String(st.mode));
|
|
17066
|
-
h.update(st.isSymbolicLink() ? readlinkSync3(p) :
|
|
17411
|
+
h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync13(p));
|
|
17067
17412
|
} catch {
|
|
17068
17413
|
h.update("<gone>");
|
|
17069
17414
|
}
|
|
@@ -17093,7 +17438,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
|
|
|
17093
17438
|
throw new Error(`the base's ${e.path} is a submodule; the pristine copy cannot reproduce it`);
|
|
17094
17439
|
}
|
|
17095
17440
|
const content = execFileSync9("git", ["show", `${base}:${e.path}`], { cwd, maxBuffer: 1 << 28, env: trustedGitEnv() });
|
|
17096
|
-
const out3 =
|
|
17441
|
+
const out3 = join22(dest, e.path);
|
|
17097
17442
|
mkdirSync10(dirname10(out3), { recursive: true });
|
|
17098
17443
|
rmSync9(out3, { force: true });
|
|
17099
17444
|
if (e.mode === "120000") {
|
|
@@ -17110,7 +17455,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
|
|
|
17110
17455
|
const inCopy = workingTreePaths(cwd);
|
|
17111
17456
|
for (const rel of inCopy) {
|
|
17112
17457
|
if (!isOverlay(rel) || baseProtected.has(fold(rel))) continue;
|
|
17113
|
-
rmSync9(
|
|
17458
|
+
rmSync9(join22(dest, rel), { force: true });
|
|
17114
17459
|
removed++;
|
|
17115
17460
|
}
|
|
17116
17461
|
for (const link of restoredLinks) {
|
|
@@ -17124,8 +17469,8 @@ function suiteEnv(scratch) {
|
|
|
17124
17469
|
if (DROPPED_ENV.has(k) || PINNED_NPM.test(k)) continue;
|
|
17125
17470
|
env[k] = v;
|
|
17126
17471
|
}
|
|
17127
|
-
const userRc =
|
|
17128
|
-
const globalRc =
|
|
17472
|
+
const userRc = join22(scratch, "npmrc-user");
|
|
17473
|
+
const globalRc = join22(scratch, "npmrc-global");
|
|
17129
17474
|
for (const f of [userRc, globalRc]) writeFileSync8(f, "", { mode: 292 });
|
|
17130
17475
|
env.npm_config_userconfig = userRc;
|
|
17131
17476
|
env.npm_config_globalconfig = globalRc;
|
|
@@ -17157,7 +17502,7 @@ function runLocalSuite(dir, cmd, budgetSecs) {
|
|
|
17157
17502
|
reason: `checkpointed-local verifier is unsupported on ${process.platform}; no trusted local shell contract is defined`
|
|
17158
17503
|
};
|
|
17159
17504
|
}
|
|
17160
|
-
const scratch = mkdtempSync3(
|
|
17505
|
+
const scratch = mkdtempSync3(join22(tmpdir4(), "tw-verify-run-"));
|
|
17161
17506
|
try {
|
|
17162
17507
|
const r = runCapturedProcessSync(shell.executable, shell.args, {
|
|
17163
17508
|
cwd: dir,
|
|
@@ -17403,8 +17748,8 @@ function runVerifyImpl(opts) {
|
|
|
17403
17748
|
} catch {
|
|
17404
17749
|
return cannotVerify("BASE_UNRESOLVABLE", `cannot resolve base rev "${opts.base ?? "HEAD"}"`);
|
|
17405
17750
|
}
|
|
17406
|
-
const visRoot = mkdtempSync3(
|
|
17407
|
-
const visDir =
|
|
17751
|
+
const visRoot = mkdtempSync3(join22(tmpdir4(), "tw-verify-vis-"));
|
|
17752
|
+
const visDir = join22(visRoot, "t");
|
|
17408
17753
|
const cleanup = (dirs) => {
|
|
17409
17754
|
if (opts.keep) return;
|
|
17410
17755
|
for (const d of dirs) rmSync9(d, { recursive: true, force: true });
|
|
@@ -17413,7 +17758,7 @@ function runVerifyImpl(opts) {
|
|
|
17413
17758
|
if (!filesystemCaseSensitive) {
|
|
17414
17759
|
const present = (rel) => {
|
|
17415
17760
|
try {
|
|
17416
|
-
lstatSync10(
|
|
17761
|
+
lstatSync10(join22(cwd, rel));
|
|
17417
17762
|
return true;
|
|
17418
17763
|
} catch {
|
|
17419
17764
|
return false;
|
|
@@ -17546,8 +17891,8 @@ function runVerifyImpl(opts) {
|
|
|
17546
17891
|
{ stage: "visible", dependency_environment: dependencyReport(), verifier_backend: backendReport(), oracle_assurance: oracleAssuranceReport() }
|
|
17547
17892
|
);
|
|
17548
17893
|
}
|
|
17549
|
-
const priRoot = mkdtempSync3(
|
|
17550
|
-
const priDir =
|
|
17894
|
+
const priRoot = mkdtempSync3(join22(tmpdir4(), "tw-verify-pri-"));
|
|
17895
|
+
const priDir = join22(priRoot, "t");
|
|
17551
17896
|
let restored = [];
|
|
17552
17897
|
let removedAdded = 0;
|
|
17553
17898
|
try {
|
|
@@ -17877,9 +18222,9 @@ var init_verify = __esm({
|
|
|
17877
18222
|
// src/cli/run.ts
|
|
17878
18223
|
import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync4 } from "node:child_process";
|
|
17879
18224
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
17880
|
-
import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as
|
|
18225
|
+
import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync5, readFileSync as readFileSync14, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
17881
18226
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
17882
|
-
import { dirname as dirname11, join as
|
|
18227
|
+
import { dirname as dirname11, join as join23, resolve as resolve10 } from "node:path";
|
|
17883
18228
|
function authoritativeRunLifecyclePlatform(platform = process.platform) {
|
|
17884
18229
|
return platform === "linux";
|
|
17885
18230
|
}
|
|
@@ -17933,7 +18278,7 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
|
|
|
17933
18278
|
for (const candidate of candidates) {
|
|
17934
18279
|
try {
|
|
17935
18280
|
const real = realpathSync8(candidate);
|
|
17936
|
-
const st =
|
|
18281
|
+
const st = statSync6(real);
|
|
17937
18282
|
if (!st.isFile() || writableByCaller2(real)) continue;
|
|
17938
18283
|
const probe = spawnSync4(
|
|
17939
18284
|
real,
|
|
@@ -17955,9 +18300,9 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
|
|
|
17955
18300
|
};
|
|
17956
18301
|
}
|
|
17957
18302
|
function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecycleTestMode, machineMode = false) {
|
|
17958
|
-
const stateDir2 = mkdtempSync4(
|
|
17959
|
-
const resultFile =
|
|
17960
|
-
const agentEnvFile =
|
|
18303
|
+
const stateDir2 = mkdtempSync4(join23(tmpdir5(), "tw-agent-supervisor-"));
|
|
18304
|
+
const resultFile = join23(stateDir2, "result.json");
|
|
18305
|
+
const agentEnvFile = join23(stateDir2, "agent-env.json");
|
|
17961
18306
|
try {
|
|
17962
18307
|
const linux = process.platform === "linux";
|
|
17963
18308
|
let executable;
|
|
@@ -18031,7 +18376,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
|
|
|
18031
18376
|
}
|
|
18032
18377
|
let state = {};
|
|
18033
18378
|
try {
|
|
18034
|
-
state = JSON.parse(
|
|
18379
|
+
state = JSON.parse(readFileSync14(resultFile, "utf8"));
|
|
18035
18380
|
} catch {
|
|
18036
18381
|
return {
|
|
18037
18382
|
exit: 1,
|
|
@@ -18075,7 +18420,7 @@ function observerExited(pid) {
|
|
|
18075
18420
|
if (!pidAlive4(pid)) return true;
|
|
18076
18421
|
if (process.platform !== "linux") return false;
|
|
18077
18422
|
try {
|
|
18078
|
-
const stat =
|
|
18423
|
+
const stat = readFileSync14(`/proc/${pid}/stat`, "utf8");
|
|
18079
18424
|
const state = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[0];
|
|
18080
18425
|
return state === "Z" || state === "X";
|
|
18081
18426
|
} catch (e) {
|
|
@@ -18083,8 +18428,8 @@ function observerExited(pid) {
|
|
|
18083
18428
|
}
|
|
18084
18429
|
}
|
|
18085
18430
|
function supervisedObserverLog(cwd) {
|
|
18086
|
-
const gd = gitDir(cwd) ??
|
|
18087
|
-
return
|
|
18431
|
+
const gd = gitDir(cwd) ?? join23(cwd, ".git");
|
|
18432
|
+
return join23(
|
|
18088
18433
|
stateDirectory(gd),
|
|
18089
18434
|
`run-observer-${process.pid}-${Date.now()}-${randomBytes3(4).toString("hex")}.jsonl`
|
|
18090
18435
|
);
|
|
@@ -18196,7 +18541,7 @@ function git4(args, cwd) {
|
|
|
18196
18541
|
}
|
|
18197
18542
|
function nowTicks() {
|
|
18198
18543
|
try {
|
|
18199
|
-
return Math.floor(parseFloat(
|
|
18544
|
+
return Math.floor(parseFloat(readFileSync14("/proc/uptime", "utf8").split(" ")[0]) * 100);
|
|
18200
18545
|
} catch {
|
|
18201
18546
|
return Number.POSITIVE_INFINITY;
|
|
18202
18547
|
}
|
|
@@ -18211,7 +18556,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
18211
18556
|
}
|
|
18212
18557
|
let pids;
|
|
18213
18558
|
try {
|
|
18214
|
-
pids =
|
|
18559
|
+
pids = readdirSync5("/proc").filter((d) => /^\d+$/.test(d));
|
|
18215
18560
|
} catch {
|
|
18216
18561
|
return out3;
|
|
18217
18562
|
}
|
|
@@ -18219,7 +18564,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
18219
18564
|
const n2 = Number(pid);
|
|
18220
18565
|
if (n2 === process.pid) continue;
|
|
18221
18566
|
try {
|
|
18222
|
-
const stat =
|
|
18567
|
+
const stat = readFileSync14(`/proc/${pid}/stat`, "utf8");
|
|
18223
18568
|
const startTicks = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]);
|
|
18224
18569
|
if (!Number.isFinite(startTicks) || startTicks < spawnedAfterTicks) continue;
|
|
18225
18570
|
} catch {
|
|
@@ -18227,7 +18572,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
18227
18572
|
}
|
|
18228
18573
|
const procLinks = [`/proc/${pid}/cwd`, `/proc/${pid}/exe`];
|
|
18229
18574
|
try {
|
|
18230
|
-
for (const fd of
|
|
18575
|
+
for (const fd of readdirSync5(`/proc/${pid}/fd`)) procLinks.push(`/proc/${pid}/fd/${fd}`);
|
|
18231
18576
|
} catch {
|
|
18232
18577
|
}
|
|
18233
18578
|
let holds = false;
|
|
@@ -18792,8 +19137,8 @@ raise SystemExit(0)
|
|
|
18792
19137
|
|
|
18793
19138
|
// src/cli/doctor.ts
|
|
18794
19139
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
18795
|
-
import { existsSync as
|
|
18796
|
-
import { join as
|
|
19140
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15, readdirSync as readdirSync6 } from "node:fs";
|
|
19141
|
+
import { join as join24, resolve as resolve11 } from "node:path";
|
|
18797
19142
|
function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
|
|
18798
19143
|
if (platform === "linux") {
|
|
18799
19144
|
if (linuxPython?.path) {
|
|
@@ -18848,10 +19193,10 @@ function wiringState(status, authority) {
|
|
|
18848
19193
|
return authority ? "BROKEN" : "WARN";
|
|
18849
19194
|
}
|
|
18850
19195
|
function pinsInFile(path) {
|
|
18851
|
-
if (!
|
|
19196
|
+
if (!existsSync11(path)) return [];
|
|
18852
19197
|
let src = "";
|
|
18853
19198
|
try {
|
|
18854
|
-
src =
|
|
19199
|
+
src = readFileSync15(path, "utf8");
|
|
18855
19200
|
} catch {
|
|
18856
19201
|
return [];
|
|
18857
19202
|
}
|
|
@@ -18872,13 +19217,13 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
|
|
|
18872
19217
|
const warnings = [];
|
|
18873
19218
|
for (const rel of workflowRels) {
|
|
18874
19219
|
const path = resolve11(cwd, rel);
|
|
18875
|
-
if (!
|
|
19220
|
+
if (!existsSync11(path)) {
|
|
18876
19221
|
broken.push(`${rel} is missing`);
|
|
18877
19222
|
continue;
|
|
18878
19223
|
}
|
|
18879
19224
|
let doc;
|
|
18880
19225
|
try {
|
|
18881
|
-
doc = yaml.parse(
|
|
19226
|
+
doc = yaml.parse(readFileSync15(path, "utf8"));
|
|
18882
19227
|
} catch (e) {
|
|
18883
19228
|
broken.push(`${rel} is not valid YAML (${e instanceof Error ? e.message : String(e)})`);
|
|
18884
19229
|
continue;
|
|
@@ -18991,9 +19336,9 @@ function collectLocalPosture(cwd, policy, authorityWorkflows, explicitWorkflow =
|
|
|
18991
19336
|
addWiring("codeowners", "codeowners", true);
|
|
18992
19337
|
checks.push(workflowPermissionCheck(cwd, authorityWorkflows));
|
|
18993
19338
|
const pins = [
|
|
18994
|
-
...pinsInFile(
|
|
18995
|
-
...pinsInFile(
|
|
18996
|
-
...pinsInFile(
|
|
19339
|
+
...pinsInFile(join24(cwd, ".claude", "settings.json")),
|
|
19340
|
+
...pinsInFile(join24(cwd, ".git", "hooks", "pre-commit")),
|
|
19341
|
+
...pinsInFile(join24(cwd, ".husky", "pre-commit"))
|
|
18997
19342
|
];
|
|
18998
19343
|
const below = pins.filter((pin) => (compareVersions(pin, TW_VERSION) ?? 0) < 0);
|
|
18999
19344
|
const above = pins.filter((pin) => (compareVersions(TW_VERSION, pin) ?? 0) < 0);
|
|
@@ -19277,19 +19622,19 @@ function diagnose(opts = {}) {
|
|
|
19277
19622
|
} else {
|
|
19278
19623
|
const workflowDirRel = ".github/workflows";
|
|
19279
19624
|
const workflowDir = resolve11(cwd, workflowDirRel);
|
|
19280
|
-
if (!
|
|
19625
|
+
if (!existsSync11(workflowDir)) {
|
|
19281
19626
|
return fail("ci-verifier", `${workflowDirRel}: workflow directory does not exist`);
|
|
19282
19627
|
}
|
|
19283
19628
|
let entries;
|
|
19284
19629
|
try {
|
|
19285
|
-
entries =
|
|
19630
|
+
entries = readdirSync6(workflowDir).filter((name) => /\.ya?ml$/i.test(name)).sort();
|
|
19286
19631
|
} catch (e) {
|
|
19287
19632
|
return fail(
|
|
19288
19633
|
"ci-verifier",
|
|
19289
19634
|
`${workflowDirRel}: could not enumerate workflows (${e instanceof Error ? e.message : String(e)})`
|
|
19290
19635
|
);
|
|
19291
19636
|
}
|
|
19292
|
-
for (const name of entries) workflowRels.push(
|
|
19637
|
+
for (const name of entries) workflowRels.push(join24(workflowDirRel, name));
|
|
19293
19638
|
}
|
|
19294
19639
|
const requiredSecs = requiredVerifierAuthoritySeconds(policy.verify.budget);
|
|
19295
19640
|
const requiredMinutes = Math.ceil(requiredSecs / 60);
|
|
@@ -19297,12 +19642,12 @@ function diagnose(opts = {}) {
|
|
|
19297
19642
|
const authorityWorkflows = [];
|
|
19298
19643
|
for (const workflowRel of workflowRels) {
|
|
19299
19644
|
const workflowPath = resolve11(cwd, workflowRel);
|
|
19300
|
-
if (!
|
|
19645
|
+
if (!existsSync11(workflowPath)) {
|
|
19301
19646
|
return fail("ci-verifier", `${workflowRel}: workflow does not exist`);
|
|
19302
19647
|
}
|
|
19303
19648
|
let doc;
|
|
19304
19649
|
try {
|
|
19305
|
-
doc = yaml.parse(
|
|
19650
|
+
doc = yaml.parse(readFileSync15(workflowPath, "utf8"));
|
|
19306
19651
|
} catch (e) {
|
|
19307
19652
|
return fail(
|
|
19308
19653
|
"ci-verifier",
|
|
@@ -19616,16 +19961,16 @@ var init_status2 = __esm({
|
|
|
19616
19961
|
// src/cli/trace-verify.ts
|
|
19617
19962
|
import { execFileSync as execFileSync12, spawnSync as spawnSync5 } from "node:child_process";
|
|
19618
19963
|
import {
|
|
19619
|
-
existsSync as
|
|
19964
|
+
existsSync as existsSync12,
|
|
19620
19965
|
mkdtempSync as mkdtempSync5,
|
|
19621
|
-
readFileSync as
|
|
19622
|
-
readdirSync as
|
|
19966
|
+
readFileSync as readFileSync16,
|
|
19967
|
+
readdirSync as readdirSync7,
|
|
19623
19968
|
realpathSync as realpathSync9,
|
|
19624
19969
|
rmSync as rmSync11,
|
|
19625
19970
|
symlinkSync as symlinkSync2
|
|
19626
19971
|
} from "node:fs";
|
|
19627
19972
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
19628
|
-
import { isAbsolute as isAbsolute12, join as
|
|
19973
|
+
import { isAbsolute as isAbsolute12, join as join25, relative as relative7, resolve as resolve12, sep as sep5 } from "node:path";
|
|
19629
19974
|
function unescapeStraceString(s) {
|
|
19630
19975
|
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, "\\");
|
|
19631
19976
|
}
|
|
@@ -19873,16 +20218,16 @@ function materializeBase(base, cwd, dest) {
|
|
|
19873
20218
|
`tar extraction failed${untar.stderr?.length ? `: ${String(untar.stderr).trim()}` : ""}`
|
|
19874
20219
|
);
|
|
19875
20220
|
}
|
|
19876
|
-
const hostModules =
|
|
19877
|
-
const tracedModules =
|
|
19878
|
-
if (
|
|
20221
|
+
const hostModules = join25(cwd, "node_modules");
|
|
20222
|
+
const tracedModules = join25(dest, "node_modules");
|
|
20223
|
+
if (existsSync12(hostModules) && !existsSync12(tracedModules)) {
|
|
19879
20224
|
symlinkSync2(resolve12(hostModules), tracedModules, "dir");
|
|
19880
20225
|
}
|
|
19881
20226
|
}
|
|
19882
20227
|
function traceFiles(prefix) {
|
|
19883
20228
|
const dir = resolve12(prefix, "..");
|
|
19884
20229
|
const base = prefix.split(sep5).at(-1) ?? prefix;
|
|
19885
|
-
return
|
|
20230
|
+
return readdirSync7(dir).filter((name) => name === base || name.startsWith(base + ".")).map((name) => join25(dir, name)).sort();
|
|
19886
20231
|
}
|
|
19887
20232
|
function rewriteTraceRoot(accesses, actualRoot) {
|
|
19888
20233
|
return accesses.map((item) => {
|
|
@@ -19897,13 +20242,13 @@ function rewriteTraceRoot(accesses, actualRoot) {
|
|
|
19897
20242
|
if (!inside3(actualRoot, real)) return { ...item, path: real };
|
|
19898
20243
|
} catch {
|
|
19899
20244
|
}
|
|
19900
|
-
return { ...item, path:
|
|
20245
|
+
return { ...item, path: join25(TRACE_ROOT, ...rel.split("/")) };
|
|
19901
20246
|
});
|
|
19902
20247
|
}
|
|
19903
20248
|
function straceOnce(root, command, budget) {
|
|
19904
|
-
const traceDir = mkdtempSync5(
|
|
20249
|
+
const traceDir = mkdtempSync5(join25(tmpdir6(), "tw-trace-log-"));
|
|
19905
20250
|
try {
|
|
19906
|
-
const prefix =
|
|
20251
|
+
const prefix = join25(traceDir, "trace");
|
|
19907
20252
|
const traced = spawnSync5(
|
|
19908
20253
|
"strace",
|
|
19909
20254
|
[
|
|
@@ -19932,7 +20277,7 @@ function straceOnce(root, command, budget) {
|
|
|
19932
20277
|
}
|
|
19933
20278
|
);
|
|
19934
20279
|
const logs = traced.error ? [] : traceFiles(prefix);
|
|
19935
|
-
const accesses = logs.flatMap((path) => parseStraceFileAccess(
|
|
20280
|
+
const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync16(path, "utf8"), TRACE_ROOT));
|
|
19936
20281
|
const raw = {
|
|
19937
20282
|
spawnError: traced.error ? traced.error.message : null,
|
|
19938
20283
|
status: traced.status,
|
|
@@ -19947,7 +20292,7 @@ function straceOnce(root, command, budget) {
|
|
|
19947
20292
|
}
|
|
19948
20293
|
}
|
|
19949
20294
|
function runOneTrace(cwd, base, command, budget) {
|
|
19950
|
-
const root = mkdtempSync5(
|
|
20295
|
+
const root = mkdtempSync5(join25(tmpdir6(), "tw-trace-base-"));
|
|
19951
20296
|
try {
|
|
19952
20297
|
materializeBase(base, cwd, root);
|
|
19953
20298
|
const { raw, accesses } = straceOnce(root, command, budget);
|
|
@@ -19957,7 +20302,7 @@ function runOneTrace(cwd, base, command, budget) {
|
|
|
19957
20302
|
}
|
|
19958
20303
|
}
|
|
19959
20304
|
function tracerPreflight(budget = 10) {
|
|
19960
|
-
const probeRoot = mkdtempSync5(
|
|
20305
|
+
const probeRoot = mkdtempSync5(join25(tmpdir6(), "tw-trace-probe-"));
|
|
19961
20306
|
try {
|
|
19962
20307
|
const { raw } = straceOnce(probeRoot, "true", Math.max(1, Math.min(budget, 10)));
|
|
19963
20308
|
return classifyTraceRun(raw);
|
|
@@ -20163,163 +20508,6 @@ var init_trace_verify = __esm({
|
|
|
20163
20508
|
}
|
|
20164
20509
|
});
|
|
20165
20510
|
|
|
20166
|
-
// src/immutable.ts
|
|
20167
|
-
function isPlainData(value) {
|
|
20168
|
-
if (value === null || typeof value !== "object") return false;
|
|
20169
|
-
const proto = Object.getPrototypeOf(value);
|
|
20170
|
-
return proto === Object.prototype || proto === Array.prototype || proto === null;
|
|
20171
|
-
}
|
|
20172
|
-
function deepFreeze(value) {
|
|
20173
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
20174
|
-
const walk2 = (node) => {
|
|
20175
|
-
if (!isPlainData(node) || seen.has(node)) return;
|
|
20176
|
-
seen.add(node);
|
|
20177
|
-
Object.freeze(node);
|
|
20178
|
-
for (const key2 of Reflect.ownKeys(node)) walk2(Reflect.get(node, key2));
|
|
20179
|
-
};
|
|
20180
|
-
walk2(value);
|
|
20181
|
-
return value;
|
|
20182
|
-
}
|
|
20183
|
-
var init_immutable = __esm({
|
|
20184
|
-
"src/immutable.ts"() {
|
|
20185
|
-
"use strict";
|
|
20186
|
-
}
|
|
20187
|
-
});
|
|
20188
|
-
|
|
20189
|
-
// src/runtimes.ts
|
|
20190
|
-
import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
|
|
20191
|
-
import { join as join25 } from "node:path";
|
|
20192
|
-
function isRecord6(x) {
|
|
20193
|
-
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
20194
|
-
}
|
|
20195
|
-
function hooksBeyondTamperward(hooks) {
|
|
20196
|
-
if (!isRecord6(hooks)) return hooks != null;
|
|
20197
|
-
for (const entries of Object.values(hooks)) {
|
|
20198
|
-
if (!Array.isArray(entries)) {
|
|
20199
|
-
if (entries != null) return true;
|
|
20200
|
-
continue;
|
|
20201
|
-
}
|
|
20202
|
-
for (const entry of entries) {
|
|
20203
|
-
const hs = isRecord6(entry) ? entry.hooks : null;
|
|
20204
|
-
if (!Array.isArray(hs)) {
|
|
20205
|
-
if (hs != null) return true;
|
|
20206
|
-
continue;
|
|
20207
|
-
}
|
|
20208
|
-
for (const h of hs) {
|
|
20209
|
-
const cmd = isRecord6(h) ? String(h.command ?? "") : "";
|
|
20210
|
-
if (!/tamperward/.test(cmd)) return true;
|
|
20211
|
-
}
|
|
20212
|
-
}
|
|
20213
|
-
}
|
|
20214
|
-
return false;
|
|
20215
|
-
}
|
|
20216
|
-
function claudeSettingsBeyondTamperward(cwd) {
|
|
20217
|
-
const path = join25(cwd, ".claude", "settings.json");
|
|
20218
|
-
let raw;
|
|
20219
|
-
try {
|
|
20220
|
-
if (!existsSync12(path)) return false;
|
|
20221
|
-
raw = readFileSync16(path, "utf8");
|
|
20222
|
-
} catch {
|
|
20223
|
-
return false;
|
|
20224
|
-
}
|
|
20225
|
-
let parsed;
|
|
20226
|
-
try {
|
|
20227
|
-
parsed = JSON.parse(raw);
|
|
20228
|
-
} catch {
|
|
20229
|
-
return raw.trim().length > 0;
|
|
20230
|
-
}
|
|
20231
|
-
if (!isRecord6(parsed)) return raw.trim().length > 0;
|
|
20232
|
-
for (const [key2, value] of Object.entries(parsed)) {
|
|
20233
|
-
if (key2 === "disableAllHooks") continue;
|
|
20234
|
-
if (key2 === "hooks") {
|
|
20235
|
-
if (hooksBeyondTamperward(value)) return true;
|
|
20236
|
-
continue;
|
|
20237
|
-
}
|
|
20238
|
-
return true;
|
|
20239
|
-
}
|
|
20240
|
-
return false;
|
|
20241
|
-
}
|
|
20242
|
-
function detectRuntimes(cwd) {
|
|
20243
|
-
const found = [];
|
|
20244
|
-
for (const rt of KNOWN_RUNTIMES) {
|
|
20245
|
-
const marker = rt.markers.find((m) => {
|
|
20246
|
-
try {
|
|
20247
|
-
return existsSync12(join25(cwd, m));
|
|
20248
|
-
} catch {
|
|
20249
|
-
return false;
|
|
20250
|
-
}
|
|
20251
|
-
});
|
|
20252
|
-
const matchedMarker = marker ?? rt.detectExtra?.(cwd) ?? void 0;
|
|
20253
|
-
if (matchedMarker !== void 0) found.push({ ...rt, matchedMarker });
|
|
20254
|
-
}
|
|
20255
|
-
return found;
|
|
20256
|
-
}
|
|
20257
|
-
function hasInLoopRuntime(detected) {
|
|
20258
|
-
return detected.some((r) => r.steering === "in-loop");
|
|
20259
|
-
}
|
|
20260
|
-
function detectionHeadline(detected) {
|
|
20261
|
-
if (detected.length === 0) {
|
|
20262
|
-
return "No agent runtime detected. Wiring Claude Code in-loop steering plus the agent-neutral layers (pre-commit + CI) that protect any agent or human.";
|
|
20263
|
-
}
|
|
20264
|
-
if (detected.length === 1) {
|
|
20265
|
-
const r = detected[0];
|
|
20266
|
-
return `Detected ${r.label} \u2014 ${r.note}`;
|
|
20267
|
-
}
|
|
20268
|
-
const parts = detected.map((r) => `${r.label} (${r.steering === "in-loop" ? "in-loop" : "neutral layers"})`);
|
|
20269
|
-
return `Detected ${detected.length} agent runtimes: ${parts.join(", ")}.`;
|
|
20270
|
-
}
|
|
20271
|
-
function neutralOnlyCaveat(detected) {
|
|
20272
|
-
const neutral = detected.filter((r) => r.steering === "neutral");
|
|
20273
|
-
if (neutral.length === 0) return null;
|
|
20274
|
-
const names = neutral.map((r) => r.label).join(", ");
|
|
20275
|
-
const claudeAlso = hasInLoopRuntime(detected);
|
|
20276
|
-
const lead = claudeAlso ? `In-loop steering (deny-before-execute) ships for Claude Code only today: Claude Code here gets it, but on ${names} your live protection is ` : `In-loop steering (deny-before-execute) ships for Claude Code only today, so on ${names} your live protection is `;
|
|
20277
|
-
const tail = claudeAlso ? "" : " The Claude hook wiring is still written and is inert unless Claude Code runs in this repository.";
|
|
20278
|
-
return `${lead}the agent-neutral layers: pre-commit and CI.${tail}`;
|
|
20279
|
-
}
|
|
20280
|
-
var KNOWN_RUNTIMES;
|
|
20281
|
-
var init_runtimes = __esm({
|
|
20282
|
-
"src/runtimes.ts"() {
|
|
20283
|
-
"use strict";
|
|
20284
|
-
init_immutable();
|
|
20285
|
-
KNOWN_RUNTIMES = deepFreeze([
|
|
20286
|
-
{
|
|
20287
|
-
id: "claude-code",
|
|
20288
|
-
// Markers are Claude Code files `init` never writes, so detection never mistakes
|
|
20289
|
-
// TamperWard's own generated `.claude/settings.json` for evidence a human runs
|
|
20290
|
-
// Claude here (#526). `detectExtra` looks at that generated file's CONTENT for
|
|
20291
|
-
// real use (configuration beyond the two hooks init merges in).
|
|
20292
|
-
label: "Claude Code",
|
|
20293
|
-
markers: ["CLAUDE.md", ".claude/settings.local.json", ".claude/commands", ".claude/agents"],
|
|
20294
|
-
detectExtra: (cwd) => claudeSettingsBeyondTamperward(cwd) ? ".claude/settings.json" : null,
|
|
20295
|
-
steering: "in-loop",
|
|
20296
|
-
note: "native in-loop steering (PreToolUse deny + Stop sweep) plus the agent-neutral layers."
|
|
20297
|
-
},
|
|
20298
|
-
{
|
|
20299
|
-
id: "cursor",
|
|
20300
|
-
label: "Cursor",
|
|
20301
|
-
markers: [".cursor", ".cursorrules"],
|
|
20302
|
-
steering: "neutral",
|
|
20303
|
-
note: "agent-neutral layers (pre-commit + CI) today; no in-loop adapter ships for Cursor."
|
|
20304
|
-
},
|
|
20305
|
-
{
|
|
20306
|
-
id: "copilot",
|
|
20307
|
-
label: "GitHub Copilot",
|
|
20308
|
-
markers: [".github/copilot-instructions.md", ".github/copilot"],
|
|
20309
|
-
steering: "neutral",
|
|
20310
|
-
note: "agent-neutral layers (pre-commit + CI) today; the experimental Copilot adapters are not qualified for in-loop steering (see `tamperward runtime verify`)."
|
|
20311
|
-
},
|
|
20312
|
-
{
|
|
20313
|
-
id: "codex",
|
|
20314
|
-
label: "an AGENTS.md-aware agent (e.g. Codex)",
|
|
20315
|
-
markers: [".codex", "AGENTS.md"],
|
|
20316
|
-
steering: "neutral",
|
|
20317
|
-
note: "agent-neutral layers (pre-commit + CI) today; the experimental Codex adapter is not qualified for in-loop steering (see `tamperward runtime verify`)."
|
|
20318
|
-
}
|
|
20319
|
-
]);
|
|
20320
|
-
}
|
|
20321
|
-
});
|
|
20322
|
-
|
|
20323
20511
|
// src/adapters/contract.ts
|
|
20324
20512
|
function immutableRuntimeCapabilities(caps) {
|
|
20325
20513
|
return Object.freeze({
|
|
@@ -23231,6 +23419,62 @@ var init_onboard = __esm({
|
|
|
23231
23419
|
}
|
|
23232
23420
|
});
|
|
23233
23421
|
|
|
23422
|
+
// src/cli/runtime-hook.ts
|
|
23423
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
23424
|
+
function isRuntimeHookAgent(agent) {
|
|
23425
|
+
return RUNTIME_HOOK_AGENTS.some((known) => known === agent);
|
|
23426
|
+
}
|
|
23427
|
+
function runRuntimeHookFromRaw(kind, agent, raw, cwd = process.cwd()) {
|
|
23428
|
+
if (!isRuntimeHookAgent(agent)) {
|
|
23429
|
+
return { exitCode: 2, stdout: "", stderr: `tamperward: unsupported ${kind === "Stop" ? "sweep" : "hook"} agent "${agent}" (claude, ${RUNTIME_HOOK_AGENTS.join(", ")})
|
|
23430
|
+
` };
|
|
23431
|
+
}
|
|
23432
|
+
const adapter = adapterFor(agent);
|
|
23433
|
+
if (!adapter) {
|
|
23434
|
+
return { exitCode: 2, stdout: "", stderr: `tamperward: no adapter is registered for "${agent}"
|
|
23435
|
+
` };
|
|
23436
|
+
}
|
|
23437
|
+
const phase = kind === "Stop" ? "end-of-turn" : "pre-action";
|
|
23438
|
+
try {
|
|
23439
|
+
const r = adapter.decide(raw, phase, cwd);
|
|
23440
|
+
return { exitCode: 0, stdout: r.wire ?? "", stderr: "" };
|
|
23441
|
+
} catch (e) {
|
|
23442
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
23443
|
+
const wire = adapter.denyPayload([steeringUnavailableFinding(detail)], phase);
|
|
23444
|
+
return { exitCode: 0, stdout: wire, stderr: "" };
|
|
23445
|
+
}
|
|
23446
|
+
}
|
|
23447
|
+
function runRuntimeHook(kind, agent) {
|
|
23448
|
+
let raw;
|
|
23449
|
+
try {
|
|
23450
|
+
raw = readFileSync20(0, "utf8");
|
|
23451
|
+
} catch (e) {
|
|
23452
|
+
raw = "";
|
|
23453
|
+
const r2 = runRuntimeHookFromRaw(kind, agent, "", process.cwd());
|
|
23454
|
+
if (r2.exitCode !== 0) {
|
|
23455
|
+
process.stderr.write(r2.stderr);
|
|
23456
|
+
return r2.exitCode;
|
|
23457
|
+
}
|
|
23458
|
+
const adapter = adapterFor(agent);
|
|
23459
|
+
const detail = `cannot read the hook payload on stdin: ${e instanceof Error ? e.message : String(e)}`;
|
|
23460
|
+
process.stdout.write(adapter ? adapter.denyPayload([steeringUnavailableFinding(detail)], kind === "Stop" ? "end-of-turn" : "pre-action") : "");
|
|
23461
|
+
return 0;
|
|
23462
|
+
}
|
|
23463
|
+
const r = runRuntimeHookFromRaw(kind, agent, raw, process.cwd());
|
|
23464
|
+
if (r.stdout) process.stdout.write(r.stdout);
|
|
23465
|
+
if (r.stderr) process.stderr.write(r.stderr);
|
|
23466
|
+
return r.exitCode;
|
|
23467
|
+
}
|
|
23468
|
+
var RUNTIME_HOOK_AGENTS;
|
|
23469
|
+
var init_runtime_hook = __esm({
|
|
23470
|
+
"src/cli/runtime-hook.ts"() {
|
|
23471
|
+
"use strict";
|
|
23472
|
+
init_registry();
|
|
23473
|
+
init_contract();
|
|
23474
|
+
RUNTIME_HOOK_AGENTS = Object.freeze(["codex", "copilot"]);
|
|
23475
|
+
}
|
|
23476
|
+
});
|
|
23477
|
+
|
|
23234
23478
|
// src/research/adapter.ts
|
|
23235
23479
|
import { isAbsolute as isAbsolute17, resolve as resolve17 } from "node:path";
|
|
23236
23480
|
function taskEnv(task) {
|
|
@@ -23372,7 +23616,7 @@ var init_capture = __esm({
|
|
|
23372
23616
|
|
|
23373
23617
|
// src/research/manifest.ts
|
|
23374
23618
|
import { createHash as createHash16 } from "node:crypto";
|
|
23375
|
-
import { readFileSync as
|
|
23619
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
23376
23620
|
import { dirname as dirname14, resolve as resolve18 } from "node:path";
|
|
23377
23621
|
function taskFrom(raw, index, manifestDir) {
|
|
23378
23622
|
const where = `task #${index + 1}`;
|
|
@@ -23406,7 +23650,7 @@ function readManifest(path) {
|
|
|
23406
23650
|
const abs = resolve18(path);
|
|
23407
23651
|
let bytes;
|
|
23408
23652
|
try {
|
|
23409
|
-
bytes =
|
|
23653
|
+
bytes = readFileSync21(abs);
|
|
23410
23654
|
} catch (e) {
|
|
23411
23655
|
throw new ResearchError(`cannot read task manifest ${abs}: ${e instanceof Error ? e.message : String(e)}`);
|
|
23412
23656
|
}
|
|
@@ -23825,7 +24069,7 @@ var init_record = __esm({
|
|
|
23825
24069
|
// src/research/run.ts
|
|
23826
24070
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
23827
24071
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
23828
|
-
import { closeSync as closeSync5, existsSync as existsSync15, linkSync, mkdirSync as mkdirSync12, openSync as openSync4, readFileSync as
|
|
24072
|
+
import { closeSync as closeSync5, existsSync as existsSync15, linkSync, mkdirSync as mkdirSync12, openSync as openSync4, readFileSync as readFileSync22, renameSync as renameSync4, rmSync as rmSync14, unlinkSync, writeFileSync as writeFileSync11, writeSync as writeSync2 } from "node:fs";
|
|
23829
24073
|
import { hostname } from "node:os";
|
|
23830
24074
|
import { join as join29, resolve as resolve19 } from "node:path";
|
|
23831
24075
|
function git7(args, cwd) {
|
|
@@ -23839,7 +24083,7 @@ function researchLockPath(ledger) {
|
|
|
23839
24083
|
}
|
|
23840
24084
|
function readLockOwner(path) {
|
|
23841
24085
|
try {
|
|
23842
|
-
const parsed = JSON.parse(
|
|
24086
|
+
const parsed = JSON.parse(readFileSync22(path, "utf8"));
|
|
23843
24087
|
if (!isRecord(parsed) || typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
|
|
23844
24088
|
if (typeof parsed.host !== "string" || typeof parsed.started_at !== "string" || typeof parsed.token !== "string") return null;
|
|
23845
24089
|
return { pid: parsed.pid, host: parsed.host, started_at: parsed.started_at, token: parsed.token };
|
|
@@ -23924,7 +24168,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
23924
24168
|
if (released) return;
|
|
23925
24169
|
released = true;
|
|
23926
24170
|
try {
|
|
23927
|
-
if (
|
|
24171
|
+
if (readFileSync22(path, "utf8") === text) unlinkSync(path);
|
|
23928
24172
|
} catch {
|
|
23929
24173
|
}
|
|
23930
24174
|
}
|
|
@@ -23933,7 +24177,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
23933
24177
|
function resumableRecord(path, expected) {
|
|
23934
24178
|
let raw;
|
|
23935
24179
|
try {
|
|
23936
|
-
raw = JSON.parse(
|
|
24180
|
+
raw = JSON.parse(readFileSync22(path, "utf8"));
|
|
23937
24181
|
} catch (e) {
|
|
23938
24182
|
throw new ResearchError(`ledger record ${path} is malformed (${errorMessage(e).split("\n")[0]}); remove it to regenerate the pair, or use a new --out`);
|
|
23939
24183
|
}
|
|
@@ -24355,7 +24599,7 @@ var init_run2 = __esm({
|
|
|
24355
24599
|
});
|
|
24356
24600
|
|
|
24357
24601
|
// src/research/ledger-files.ts
|
|
24358
|
-
import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync3, lstatSync as lstatSync12, openSync as openSync5, readFileSync as
|
|
24602
|
+
import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync3, lstatSync as lstatSync12, openSync as openSync5, readFileSync as readFileSync23, readdirSync as readdirSync8, realpathSync as realpathSync12 } from "node:fs";
|
|
24359
24603
|
import { join as join30, resolve as resolve20 } from "node:path";
|
|
24360
24604
|
function lstatOrNull(path) {
|
|
24361
24605
|
try {
|
|
@@ -24398,7 +24642,7 @@ function readRegularFile(path, realPairsDir, name) {
|
|
|
24398
24642
|
if (!opened.isFile() || !sameInode || opened.nlink !== 1) {
|
|
24399
24643
|
throw new ResearchError(`ledger record ${path} changed between listing and reading; refusing to read it`);
|
|
24400
24644
|
}
|
|
24401
|
-
return
|
|
24645
|
+
return readFileSync23(fd);
|
|
24402
24646
|
} finally {
|
|
24403
24647
|
closeSync6(fd);
|
|
24404
24648
|
}
|
|
@@ -24414,7 +24658,7 @@ function readPairFiles(ledger) {
|
|
|
24414
24658
|
const realPairsDir = realpathSync12.native(pairsDir);
|
|
24415
24659
|
let names;
|
|
24416
24660
|
try {
|
|
24417
|
-
names =
|
|
24661
|
+
names = readdirSync8(pairsDir).filter((n2) => n2.endsWith(".json")).sort();
|
|
24418
24662
|
} catch (e) {
|
|
24419
24663
|
throw new ResearchError(`cannot read ledger ${pairsDir}: ${errorMessage(e)}`);
|
|
24420
24664
|
}
|
|
@@ -24571,7 +24815,7 @@ var init_summarize = __esm({
|
|
|
24571
24815
|
|
|
24572
24816
|
// src/research/init.ts
|
|
24573
24817
|
import { createHash as createHash17 } from "node:crypto";
|
|
24574
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as
|
|
24818
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync24, writeFileSync as writeFileSync12 } from "node:fs";
|
|
24575
24819
|
import { basename as basename6, dirname as dirname15, resolve as resolve21 } from "node:path";
|
|
24576
24820
|
function idFrom(repo) {
|
|
24577
24821
|
const clean = repo.replace(/[\\/]$/, "").split(/[\\/]/).pop() || "task";
|
|
@@ -24623,7 +24867,7 @@ function runResearchInit(opts) {
|
|
|
24623
24867
|
}
|
|
24624
24868
|
}
|
|
24625
24869
|
function requireHash(path) {
|
|
24626
|
-
return createHash17("sha256").update(
|
|
24870
|
+
return createHash17("sha256").update(readFileSync24(path)).digest("hex");
|
|
24627
24871
|
}
|
|
24628
24872
|
var init_init2 = __esm({
|
|
24629
24873
|
"src/research/init.ts"() {
|
|
@@ -24691,7 +24935,7 @@ var init_report2 = __esm({
|
|
|
24691
24935
|
import { createHash as createHash18 } from "node:crypto";
|
|
24692
24936
|
import { isDeepStrictEqual } from "node:util";
|
|
24693
24937
|
import { gzipSync, gunzipSync } from "node:zlib";
|
|
24694
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
24938
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync25, writeFileSync as writeFileSync13 } from "node:fs";
|
|
24695
24939
|
import { dirname as dirname16, resolve as resolve22 } from "node:path";
|
|
24696
24940
|
function octal(value, width) {
|
|
24697
24941
|
return value.toString(8).padStart(width - 1, "0") + "\0";
|
|
@@ -24797,7 +25041,7 @@ function jsonBytes(value) {
|
|
|
24797
25041
|
function validateResearchBundle(path) {
|
|
24798
25042
|
let files;
|
|
24799
25043
|
try {
|
|
24800
|
-
files = parseTar(gunzipSync(
|
|
25044
|
+
files = parseTar(gunzipSync(readFileSync25(resolve22(path))));
|
|
24801
25045
|
} catch (e) {
|
|
24802
25046
|
throw new ResearchError(`cannot read research bundle ${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
24803
25047
|
}
|
|
@@ -24867,7 +25111,7 @@ function createResearchBundle(opts) {
|
|
|
24867
25111
|
const { records, entries } = packagedRecords(ledger);
|
|
24868
25112
|
const summary = summarizeRecords(records);
|
|
24869
25113
|
if (opts.manifest) {
|
|
24870
|
-
const manifest =
|
|
25114
|
+
const manifest = readFileSync25(resolve22(opts.manifest));
|
|
24871
25115
|
const sha3 = createHash18("sha256").update(manifest).digest("hex");
|
|
24872
25116
|
if (sha3 !== summary.manifest_sha256) throw new ResearchError(`manifest sha256 ${sha3} does not match ledger ${summary.manifest_sha256}`);
|
|
24873
25117
|
entries.push({ name: "manifest.json", bytes: manifest });
|
|
@@ -25038,7 +25282,7 @@ var init_research = __esm({
|
|
|
25038
25282
|
});
|
|
25039
25283
|
|
|
25040
25284
|
// src/cli/receipt.ts
|
|
25041
|
-
import { appendFileSync as appendFileSync3, readFileSync as
|
|
25285
|
+
import { appendFileSync as appendFileSync3, readFileSync as readFileSync26, writeFileSync as writeFileSync14 } from "node:fs";
|
|
25042
25286
|
function parseReceipt(args) {
|
|
25043
25287
|
const [sub, ...rest] = args;
|
|
25044
25288
|
const exportOpts = {};
|
|
@@ -25165,7 +25409,7 @@ function ciAdjudicate(cwd, opts) {
|
|
|
25165
25409
|
if (opts.ciResult !== void 0) {
|
|
25166
25410
|
let doc;
|
|
25167
25411
|
try {
|
|
25168
|
-
doc = JSON.parse(
|
|
25412
|
+
doc = JSON.parse(readFileSync26(opts.ciResult, "utf8"));
|
|
25169
25413
|
} catch (e) {
|
|
25170
25414
|
return {
|
|
25171
25415
|
ci: { verdict: "CANNOT_VERIFY", binding, binding_error: `unreadable CI verify result: ${e instanceof Error ? e.message : String(e)}` },
|
|
@@ -25311,7 +25555,7 @@ function loadClaimedReceipt(path) {
|
|
|
25311
25555
|
}
|
|
25312
25556
|
let raw;
|
|
25313
25557
|
try {
|
|
25314
|
-
raw =
|
|
25558
|
+
raw = readFileSync26(path, "utf8");
|
|
25315
25559
|
} catch (e) {
|
|
25316
25560
|
return { disposition: "MALFORMED", detail: `receipt file could not be read (${e instanceof Error ? e.message : String(e)})` };
|
|
25317
25561
|
}
|
|
@@ -25435,13 +25679,9 @@ function parseAllow(args) {
|
|
|
25435
25679
|
return o;
|
|
25436
25680
|
}
|
|
25437
25681
|
function runAgentCommand(kind, args) {
|
|
25438
|
-
const agent = args[0];
|
|
25439
|
-
if (agent
|
|
25440
|
-
|
|
25441
|
-
`);
|
|
25442
|
-
return 2;
|
|
25443
|
-
}
|
|
25444
|
-
return kind === "hook" ? runHookClaude() : runSweepClaude();
|
|
25682
|
+
const agent = args[0] ?? "";
|
|
25683
|
+
if (agent === "claude") return kind === "hook" ? runHookClaude() : runSweepClaude();
|
|
25684
|
+
return runRuntimeHook(kind === "hook" ? "PreToolUse" : "Stop", agent);
|
|
25445
25685
|
}
|
|
25446
25686
|
function parseInit(args) {
|
|
25447
25687
|
const o = {};
|
|
@@ -26161,6 +26401,7 @@ var init_main = __esm({
|
|
|
26161
26401
|
init_run();
|
|
26162
26402
|
init_watch();
|
|
26163
26403
|
init_onboard();
|
|
26404
|
+
init_runtime_hook();
|
|
26164
26405
|
init_research();
|
|
26165
26406
|
init_runtime();
|
|
26166
26407
|
init_receipt();
|
|
@@ -26172,7 +26413,7 @@ var init_main = __esm({
|
|
|
26172
26413
|
// src/cli/index.ts
|
|
26173
26414
|
init_hook_client();
|
|
26174
26415
|
init_exit();
|
|
26175
|
-
import { readFileSync as
|
|
26416
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
26176
26417
|
function loadMain() {
|
|
26177
26418
|
return Promise.resolve().then(() => (init_main(), main_exports));
|
|
26178
26419
|
}
|
|
@@ -26185,7 +26426,7 @@ async function launch(argv) {
|
|
|
26185
26426
|
if (kind && hookServiceEnabled()) {
|
|
26186
26427
|
let raw = null;
|
|
26187
26428
|
try {
|
|
26188
|
-
raw =
|
|
26429
|
+
raw = readFileSync27(0, "utf8");
|
|
26189
26430
|
} catch {
|
|
26190
26431
|
raw = null;
|
|
26191
26432
|
}
|