@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,221 @@
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
+ /** Controller-derived literal GH requests narrow canonical parsing without replacing it. */
5
+ import { canonicalJson } from "../lisa-automation-provenance.mjs";
6
+ import { required, keys } from "./npm-update-contract.mjs";
7
+ import {
8
+ githubIssueViewArgs,
9
+ githubHierarchyArgs,
10
+ pullRequestViewArgs,
11
+ githubBacklinkListArgs,
12
+ } from "../lisa-work-item.mjs";
13
+ import {
14
+ officialArguments,
15
+ PROPOSAL_PREDICATE,
16
+ RECOVERY_PREDICATE,
17
+ } from "./github-attestation-verifier.mjs";
18
+ const BACKLINK_PHASE = "publication-backlink";
19
+ const STAGE_PHASE = "stage-read";
20
+ const PHASES = [
21
+ STAGE_PHASE,
22
+ "hook-read",
23
+ BACKLINK_PHASE,
24
+ "publication-validate-pr",
25
+ ];
26
+ const ID = /^[1-9]\d*$/;
27
+ const REPO = /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9_.-]{1,100}$/;
28
+ const decimal = value => typeof value === "string" && ID.test(value);
29
+ const key = args => canonicalJson(args);
30
+ const api = endpoint => ["api", "--hostname", "github.com", endpoint];
31
+ import { recoveryPage } from "./npm-update-gh-grants.mjs";
32
+
33
+ /** Actual trusted allocation/config/proof bytes choose subject; requests cannot choose these fields. */
34
+ function checkedSubject(subject) {
35
+ keys(subject, [
36
+ "phase",
37
+ "repository",
38
+ "tracker",
39
+ "issue",
40
+ "branch",
41
+ "parent",
42
+ "origin",
43
+ "claim",
44
+ "recovery",
45
+ "maintainer",
46
+ "pr",
47
+ "proofs",
48
+ ]);
49
+ required(
50
+ PHASES.includes(subject.phase) &&
51
+ REPO.test(subject.repository) &&
52
+ REPO.test(subject.tracker) &&
53
+ ![subject.repository, subject.tracker].some(value =>
54
+ [".", ".."].includes(value.split("/")[1])
55
+ ) &&
56
+ decimal(subject.issue) &&
57
+ decimal(subject.claim) &&
58
+ /^lisa\/npm-[a-f0-9]{64}$/.test(subject.branch) &&
59
+ /^[a-f0-9]{40}$/.test(subject.parent),
60
+ "invalid closed GH subject"
61
+ );
62
+ keys(subject.origin, ["runId", "runAttempt"]);
63
+ required(
64
+ decimal(subject.origin.runId) && decimal(subject.origin.runAttempt),
65
+ "invalid GH run identity"
66
+ );
67
+ if (subject.pr !== null) keys(subject.pr, ["number", "url"]);
68
+ required(
69
+ subject.pr === null ||
70
+ (decimal(subject.pr.number) &&
71
+ subject.pr.url ===
72
+ `https://github.com/${subject.repository}/pull/${subject.pr.number}`),
73
+ "invalid GH PR identity"
74
+ );
75
+ required(
76
+ /^[A-Za-z0-9][A-Za-z0-9-]{0,38}$/.test(subject.maintainer) &&
77
+ Array.isArray(subject.proofs) &&
78
+ subject.proofs.length <= 2 &&
79
+ (subject.phase !== STAGE_PHASE || subject.proofs.length === 0),
80
+ "invalid GH phase proof scope"
81
+ );
82
+ if (subject.recovery !== null) {
83
+ keys(subject.recovery, ["runId", "runAttempt", "branch"]);
84
+ required(
85
+ decimal(subject.recovery.runId) &&
86
+ decimal(subject.recovery.runAttempt) &&
87
+ subject.recovery.branch === subject.branch,
88
+ "invalid GH recovery identity"
89
+ );
90
+ }
91
+ }
92
+
93
+ /** Read families use the same literal constructors as the canonical shipped callers. */
94
+ function ordinaryRequests(subject) {
95
+ const records = [
96
+ { kind: "plain", args: ["--version"] },
97
+ {
98
+ kind: "plain",
99
+ args: githubIssueViewArgs(subject.tracker, subject.issue),
100
+ },
101
+ {
102
+ kind: "hierarchy",
103
+ args: githubHierarchyArgs(
104
+ { repository: subject.tracker },
105
+ subject.issue,
106
+ null
107
+ ),
108
+ },
109
+ ];
110
+ if (subject.phase !== STAGE_PHASE)
111
+ records.push({
112
+ kind: "plain",
113
+ args: pullRequestViewArgs(
114
+ subject.pr?.number,
115
+ subject.branch,
116
+ subject.repository
117
+ ),
118
+ });
119
+ if (subject.phase === BACKLINK_PHASE)
120
+ records.push({
121
+ kind: "comments",
122
+ args: githubBacklinkListArgs(subject.tracker, subject.issue),
123
+ });
124
+ return records;
125
+ }
126
+
127
+ /** Only hook verification receives attestation/provider reads; stage has no premature proof scope. */
128
+ function proofRequests(subject) {
129
+ if (subject.phase !== "hook-read") return [];
130
+ const repository = subject.repository;
131
+ const endpoints = [
132
+ `repos/${repository}`,
133
+ `repos/${repository}/git/ref/heads/main`,
134
+ `repos/${repository}/actions/runs/${subject.origin.runId}/attempts/${subject.origin.runAttempt}`,
135
+ `repos/${subject.tracker}/issues/comments/${subject.claim}`,
136
+ ];
137
+ if (subject.recovery)
138
+ endpoints.push(
139
+ `repos/${repository}/actions/runs/${subject.recovery.runId}/attempts/${subject.recovery.runAttempt}`,
140
+ `repos/${repository}/issues/${subject.issue}`,
141
+ `repos/${repository}/git/matching-refs/heads/${subject.branch}`
142
+ );
143
+ const records = endpoints.map(endpoint => ({
144
+ kind: "plain",
145
+ args: api(endpoint),
146
+ }));
147
+ if (subject.recovery)
148
+ records.push(
149
+ { kind: "recovery-page", page: 1, args: recoveryPage(subject, 1) },
150
+ {
151
+ kind: "plain",
152
+ args: [
153
+ "api",
154
+ "--hostname",
155
+ "github.com",
156
+ "--include",
157
+ `repos/${repository}/assignees/${subject.maintainer}`,
158
+ ],
159
+ }
160
+ );
161
+ for (const proof of subject.proofs)
162
+ records.push(proofRequest(subject, proof));
163
+ return records;
164
+ }
165
+
166
+ function proofRequest(subject, proof) {
167
+ keys(proof, [
168
+ "file",
169
+ "bundle",
170
+ "predicate",
171
+ "signerWorkflow",
172
+ "signerDigest",
173
+ ]);
174
+ required(
175
+ [proof.file, proof.bundle].every(
176
+ value =>
177
+ typeof value === "string" &&
178
+ value.startsWith("/") &&
179
+ !/[\0\n]/.test(value)
180
+ ) &&
181
+ [PROPOSAL_PREDICATE, RECOVERY_PREDICATE].includes(proof.predicate) &&
182
+ /^[a-f0-9]{40}$/.test(proof.signerDigest) &&
183
+ /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/\.github\/workflows\/[A-Za-z0-9_.-]+\.ya?ml$/.test(
184
+ proof.signerWorkflow
185
+ ),
186
+ "invalid fixed GH proof request"
187
+ );
188
+ return {
189
+ kind: "plain",
190
+ args: officialArguments(
191
+ {
192
+ repository: subject.repository,
193
+ signerWorkflow: proof.signerWorkflow,
194
+ signerDigest: proof.signerDigest,
195
+ },
196
+ subject,
197
+ proof.file,
198
+ proof.bundle,
199
+ proof.predicate
200
+ ),
201
+ };
202
+ }
203
+
204
+ /** This finite subject catalogue grants no native execution or credentials. */
205
+ export function createGhRequests(subject) {
206
+ checkedSubject(subject);
207
+ const snapshot = JSON.parse(JSON.stringify(subject));
208
+ const records = [...ordinaryRequests(snapshot), ...proofRequests(snapshot)];
209
+ required(
210
+ records.length <= 64 &&
211
+ new Set(records.map(record => key(record.args))).size === records.length,
212
+ "ambiguous GH request catalogue"
213
+ );
214
+ return { subject: snapshot, records };
215
+ }
216
+
217
+ export {
218
+ createGhState,
219
+ prepareGhRequest,
220
+ observeGhResponse,
221
+ } from "./npm-update-gh-grants.mjs";
@@ -0,0 +1,214 @@
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 trusted GitHub transport. @module npm-updater */
5
+ import { canonicalJson } from "../lisa-automation-provenance.mjs";
6
+ import { assertPinnedVerifier } from "./github-attestation-verifier.mjs";
7
+ import { required } from "./npm-update-contract.mjs";
8
+ import { runProcess } from "./npm-update-process.mjs";
9
+ import { gitObjectId } from "./npm-update-object.mjs";
10
+ import { FILES } from "./npm-update-contract.mjs";
11
+ import { executeCanonicalHelper } from "./npm-update-controller-factory.mjs";
12
+ /** Explicit minimum gh identity and token; no caller/candidate executable override. */
13
+ const HOST_OPTION = "--hostname";
14
+ const GITHUB_HOST = "github.com";
15
+ const TRANSPORT_PATH = "/usr/bin:/bin";
16
+ const NO_HOME = "/nonexistent";
17
+
18
+ export class GitHub {
19
+ constructor(policy, token, execute = runProcess) {
20
+ assertPinnedVerifier(policy);
21
+ required(
22
+ typeof token === "string" && token.length > 0,
23
+ "job-scoped GitHub token is absent"
24
+ );
25
+ this.policy = policy;
26
+ this.token = token;
27
+ this.execute = execute;
28
+ }
29
+ async request(endpoint, method = "GET", body) {
30
+ required(
31
+ endpoint.startsWith(`repos/${this.policy.repository}/`) ||
32
+ endpoint === `repos/${this.policy.repository}` ||
33
+ endpoint.startsWith("search/issues?"),
34
+ "provider endpoint outside configured repository"
35
+ );
36
+ const args = [
37
+ "api",
38
+ HOST_OPTION,
39
+ GITHUB_HOST,
40
+ "--method",
41
+ method,
42
+ endpoint,
43
+ ];
44
+ if (body) args.push("--input", "-");
45
+ const result = await this.execute(this.policy.ghExecutable, args, {
46
+ env: {
47
+ PATH: TRANSPORT_PATH,
48
+ GH_TOKEN: this.token,
49
+ GH_HOST: GITHUB_HOST,
50
+ HOME: NO_HOME,
51
+ GIT_TERMINAL_PROMPT: "0",
52
+ },
53
+ input: body ? canonicalJson(body) : undefined,
54
+ timeout: 30_000,
55
+ });
56
+ const bytes = result.stdout;
57
+ required(bytes.length <= 3_145_728, "provider response exceeds bound");
58
+ return bytes.length
59
+ ? JSON.parse(new TextDecoder("utf8", { fatal: true }).decode(bytes))
60
+ : null;
61
+ }
62
+ async list(endpoint) {
63
+ const entries = [];
64
+ for (let page = 1; page <= 20; page++) {
65
+ const batch = await this.request(
66
+ `${endpoint}${endpoint.includes("?") ? "&" : "?"}per_page=100&page=${page}`
67
+ );
68
+ required(Array.isArray(batch), "provider page is not an array");
69
+ entries.push(...batch);
70
+ if (batch.length < 100) return entries;
71
+ }
72
+ throw new Error("npm updater: provider pagination exceeded bound");
73
+ }
74
+ /** Absence is a proved 404, never a read error interpreted as empty state. */
75
+ async maybe(endpoint) {
76
+ required(
77
+ endpoint.startsWith(`repos/${this.policy.repository}/`),
78
+ "recovery endpoint outside configured repository"
79
+ );
80
+ const result = await this.execute(
81
+ this.policy.ghExecutable,
82
+ ["api", HOST_OPTION, GITHUB_HOST, "--include", endpoint],
83
+ {
84
+ env: {
85
+ PATH: TRANSPORT_PATH,
86
+ GH_TOKEN: this.token,
87
+ HOME: NO_HOME,
88
+ },
89
+ allowed: [0, 1],
90
+ timeout: 30_000,
91
+ }
92
+ );
93
+ const text = result.stdout.toString();
94
+ const split = text.includes("\r\n\r\n") ? "\r\n\r\n" : "\n\n";
95
+ const status = /^HTTP\/\S+ (\d+)\b/.exec(text);
96
+ required(Boolean(status), "recovery provider status missing");
97
+ if (status[1] === "404") return null;
98
+ required(
99
+ result.code === 0 && status[1] === "200",
100
+ "recovery provider refused read"
101
+ );
102
+ return JSON.parse(text.slice(text.indexOf(split) + split.length));
103
+ }
104
+ async issue(number) {
105
+ required(
106
+ Number.isSafeInteger(number) && number > 0,
107
+ "invalid live issue number"
108
+ );
109
+ const path = `repos/${this.policy.repository}/issues/${number}`;
110
+ const issue = await this.request(path);
111
+ issue.comments = await this.list(`${path}/comments`);
112
+ issue.children = await this.list(`${path}/sub_issues`);
113
+ issue.blockers = await this.list(`${path}/dependencies/blocked_by`);
114
+ return issue;
115
+ }
116
+ async main(parent) {
117
+ const repo = await this.request(`repos/${this.policy.repository}`);
118
+ required(
119
+ repo.full_name === this.policy.repository &&
120
+ repo.default_branch === "main" &&
121
+ String(repo.id) === this.policy.repositoryId &&
122
+ String(repo.owner.id) === this.policy.ownerId,
123
+ "actual repository identity differs"
124
+ );
125
+ const main = await this.request(
126
+ `repos/${this.policy.repository}/git/ref/heads/main`
127
+ );
128
+ required(main.object?.sha === parent, "actual main parent changed");
129
+ return repo;
130
+ }
131
+ async assignable(login) {
132
+ const result = await this.execute(
133
+ this.policy.ghExecutable,
134
+ [
135
+ "api",
136
+ HOST_OPTION,
137
+ GITHUB_HOST,
138
+ "--include",
139
+ `repos/${this.policy.repository}/assignees/${login}`,
140
+ ],
141
+ {
142
+ env: {
143
+ PATH: TRANSPORT_PATH,
144
+ GH_TOKEN: this.token,
145
+ HOME: NO_HOME,
146
+ },
147
+ timeout: 30_000,
148
+ }
149
+ );
150
+ required(
151
+ /^HTTP\/\S+ 204\b/m.test(result.stdout.toString()),
152
+ "trusted maintainer is not currently assignable"
153
+ );
154
+ }
155
+ /** Invoke the shipped tracker writer/validator, preserving configured full/trailer mode. */
156
+ async workItem(cwd, args, authority) {
157
+ required(
158
+ ["backlink", "validate-pr"].includes(args[0]) &&
159
+ args.every(value => typeof value === "string"),
160
+ "unsupported traceability operation"
161
+ );
162
+ required(
163
+ authority?.config && authority?.pr && authority?.descriptor,
164
+ "authenticated publication controller authority is absent"
165
+ );
166
+ const phase =
167
+ args[0] === "backlink"
168
+ ? "publication-backlink"
169
+ : "publication-validate-pr";
170
+ return executeCanonicalHelper(
171
+ { ...authority, cwd, token: this.token },
172
+ phase,
173
+ args[0],
174
+ authority.pr,
175
+ args
176
+ );
177
+ }
178
+ /** Git API writes reproduce only the already qualified raw commit and complete tree. */
179
+ async gitCommit(proposal, descriptor, commit, authorize, cwd) {
180
+ const path = `repos/${this.policy.repository}/git`;
181
+ const tree = [];
182
+ for (const file of FILES) {
183
+ await authorize();
184
+ const bytes = Buffer.from(proposal.files[file]);
185
+ const blob = await this.request(`${path}/blobs`, "POST", {
186
+ content: bytes.toString("base64"),
187
+ encoding: "base64",
188
+ });
189
+ const sha = gitObjectId(cwd, "blob", bytes);
190
+ required(blob.sha === sha, "provider blob bytes differ");
191
+ tree.push({ path: file, mode: "100644", type: "blob", sha });
192
+ }
193
+ const parent = await this.request(`${path}/commits/${proposal.parent}`);
194
+ await authorize();
195
+ const built = await this.request(`${path}/trees`, "POST", {
196
+ base_tree: parent.tree.sha,
197
+ tree,
198
+ });
199
+ required(built.sha === descriptor.tree, "provider complete tree differs");
200
+ await authorize();
201
+ const created = await this.request(`${path}/commits`, "POST", {
202
+ message: commit.message,
203
+ tree: built.sha,
204
+ parents: [proposal.parent],
205
+ author: commit.author,
206
+ committer: commit.committer,
207
+ });
208
+ required(
209
+ created.sha === commit.sha,
210
+ "provider raw commit cannot preserve the gated SHA"
211
+ );
212
+ return created;
213
+ }
214
+ }
@@ -0,0 +1,238 @@
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
+ /** Fixed authenticated data qualifies complete import and canonical-child closures without evaluating source. */
5
+ import assert from "node:assert/strict";
6
+ import { createHash } from "node:crypto";
7
+ import { isBuiltin } from "node:module";
8
+ import { posix } from "node:path";
9
+
10
+ import {
11
+ managedTemplateMembers,
12
+ HELPER_CONTROLS as CONTROLS,
13
+ HELPER_PACKAGE_MEMBERS as PACKAGE,
14
+ } from "./npm-update-helper-inventory.mjs";
15
+ export { managedTemplateMembers } from "./npm-update-helper-inventory.mjs";
16
+
17
+ /** The independently authenticated classifier retains its one literal canonical dependency. */
18
+ export function classifierGraphMatches(bytes) {
19
+ const text = bytes.map(value =>
20
+ new TextDecoder("utf8", { fatal: true }).decode(value)
21
+ );
22
+ const importsIn = source =>
23
+ source
24
+ .split("\n")
25
+ .map(line =>
26
+ line
27
+ .trim()
28
+ .replace(/^export\s+(?:async\s+)?(?:function|const|let|class)\b/, "")
29
+ );
30
+ const dependenciesIn = source =>
31
+ importsIn(source).filter(line => /\b(?:import|export)\b/.test(line));
32
+ const imports = dependenciesIn(text[0]);
33
+ return (
34
+ imports.length === 1 &&
35
+ imports[0] ===
36
+ 'import { isPreWorkLaneType } from "./intake-prework-denominator.mjs";' &&
37
+ !/\bimport\s*\(/.test(text[0]) &&
38
+ dependenciesIn(text[1]).length === 0 &&
39
+ !/\bimport\s*\(/.test(text[1])
40
+ );
41
+ }
42
+
43
+ /** Relative normalized names never authorize traversal, URLs, absolute paths or unknown helpers. */
44
+ function memberPath(member) {
45
+ assert(
46
+ typeof member === "string" &&
47
+ member.length <= 256 &&
48
+ member === posix.normalize(member) &&
49
+ !member.startsWith("/") &&
50
+ !member.split("/").includes("..") &&
51
+ !/[\\\0\n]/.test(member) &&
52
+ (managedTemplateMembers().has(member) || PACKAGE.includes(member)),
53
+ "helper path is outside the fixed inventory"
54
+ );
55
+ }
56
+
57
+ function fields(value, expected) {
58
+ assert(
59
+ value &&
60
+ typeof value === "object" &&
61
+ !Array.isArray(value) &&
62
+ Object.getPrototypeOf(value) === Object.prototype &&
63
+ Object.keys(value).sort().join("\n") === [...expected].sort().join("\n"),
64
+ "helper graph schema differs"
65
+ );
66
+ }
67
+
68
+ /** Every declared edge is finite, unique and points to a builtin or another fixed member. */
69
+ function edges(record, field) {
70
+ const values = record[field];
71
+ assert(
72
+ Array.isArray(values) &&
73
+ values.length <= 64 &&
74
+ new Set(values).size === values.length,
75
+ "helper edges are ambiguous or unbounded"
76
+ );
77
+ for (const value of values) {
78
+ if (
79
+ field !== "children" &&
80
+ typeof value === "string" &&
81
+ value.startsWith("node:") &&
82
+ isBuiltin(value)
83
+ )
84
+ continue;
85
+ memberPath(value);
86
+ }
87
+ return values.filter(value => !value.startsWith("node:"));
88
+ }
89
+
90
+ /**
91
+ * @typedef {{staticImports: string[], dynamicImports: string[], children: string[]}} HelperEdges
92
+ * @typedef {HelperEdges & {sha256: string}} HelperMember
93
+ * @typedef {{version: 1, controls: Record<string, HelperEdges>, members: Record<string, HelperMember>}} HelperManifest
94
+ */
95
+
96
+ /**
97
+ * SHA256 binds static, literal dynamic and canonical subprocess edges to the actual audited source.
98
+ * @returns {HelperManifest} The closed validated manifest.
99
+ */
100
+ export function validateHelperManifest(manifest) {
101
+ fields(manifest, ["version", "controls", "members"]);
102
+ assert(manifest.version === 1, "unsupported helper graph version");
103
+ const names = Object.keys(manifest.members ?? {});
104
+ assert(
105
+ names.length > 0 && names.length <= 128,
106
+ "helper member inventory is empty or unbounded"
107
+ );
108
+ for (const name of names) {
109
+ memberPath(name);
110
+ const record = manifest.members[name];
111
+ fields(record, ["sha256", "staticImports", "dynamicImports", "children"]);
112
+ assert(
113
+ /^[a-f0-9]{64}$/.test(record.sha256),
114
+ "helper source hash is invalid"
115
+ );
116
+ for (const field of ["staticImports", "dynamicImports", "children"])
117
+ edges(record, field);
118
+ }
119
+ assert(
120
+ manifest.controls &&
121
+ typeof manifest.controls === "object" &&
122
+ !Array.isArray(manifest.controls),
123
+ "helper control inventory differs"
124
+ );
125
+ for (const [name, record] of Object.entries(manifest.controls)) {
126
+ assert(
127
+ CONTROLS.includes(name) && !Object.hasOwn(manifest.members, name),
128
+ "unknown or overlapping helper control"
129
+ );
130
+ fields(record, ["staticImports", "dynamicImports", "children"]);
131
+ for (const field of ["staticImports", "dynamicImports", "children"])
132
+ edges(record, field);
133
+ }
134
+ return manifest;
135
+ }
136
+
137
+ /** Independently authenticated control/manifest bytes avoid an impossible self-inclusive source hash. */
138
+ export function helperManifest(bytes) {
139
+ assert(
140
+ Buffer.isBuffer(bytes) && bytes.length <= 262_144,
141
+ "helper manifest is unbounded"
142
+ );
143
+ const text = new TextDecoder("utf8", { fatal: true, ignoreBOM: true }).decode(
144
+ bytes
145
+ );
146
+ const manifest = validateHelperManifest(JSON.parse(text));
147
+ assert(
148
+ text === `${JSON.stringify(manifest, null, 2)}\n`,
149
+ "helper manifest is not canonical readable JSON"
150
+ );
151
+ assert(
152
+ Object.keys(manifest.controls).sort().join("\n") ===
153
+ [...CONTROLS].sort().join("\n"),
154
+ "independent helper controls are incomplete"
155
+ );
156
+ return manifest;
157
+ }
158
+
159
+ /** A controller manifest covers the whole fixed inventory; omitted unrelated members cannot mask drift. */
160
+ export function controllerMembers(manifest) {
161
+ validateHelperManifest(manifest);
162
+ const expected = [...managedTemplateMembers().keys(), ...PACKAGE].filter(
163
+ member => !CONTROLS.includes(member)
164
+ );
165
+ assert(
166
+ Object.keys(manifest.members).sort().join("\n") ===
167
+ expected.sort().join("\n"),
168
+ "controller helper manifest inventory is incomplete"
169
+ );
170
+ return [...expected, ...CONTROLS];
171
+ }
172
+
173
+ /** This pure check never grants authority; callers authenticate every buffer from one pinned owner first. */
174
+ function checkedClosure(manifest, authenticated, entries) {
175
+ validateHelperManifest(manifest);
176
+ const selected = new Set();
177
+ const visit = name => {
178
+ memberPath(name);
179
+ if (selected.has(name)) return;
180
+ const record = manifest.members[name] ?? manifest.controls[name];
181
+ assert(record, "helper entry is outside the manifest inventory");
182
+ const bytes = authenticated.get(name);
183
+ assert(
184
+ Buffer.isBuffer(bytes) && bytes.length <= 1_048_576,
185
+ "authenticated helper member is missing or unbounded"
186
+ );
187
+ if (record.sha256)
188
+ assert(
189
+ createHash("sha256").update(bytes).digest("hex") === record.sha256,
190
+ "authenticated helper source hash changed"
191
+ );
192
+ selected.add(name);
193
+ for (const field of ["staticImports", "dynamicImports", "children"])
194
+ for (const child of edges(record, field)) visit(child);
195
+ };
196
+ for (const entry of entries) visit(entry);
197
+ return [...selected].sort();
198
+ }
199
+
200
+ /** The external selector remains bounded independently of the fixed authority inventory. */
201
+ export function assertManagedSelection(entries) {
202
+ assert(
203
+ Array.isArray(entries) &&
204
+ entries.length > 0 &&
205
+ entries.length <= 64 &&
206
+ new Set(entries).size === entries.length,
207
+ "helper selection is ambiguous"
208
+ );
209
+ }
210
+
211
+ /** External callers select at most64 unique roots; fixed control nodes do not consume that budget. */
212
+ export function managedClosure(manifest, authenticated, entries) {
213
+ assertManagedSelection(entries);
214
+ assert(authenticated instanceof Map, "helper selection is ambiguous");
215
+ return checkedClosure(manifest, authenticated, entries);
216
+ }
217
+
218
+ /** Fixed control authority accompanies a bounded caller's complete selected closure. */
219
+ export function managedControllerSelection(manifest, authenticated, entries) {
220
+ return [
221
+ ...new Set([
222
+ ...managedClosure(manifest, authenticated, entries),
223
+ ...CONTROLS,
224
+ ]),
225
+ ];
226
+ }
227
+
228
+ /** Complete authority audit accepts exactly the fixed inventory, never an expanded caller selector. */
229
+ export function auditControllerClosure(manifest, authenticated) {
230
+ const members = controllerMembers(manifest);
231
+ assert(
232
+ authenticated instanceof Map &&
233
+ authenticated.size === members.length &&
234
+ [...authenticated.keys()].every(member => members.includes(member)),
235
+ "complete authenticated controller inventory differs"
236
+ );
237
+ return checkedClosure(manifest, authenticated, members);
238
+ }