@codyswann/lisa 4.70.4 → 4.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/all/copy-overwrite/scripts/lib/automation-provenance-contract.mjs +174 -0
  2. package/all/copy-overwrite/scripts/lib/automation-provenance-local.mjs +279 -0
  3. package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +17 -7
  4. package/all/copy-overwrite/scripts/lib/github-attestation-provider.mjs +241 -0
  5. package/all/copy-overwrite/scripts/lib/github-attestation-recovery.mjs +283 -0
  6. package/all/copy-overwrite/scripts/lib/github-attestation-verifier.mjs +244 -0
  7. package/all/copy-overwrite/scripts/lib/npm-update-allocate.mjs +289 -0
  8. package/all/copy-overwrite/scripts/lib/npm-update-authorization.mjs +235 -0
  9. package/all/copy-overwrite/scripts/lib/npm-update-broker-client.mjs +92 -0
  10. package/all/copy-overwrite/scripts/lib/npm-update-checkpoint.mjs +241 -0
  11. package/all/copy-overwrite/scripts/lib/npm-update-contract.mjs +278 -0
  12. package/all/copy-overwrite/scripts/lib/npm-update-controller-broker.mjs +234 -0
  13. package/all/copy-overwrite/scripts/lib/npm-update-controller-factory.mjs +278 -0
  14. package/all/copy-overwrite/scripts/lib/npm-update-controller-recipe.mjs +135 -0
  15. package/all/copy-overwrite/scripts/lib/npm-update-execution-adapter.mjs +292 -0
  16. package/all/copy-overwrite/scripts/lib/npm-update-gate-hooks.mjs +214 -0
  17. package/all/copy-overwrite/scripts/lib/npm-update-gate-install.mjs +267 -0
  18. package/all/copy-overwrite/scripts/lib/npm-update-gate-proof.mjs +99 -0
  19. package/all/copy-overwrite/scripts/lib/npm-update-gate.mjs +300 -0
  20. package/all/copy-overwrite/scripts/lib/npm-update-gh-dispatch.mjs +182 -0
  21. package/all/copy-overwrite/scripts/lib/npm-update-gh-grants.mjs +156 -0
  22. package/all/copy-overwrite/scripts/lib/npm-update-gh-requests.mjs +221 -0
  23. package/all/copy-overwrite/scripts/lib/npm-update-github.mjs +214 -0
  24. package/all/copy-overwrite/scripts/lib/npm-update-helper-graph.mjs +238 -0
  25. package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +126 -0
  26. package/all/copy-overwrite/scripts/lib/npm-update-helper.mjs +292 -0
  27. package/all/copy-overwrite/scripts/lib/npm-update-hook-installation.mjs +116 -0
  28. package/all/copy-overwrite/scripts/lib/npm-update-hook-preload.mjs +153 -0
  29. package/all/copy-overwrite/scripts/lib/npm-update-hook-provider.mjs +207 -0
  30. package/all/copy-overwrite/scripts/lib/npm-update-hook-read-client.mjs +164 -0
  31. package/all/copy-overwrite/scripts/lib/npm-update-hosted-gate.mjs +300 -0
  32. package/all/copy-overwrite/scripts/lib/npm-update-hosted-hook.mjs +157 -0
  33. package/all/copy-overwrite/scripts/lib/npm-update-invariants.mjs +28 -0
  34. package/all/copy-overwrite/scripts/lib/npm-update-isolation.mjs +11 -0
  35. package/all/copy-overwrite/scripts/lib/npm-update-leaf-contract.mjs +171 -0
  36. package/all/copy-overwrite/scripts/lib/npm-update-leaf.mjs +194 -0
  37. package/all/copy-overwrite/scripts/lib/npm-update-native-process.mjs +155 -0
  38. package/all/copy-overwrite/scripts/lib/npm-update-npm.mjs +190 -0
  39. package/all/copy-overwrite/scripts/lib/npm-update-object.mjs +112 -0
  40. package/all/copy-overwrite/scripts/lib/npm-update-orchestrator.mjs +273 -0
  41. package/all/copy-overwrite/scripts/lib/npm-update-owner.mjs +292 -0
  42. package/all/copy-overwrite/scripts/lib/npm-update-prepare.mjs +263 -0
  43. package/all/copy-overwrite/scripts/lib/npm-update-process-core.mjs +179 -0
  44. package/all/copy-overwrite/scripts/lib/npm-update-process.mjs +16 -0
  45. package/all/copy-overwrite/scripts/lib/npm-update-publication.mjs +258 -0
  46. package/all/copy-overwrite/scripts/lib/npm-update-publish.mjs +254 -0
  47. package/all/copy-overwrite/scripts/lib/npm-update-quality.mjs +153 -0
  48. package/all/copy-overwrite/scripts/lib/npm-update-recovery.mjs +264 -0
  49. package/all/copy-overwrite/scripts/lib/npm-update-runtime-archive.mjs +204 -0
  50. package/all/copy-overwrite/scripts/lib/npm-update-runtime-graph.mjs +185 -0
  51. package/all/copy-overwrite/scripts/lib/npm-update-runtime-transport.mjs +221 -0
  52. package/all/copy-overwrite/scripts/lib/npm-update-runtime.mjs +284 -0
  53. package/all/copy-overwrite/scripts/lib/npm-update-tool-launcher.mjs +287 -0
  54. package/all/copy-overwrite/scripts/lib/npm-update-worker-environment.mjs +161 -0
  55. package/all/copy-overwrite/scripts/lib/npm-update-worker-inspection.mjs +142 -0
  56. package/all/copy-overwrite/scripts/lib/npm-update-worker-lifecycle.mjs +256 -0
  57. package/all/copy-overwrite/scripts/lib/npm-update-worker-policy.mjs +266 -0
  58. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +230 -36
  59. package/all/copy-overwrite/scripts/lisa-automation-provenance.mjs +161 -0
  60. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +89 -5
  61. package/all/copy-overwrite/scripts/lisa-npm-updater.mjs +204 -0
  62. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +48 -123
  63. package/all/copy-overwrite/scripts/lisa-work-item.mjs +107 -41
  64. package/all/copy-overwrite/scripts/npm-updater-gate-runtime.json +1 -0
  65. package/all/copy-overwrite/scripts/npm-updater-gate-supervisor.c +121 -0
  66. package/all/copy-overwrite/scripts/npm-updater-gate.Dockerfile +49 -0
  67. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +1085 -0
  68. package/dist/codex/scripts/block-no-verify.sh +89 -5
  69. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  70. package/dist/core/lisa-owned-hash-ledger.js +604 -0
  71. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  72. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  73. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  74. package/dist/core/upstream-evidence-manifest.js +159 -11
  75. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  76. package/dist/opencode/plugin-templates/block-no-verify.sh +89 -5
  77. package/package.json +4 -4
  78. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa/hooks/block-no-verify.agy.sh +89 -5
  81. package/plugins/lisa/hooks/block-no-verify.sh +89 -5
  82. package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +89 -5
  83. package/plugins/lisa-agy/plugin.json +1 -1
  84. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  87. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-copilot/hooks/block-no-verify.sh +89 -5
  91. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-cursor/hooks/block-no-verify.sh +89 -5
  93. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-expo-agy/plugin.json +1 -1
  96. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  100. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  101. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  106. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  110. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  111. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  115. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  116. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  119. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  120. package/plugins/lisa-rails-agy/plugin.json +1 -1
  121. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  125. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  126. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  127. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  130. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  131. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  133. package/plugins/src/base/hooks/block-no-verify.agy.sh +89 -5
  134. package/plugins/src/base/hooks/block-no-verify.sh +89 -5
  135. package/scripts/check-shipped-surface-removals.mjs +96 -24
  136. package/scripts/generate-npm-updater-helper-graph.mjs +184 -0
  137. package/scripts/two-channel-couplings.json +31 -7
  138. package/typescript/copy-contents/.husky/commit-msg +18 -0
  139. package/typescript/copy-overwrite/.prettierignore +2 -0
@@ -0,0 +1,155 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** Literal native process supervision and capture preserve argv/stdin/status and cancellation. */
5
+ import { spawn } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ import { required } from "./npm-update-contract.mjs";
8
+
9
+ /** The complete supervisor vector, including its control overhead, is bounded literal data. */
10
+ function supervisedArguments(vector) {
11
+ required(
12
+ vector.length <= 512 &&
13
+ vector.every(
14
+ value =>
15
+ typeof value === "string" &&
16
+ !value.includes("\0") &&
17
+ Buffer.byteLength(value) <= 65_536
18
+ ) &&
19
+ Buffer.byteLength(JSON.stringify(vector)) <= 262_144,
20
+ "invalid or unbounded supervised argument vector"
21
+ );
22
+ return vector;
23
+ }
24
+
25
+ /** Explicit direct argv preserves authenticated bootstrap arguments without a shell hop. */
26
+ function startSupervised(command, args, timeout, cwd, env) {
27
+ const supervisor = fileURLToPath(
28
+ new URL("./process-tree-runner.mjs", import.meta.url)
29
+ );
30
+ required(
31
+ typeof command === "string" && command.length > 0 && Array.isArray(args),
32
+ "literal executable and arguments required"
33
+ );
34
+ return spawn(
35
+ process.execPath,
36
+ supervisedArguments([
37
+ supervisor,
38
+ `--timeout-ms=${timeout}`,
39
+ `--watch-pid=${process.pid}`,
40
+ "--direct-argv",
41
+ "--",
42
+ command,
43
+ ...args,
44
+ ]),
45
+ { cwd, env, stdio: ["pipe", "pipe", "pipe"] }
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Execute argv data in an owned session; timeout/output overflow never returns success.
51
+ * @param {string} command Literal executable.
52
+ * @param {string[]} args Original argument vector.
53
+ * @param {{cwd?: string, env?: NodeJS.ProcessEnv, input?: string | Buffer,
54
+ * timeout?: number, allowed?: number[], maximum?: number,
55
+ * signal?: AbortSignal}} [options] Bounded execution.
56
+ * @returns {Promise<{code: number, stdout: Buffer, stderr: Buffer}>} Actual child result.
57
+ */
58
+ export function runProcess(
59
+ command,
60
+ args,
61
+ {
62
+ cwd,
63
+ env,
64
+ input,
65
+ timeout = 30_000,
66
+ allowed = [0],
67
+ maximum = 3_145_728,
68
+ signal: abortSignal,
69
+ } = {}
70
+ ) {
71
+ required(
72
+ process.platform !== "win32",
73
+ "owned process sessions require Linux or macOS"
74
+ );
75
+ required(
76
+ Number.isInteger(timeout) && timeout > 0 && timeout <= 1_800_000,
77
+ "invalid child deadline"
78
+ );
79
+ required(
80
+ abortSignal === undefined || abortSignal instanceof AbortSignal,
81
+ "invalid child cancellation signal"
82
+ );
83
+ required(!abortSignal?.aborted, "child was cancelled before execution");
84
+ return new Promise((resolve, reject) => {
85
+ const child = startSupervised(command, args, timeout, cwd, env);
86
+ captureProcess(child, {
87
+ command,
88
+ input,
89
+ timeout,
90
+ allowed,
91
+ maximum,
92
+ abortSignal,
93
+ }).then(resolve, reject);
94
+ });
95
+ }
96
+
97
+ /** Native close, not cancellation delivery, completes an owned child. */
98
+ function captureProcess(
99
+ child,
100
+ { command, input, timeout, allowed, maximum, abortSignal }
101
+ ) {
102
+ return new Promise((resolveResult, reject) => {
103
+ const chunks = [];
104
+ const errors = [];
105
+ let size = 0;
106
+ let failure;
107
+ let closed = false;
108
+ const stop = reason => {
109
+ failure ??= new Error(`npm updater: ${reason}`);
110
+ if (!closed && child.pid) child.kill("SIGTERM");
111
+ };
112
+ const abort = () => stop("child was cancelled");
113
+ abortSignal?.addEventListener("abort", abort, { once: true });
114
+ if (abortSignal?.aborted) abort();
115
+ const timer = setTimeout(() => stop("child exceeded deadline"), timeout);
116
+ const collect = target => data => {
117
+ size += data.length;
118
+ if (size > maximum) stop("child output exceeded bound");
119
+ else target.push(data);
120
+ };
121
+ child.stdout.on("data", collect(chunks));
122
+ child.stderr.on("data", collect(errors));
123
+ child.on("error", error => {
124
+ failure = error;
125
+ });
126
+ child.stdin.on("error", error => {
127
+ if (error.code !== "EPIPE") stop("child input failed");
128
+ });
129
+ child.stdin.end(input);
130
+ child.on("close", (code, signal) => {
131
+ closed = true;
132
+ clearTimeout(timer);
133
+ abortSignal?.removeEventListener("abort", abort);
134
+ if (failure || signal || !allowed.includes(code)) {
135
+ const error =
136
+ failure ?? new Error(`npm updater: child failed (${code ?? signal})`);
137
+ reject(
138
+ Object.assign(error, {
139
+ stdout: Buffer.concat(chunks),
140
+ stderr: Buffer.concat(errors),
141
+ command,
142
+ code,
143
+ nativeCompleted: !failure && !signal,
144
+ })
145
+ );
146
+ return;
147
+ }
148
+ resolveResult({
149
+ code,
150
+ stdout: Buffer.concat(chunks),
151
+ stderr: Buffer.concat(errors),
152
+ });
153
+ });
154
+ });
155
+ }
@@ -0,0 +1,190 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** Genuine immutable npm and released helper identity preserve native engine policy. */
5
+ import {
6
+ lstatSync,
7
+ existsSync,
8
+ constants,
9
+ openSync,
10
+ closeSync,
11
+ fstatSync,
12
+ readSync,
13
+ realpathSync,
14
+ } from "node:fs";
15
+ import { createHash } from "node:crypto";
16
+ import { join, dirname, isAbsolute, parse } from "node:path";
17
+ import { required } from "./npm-update-contract.mjs";
18
+ import { readBytes, runProcess } from "./npm-update-process-core.mjs";
19
+ import { sha256 } from "./github-attestation-verifier.mjs";
20
+
21
+ /** Ascend from the actual entry; a host manifest never becomes the Lisa package owner. */
22
+ export function findLisaPackageOwner(entry) {
23
+ let directory = dirname(realpathSync(entry));
24
+ const root = parse(directory).root;
25
+ while (directory !== root) {
26
+ const manifest = join(directory, "package.json");
27
+ if (existsSync(manifest)) {
28
+ const metadata = JSON.parse(readBytes(manifest, 1_048_576, false));
29
+ if (metadata.name === "@codyswann/lisa")
30
+ return { root: directory, metadata };
31
+ }
32
+ directory = dirname(directory);
33
+ }
34
+ return undefined;
35
+ }
36
+
37
+ /** Stream a bounded regular archive so its actual bytes, not metadata, prove registry integrity. */
38
+ export function archiveIntegrity(file) {
39
+ const fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW);
40
+ try {
41
+ const stat = fstatSync(fd);
42
+ required(
43
+ lstatSync(file).isFile() &&
44
+ !lstatSync(file).isSymbolicLink() &&
45
+ stat.isFile() &&
46
+ stat.nlink === 1 &&
47
+ stat.uid === process.getuid() &&
48
+ stat.size > 0 &&
49
+ stat.size <= 150_000_000,
50
+ "invalid bounded owned helper archive"
51
+ );
52
+ const hash = createHash("sha512");
53
+ const buffer = Buffer.alloc(65_536);
54
+ let total = 0;
55
+ let count;
56
+ while ((count = readSync(fd, buffer, 0, buffer.length, null)) > 0) {
57
+ total += count;
58
+ required(total <= 150_000_000, "helper archive grew beyond bound");
59
+ hash.update(buffer.subarray(0, count));
60
+ }
61
+ return `sha512-${hash.digest("base64")}`;
62
+ } finally {
63
+ closeSync(fd);
64
+ }
65
+ }
66
+
67
+ /** Public metadata is bounded during consumption and must match the prepared lock. */
68
+ export async function registryVersion(update, proposal) {
69
+ const response = await fetch(
70
+ `https://registry.npmjs.org/${encodeURIComponent(update.name)}/${update.to}`,
71
+ { signal: AbortSignal.timeout(30_000), redirect: "error" }
72
+ );
73
+ if (!response.ok || !response.body) {
74
+ await response.body?.cancel();
75
+ required(false, "selected registry version is unavailable");
76
+ }
77
+ const reader = response.body.getReader();
78
+ const chunks = [];
79
+ let length = 0;
80
+ try {
81
+ for (;;) {
82
+ const { done, value } = await reader.read();
83
+ if (done) break;
84
+ length += value.byteLength;
85
+ required(length <= 1_048_576, "registry response exceeds bound");
86
+ chunks.push(Buffer.from(value));
87
+ }
88
+ } finally {
89
+ await reader.cancel().catch(() => {});
90
+ reader.releaseLock();
91
+ }
92
+ const text = new TextDecoder("utf-8", {
93
+ fatal: true,
94
+ ignoreBOM: true,
95
+ }).decode(Buffer.concat(chunks));
96
+ const value = JSON.parse(text);
97
+ const lock = JSON.parse(proposal.files["package-lock.json"]);
98
+ const item = lock.packages?.[`node_modules/${update.name}`];
99
+ required(
100
+ value.name === update.name &&
101
+ value.version === update.to &&
102
+ item?.version === update.to &&
103
+ value.dist?.integrity === item.integrity &&
104
+ typeof item.integrity === "string" &&
105
+ value.dist.tarball === item.resolved &&
106
+ typeof item.resolved === "string",
107
+ "registry selected version, archive or integrity differs"
108
+ );
109
+ return { name: value.name, version: value.version };
110
+ }
111
+
112
+ /** Run genuine immutable npm11 with the actual required Node interpreter. */
113
+ export async function runNpm(args, options) {
114
+ const cli = process.env.LISA_NPM_CLI;
115
+ if (!cli) return runProcess("npm", args, options);
116
+ required(
117
+ process.versions.node === "22.23.3" &&
118
+ isAbsolute(cli) &&
119
+ !lstatSync(cli).isSymbolicLink(),
120
+ "qualified Node/npm CLI required"
121
+ );
122
+ const metadata = JSON.parse(
123
+ readBytes(join(dirname(cli), "../package.json"), 1_048_576, false)
124
+ );
125
+ required(
126
+ metadata.name === "npm" &&
127
+ metadata.version === "11.21.0" &&
128
+ metadata.engines?.node === "^20.17.0 || >=22.9.0" &&
129
+ sha256(readBytes(cli, 1_048_576, false)) ===
130
+ "8e5f6f3429f8cdbe693cdc29904e9d5a7b127a494bd15c804bd54c7403bfcbe7",
131
+ "immutable supported npm identity differs"
132
+ );
133
+ return runProcess(process.execPath, [cli, ...args], options);
134
+ }
135
+
136
+ /** Resolve only the effective boolean with native precedence, before isolating HOME. */
137
+ export async function effectiveEngineStrict(cwd, source = process.env) {
138
+ const env = {
139
+ PATH: source.PATH,
140
+ HOME: source.HOME,
141
+ LANG: "C.UTF-8",
142
+ GIT_TERMINAL_PROMPT: "0",
143
+ };
144
+ required(typeof env.HOME === "string", "engine policy HOME is unavailable");
145
+ const allowed = new Set([
146
+ "engine_strict",
147
+ "engine-strict",
148
+ "userconfig",
149
+ "globalconfig",
150
+ "prefix",
151
+ ]);
152
+ for (const [name, value] of Object.entries(source))
153
+ if (
154
+ allowed.has(name.toLowerCase().replace(/^npm_config_/, "")) &&
155
+ /^npm_config_/i.test(name)
156
+ )
157
+ env[name] = value;
158
+ const git = args => runProcess("git", args, { cwd, env, maximum: 1_048_576 });
159
+ const project = join(cwd, ".npmrc");
160
+ if (existsSync(project)) {
161
+ required(
162
+ lstatSync(project).isFile() && !lstatSync(project).isSymbolicLink(),
163
+ "engine policy project config is aliased"
164
+ );
165
+ const entry = (
166
+ await git(["ls-tree", "HEAD", "--", ".npmrc"])
167
+ ).stdout.toString();
168
+ required(
169
+ entry.startsWith("100644 blob ") && entry.trim().split("\n").length === 1,
170
+ "engine policy project config is not committed"
171
+ );
172
+ const changed = await git(["diff", "--name-only", "HEAD", "--", ".npmrc"]);
173
+ required(
174
+ !changed.stdout.length,
175
+ "engine policy project config differs from HEAD"
176
+ );
177
+ }
178
+ const result = await runNpm(["config", "get", "engine-strict", "--json"], {
179
+ cwd,
180
+ env,
181
+ maximum: 65_536,
182
+ });
183
+ required(!result.stderr.length, "engine policy config warning is ambiguous");
184
+ const text = result.stdout.toString().trim();
185
+ required(
186
+ ["true", "false"].includes(text),
187
+ "engine policy is unresolved or nonboolean"
188
+ );
189
+ return text === "true";
190
+ }
@@ -0,0 +1,112 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** Bounded read-only local Git validates exact protocol identities without granting publication authority. */
5
+ import { isAbsolute } from "node:path";
6
+ import { boundedSpawnSync } from "./bounded-spawn.mjs";
7
+ import { required } from "./npm-update-invariants.mjs";
8
+ import { sha256 } from "./github-attestation-verifier.mjs";
9
+
10
+ /**
11
+ * Git computes its own protocol identities without writing an object or running filters.
12
+ * @param {string} cwd Original trusted publisher checkout.
13
+ * @param {string} type Closed blob or commit object kind.
14
+ * @param {Buffer} bytes Exact already bounded raw object contents.
15
+ * @returns {string} The genuine SHA1 Git object identity.
16
+ */
17
+ export function gitObjectId(cwd, type, bytes) {
18
+ required(
19
+ typeof cwd === "string" && isAbsolute(cwd),
20
+ "trusted Git cwd required"
21
+ );
22
+ required(type === "blob" || type === "commit", "unsupported Git object kind");
23
+ required(
24
+ Buffer.isBuffer(bytes) &&
25
+ bytes.length <= (type === "commit" ? 65_536 : 1_048_576),
26
+ "bounded raw Git object required"
27
+ );
28
+ const options = {
29
+ cwd,
30
+ env: {
31
+ PATH: process.env.PATH,
32
+ HOME: "/nonexistent",
33
+ GIT_TERMINAL_PROMPT: "0",
34
+ },
35
+ encoding: "utf8",
36
+ timeout: 30_000,
37
+ maxBuffer: 128,
38
+ };
39
+ const format = boundedSpawnSync(
40
+ "git",
41
+ ["rev-parse", "--show-object-format"],
42
+ options
43
+ );
44
+ required(
45
+ format.status === 0 &&
46
+ !format.error &&
47
+ !format.signal &&
48
+ format.stderr === "" &&
49
+ format.stdout === "sha1\n",
50
+ "publisher requires genuine sha1 Git object format"
51
+ );
52
+ const result = boundedSpawnSync(
53
+ "git",
54
+ ["hash-object", "-t", type, "--stdin"],
55
+ { ...options, input: bytes }
56
+ );
57
+ required(
58
+ result.status === 0 &&
59
+ !result.error &&
60
+ !result.signal &&
61
+ result.stderr === "" &&
62
+ /^[a-f0-9]{40}\n$/.test(result.stdout),
63
+ "native Git object identity unavailable"
64
+ );
65
+ return result.stdout.slice(0, -1);
66
+ }
67
+
68
+ /** GitHub must recreate the actual raw object, including identity, time and message. */
69
+ export function validateRawCommit(bytes, descriptor, cwd) {
70
+ required(
71
+ Buffer.isBuffer(bytes) && bytes.length <= 65_536,
72
+ "bounded raw commit required"
73
+ );
74
+ const text = new TextDecoder("utf-8", {
75
+ fatal: true,
76
+ ignoreBOM: true,
77
+ }).decode(bytes);
78
+ const split = text.indexOf("\n\n");
79
+ required(split > 0, "invalid raw commit");
80
+ const fields = text.slice(0, split).split("\n");
81
+ required(
82
+ fields.length === 4 &&
83
+ fields[0] === `tree ${descriptor.tree}` &&
84
+ fields[1] === `parent ${descriptor.parent}` &&
85
+ fields[2] === `author ${descriptor.author}` &&
86
+ fields[3] === `committer ${descriptor.committer}`,
87
+ "raw commit metadata differs or signature is unsupported"
88
+ );
89
+ const message = text.slice(split + 2);
90
+ required(
91
+ sha256(Buffer.from(message)) === descriptor.messageSha256,
92
+ "raw commit message differs"
93
+ );
94
+ const sha = gitObjectId(cwd, "commit", bytes);
95
+ return {
96
+ sha,
97
+ message,
98
+ author: identity(descriptor.author),
99
+ committer: identity(descriptor.committer),
100
+ };
101
+ }
102
+
103
+ /** UTC-only metadata avoids a provider rewriting timezone-sensitive raw bytes. */
104
+ function identity(value) {
105
+ const match = /^([^\n<>]+) <([^\n<>]+)> ([1-9]\d*) \+0000$/.exec(value);
106
+ required(Boolean(match), "publisher requires unsigned UTC Git identity");
107
+ return {
108
+ name: match[1],
109
+ email: match[2],
110
+ date: new Date(Number(match[3]) * 1000).toISOString(),
111
+ };
112
+ }