@kungfu-tech/buildchain 3.0.4-alpha.1 → 3.0.4-alpha.11

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 (71) hide show
  1. package/AGENTS.md +14 -5
  2. package/README.md +18 -0
  3. package/bin/buildchain.mjs +136 -73
  4. package/bin/internal/trust-release-cli.mjs +15 -537
  5. package/bin/internal/trust-release-command-handlers.mjs +14 -0
  6. package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
  7. package/bin/internal/trust-release-release-handlers.mjs +317 -0
  8. package/bin/internal/trust-release-verification-handlers.mjs +306 -0
  9. package/dist/site/buildchain-contract.json +35 -24
  10. package/dist/site/buildchain-site.json +1474 -61
  11. package/dist/site/capability-registry.json +8 -5
  12. package/dist/site/cli-registry.json +1869 -0
  13. package/dist/site/controller-registry.json +16 -3
  14. package/dist/site/kfd-claims.json +84 -10
  15. package/dist/site/kfd-upstream-aggregate.json +1 -1
  16. package/dist/site/manual-registry.json +49 -6
  17. package/dist/site/node-api-registry.json +17802 -11
  18. package/dist/site/page-registry.json +1441 -59
  19. package/dist/site/public-surface-audit.json +2863 -358
  20. package/dist/site/publication-registry.json +4 -4
  21. package/dist/site/release-model.json +7 -0
  22. package/dist/site/site-manifest.json +34 -10
  23. package/dist/site/workflow-registry.json +13 -5
  24. package/docs/MAP.md +21 -6
  25. package/docs/cli-reference.md +1936 -0
  26. package/docs/cli.md +15 -1
  27. package/docs/getting-started.md +167 -0
  28. package/docs/install.md +7 -8
  29. package/docs/node-api-reference.md +1952 -0
  30. package/docs/release-propagation.md +166 -8
  31. package/docs/site-bundle-contract.md +10 -4
  32. package/docs/versioning.md +5 -4
  33. package/package.json +8 -5
  34. package/packages/core/buildchain-agent-manuals.js +37 -0
  35. package/packages/core/buildchain-kfd-claims.js +4 -37
  36. package/packages/core/controller-evidence.js +8 -1
  37. package/packages/core/index.js +1 -13
  38. package/packages/core/paper-agent-entry.js +14 -11
  39. package/packages/core/paper-fleet.js +34 -7
  40. package/packages/core/paper-npm-bootstrap.js +492 -0
  41. package/packages/core/paper-repository.js +1 -0
  42. package/packages/core/paper.js +346 -671
  43. package/packages/core/public-surface-cli.js +12 -1
  44. package/packages/core/publication-package.js +7 -0
  45. package/packages/core/release-passport.js +67 -71
  46. package/packages/core/release-propagation-common.js +64 -0
  47. package/packages/core/release-propagation-execution-profile.js +59 -0
  48. package/packages/core/release-propagation-release.js +196 -0
  49. package/packages/core/release-propagation-stage-evidence.js +364 -0
  50. package/packages/core/release-propagation-work-capture.js +64 -0
  51. package/packages/core/release-propagation-work-constants.js +34 -0
  52. package/packages/core/release-propagation-work-control.js +203 -0
  53. package/packages/core/release-propagation-work-transitions.js +145 -0
  54. package/packages/core/release-propagation-work.js +517 -0
  55. package/packages/core/release-propagation.js +34 -158
  56. package/scripts/aws-windows-jit-controller-core.mjs +269 -0
  57. package/scripts/aws-windows-jit-controller.mjs +502 -0
  58. package/scripts/check-internal-architecture.mjs +178 -52
  59. package/scripts/check-inventory.mjs +2 -2
  60. package/scripts/check-maintainability.mjs +76 -17
  61. package/scripts/generate-public-reference.mjs +68 -0
  62. package/scripts/generate-site-bundle.mjs +35 -41
  63. package/scripts/maintainability-metrics.mjs +24 -4
  64. package/scripts/paper-work-fleet-cli.mjs +22 -3
  65. package/scripts/public-reference.mjs +557 -0
  66. package/scripts/release-propagation.mjs +126 -0
  67. package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
  68. package/scripts/site-reference-registry.mjs +174 -0
  69. package/scripts/verify-golden-path.mjs +169 -0
  70. package/scripts/web-surface-core.mjs +46 -207
  71. package/scripts/web-surface-routing.mjs +286 -0
@@ -45,6 +45,7 @@ import {
45
45
  scaffoldPackageJson,
46
46
  scaffoldReadme,
47
47
  } from "./paper-scaffold-content.js";
48
+ import { executePaperNpmBootstrap as executePaperNpmBootstrapOperation } from "./paper-npm-bootstrap.js";
48
49
 
49
50
  export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
50
51
  export {
@@ -73,6 +74,7 @@ export {
73
74
  collectPaperFleetAudit,
74
75
  discoverPaperFleet,
75
76
  planPaperFleetUpdate,
77
+ paperFleetTransitionWorkspace,
76
78
  writePaperFleetUpdate,
77
79
  } from "./paper-fleet.js";
78
80
 
@@ -112,6 +114,7 @@ const PAPER_SCAFFOLD_PATHS = Object.freeze([
112
114
  PAPER_PATHS.buildWorkflow,
113
115
  PAPER_PATHS.verifyWorkflow,
114
116
  PAPER_PATHS.releaseWorkflow,
117
+ PAPER_PATHS.pnpmWorkspace,
115
118
  PAPER_PATHS.provisioningAuthority,
116
119
  "Makefile",
117
120
  "package.json",
@@ -133,6 +136,7 @@ const DEFAULT_TOOLCHAIN_COMMAND = "latexmk -pdf -outdir=_build paper/main.tex";
133
136
  const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/i;
134
137
  const GIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
135
138
  const PACKAGE_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
139
+ const BUILDCHAIN_PACKAGE_NAME = "@kungfu-tech/buildchain";
136
140
 
137
141
  function toPosix(value) {
138
142
  return String(value || "")
@@ -148,6 +152,66 @@ function jsonText(value) {
148
152
  return `${JSON.stringify(value, null, 2)}\n`;
149
153
  }
150
154
 
155
+ function paperPnpmWorkspace(current, buildchainVersion) {
156
+ const entry = `${BUILDCHAIN_PACKAGE_NAME}@${buildchainVersion}`;
157
+ const source = String(current || "");
158
+ const lines = source ? source.replace(/\r\n/g, "\n").split("\n") : [];
159
+ if (lines.at(-1) === "") lines.pop();
160
+ const keyLines = lines
161
+ .map((line, index) =>
162
+ /^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line) ? index : -1,
163
+ )
164
+ .filter((index) => index >= 0);
165
+ const unsupportedKey = lines.some(
166
+ (line) =>
167
+ /^minimumReleaseAgeExclude\s*:/.test(line) &&
168
+ !/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line),
169
+ );
170
+ if (unsupportedKey || keyLines.length > 1) {
171
+ throw new Error(
172
+ "paper migration requires minimumReleaseAgeExclude to be one top-level block sequence",
173
+ );
174
+ }
175
+ if (keyLines.length === 0) {
176
+ const prefix = lines.length > 0 ? [...lines, ""] : [];
177
+ return `${[...prefix, "minimumReleaseAgeExclude:", ` - '${entry}'`].join(
178
+ "\n",
179
+ )}\n`;
180
+ }
181
+ const keyIndex = keyLines[0];
182
+ let blockEnd = lines.length;
183
+ for (let index = keyIndex + 1; index < lines.length; index += 1) {
184
+ if (/^[^\s#]/.test(lines[index])) {
185
+ blockEnd = index;
186
+ break;
187
+ }
188
+ }
189
+ const retained = [];
190
+ for (const line of lines.slice(keyIndex + 1, blockEnd)) {
191
+ if (!line.trim() || /^\s*#/.test(line)) {
192
+ retained.push(line);
193
+ continue;
194
+ }
195
+ const item = line.match(/^\s*-\s+(.+?)\s*(?:#.*)?$/);
196
+ if (!item) {
197
+ throw new Error(
198
+ "paper migration requires minimumReleaseAgeExclude to contain scalar package entries",
199
+ );
200
+ }
201
+ const value = item[1]
202
+ .trim()
203
+ .replace(/^'(.*)'$/, "$1")
204
+ .replace(/^"(.*)"$/, "$1");
205
+ if (!value.startsWith(`${BUILDCHAIN_PACKAGE_NAME}@`)) retained.push(line);
206
+ }
207
+ return `${[
208
+ ...lines.slice(0, keyIndex + 1),
209
+ ...retained,
210
+ ` - '${entry}'`,
211
+ ...lines.slice(blockEnd),
212
+ ].join("\n")}\n`;
213
+ }
214
+
151
215
  function existingFileFact(cwd, relativePath) {
152
216
  const filePath = path.resolve(cwd, relativePath);
153
217
  if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
@@ -420,6 +484,7 @@ ${passportInput} secrets:
420
484
  BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID }}
421
485
  BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY }}
422
486
  BUILDCHAIN_GENERATED_WRITE_TOKEN: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_TOKEN }}
487
+ BUILDCHAIN_PROMOTION_TOKEN: \${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
423
488
  `;
424
489
  }
425
490
 
@@ -602,6 +667,7 @@ function scaffoldFiles({
602
667
  [PAPER_PATHS.buildWorkflow, buildWorkflow],
603
668
  [PAPER_PATHS.verifyWorkflow, verifyWorkflow],
604
669
  [PAPER_PATHS.releaseWorkflow, releaseWorkflow],
670
+ [PAPER_PATHS.pnpmWorkspace, paperPnpmWorkspace("", buildchainVersion)],
605
671
  [PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
606
672
  ...agentEntry,
607
673
  [
@@ -923,12 +989,20 @@ function migrationFiles({
923
989
  currentPackage.value,
924
990
  runtimeIdentity.version,
925
991
  );
992
+ const pnpmWorkspacePath = path.resolve(cwd, PAPER_PATHS.pnpmWorkspace);
993
+ const pnpmWorkspace = paperPnpmWorkspace(
994
+ fs.existsSync(pnpmWorkspacePath)
995
+ ? fs.readFileSync(pnpmWorkspacePath, "utf8")
996
+ : "",
997
+ runtimeIdentity.version,
998
+ );
926
999
  const files = new Map([
927
1000
  [PAPER_PATHS.contractLock, contractLockText],
928
1001
  [PAPER_PATHS.versionPin, `${runtimeIdentity.version}\n`],
929
1002
  [PAPER_PATHS.buildWorkflow, buildWorkflow],
930
1003
  [PAPER_PATHS.verifyWorkflow, verifyWorkflow],
931
1004
  [PAPER_PATHS.releaseWorkflow, releaseWorkflow],
1005
+ [PAPER_PATHS.pnpmWorkspace, pnpmWorkspace],
932
1006
  [PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
933
1007
  ...agentEntry,
934
1008
  ["package.json", jsonText(packageJson)],
@@ -1230,8 +1304,7 @@ function releaseTransactionFacts(candidates, version) {
1230
1304
  };
1231
1305
  }
1232
1306
 
1233
- export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1234
- const resolvedCwd = path.resolve(cwd);
1307
+ function collectPaperGovernanceFacts(resolvedCwd) {
1235
1308
  const configResult = paperConfig(resolvedCwd);
1236
1309
  const loaded = configResult.loaded;
1237
1310
  const publication = loaded?.config?.publication;
@@ -1272,7 +1345,13 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1272
1345
  gitRepo.ok &&
1273
1346
  gitRepo.stdout === "true",
1274
1347
  );
1348
+ return {
1349
+ configResult, loaded, publication, packageName, version, configFact,
1350
+ scaffoldFacts, contractLockError, scaffoldOk, governedOk,
1351
+ };
1352
+ }
1275
1353
 
1354
+ function collectPaperEvidenceFacts(resolvedCwd, publication, version) {
1276
1355
  const admissionInputs = [PAPER_PATHS.capability, PAPER_PATHS.admission].map(
1277
1356
  (relativePath) => ({
1278
1357
  relativePath,
@@ -1330,6 +1409,82 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1330
1409
  visibility.value,
1331
1410
  "production",
1332
1411
  );
1412
+ return {
1413
+ admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
1414
+ sealedOk, sealedError, candidates, transactionFacts, packagePublished,
1415
+ alphaComplete, stagingVisible, productionVisible,
1416
+ };
1417
+ }
1418
+
1419
+ function paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts }) {
1420
+ const actions = [];
1421
+ if (!stateMap.scaffolded.satisfied) {
1422
+ actions.push({
1423
+ id: "scaffold-paper",
1424
+ command: "buildchain paper scaffold --help",
1425
+ description: "Plan a no-overwrite Buildchain paper scaffold.",
1426
+ });
1427
+ } else if (!stateMap.governed.satisfied) {
1428
+ actions.push({
1429
+ id: "restore-governance",
1430
+ command: "buildchain paper preflight --json",
1431
+ description:
1432
+ "Repair the Git/contract-lock governance checks reported by preflight.",
1433
+ });
1434
+ } else if (!stateMap["content-ready"].satisfied) {
1435
+ actions.push({
1436
+ id: "complete-paper-content",
1437
+ command: "make check",
1438
+ description: "Add every declared paper source and metadata input.",
1439
+ });
1440
+ } else if (!deterministic) {
1441
+ actions.push({
1442
+ id: "build-reproducible-artifact",
1443
+ command: "buildchain paper build --execute --json",
1444
+ description:
1445
+ "Run the existing two-clean-build reproducibility gate and promote exact bytes.",
1446
+ });
1447
+ } else if (
1448
+ !stateMap.bootstrapped.satisfied ||
1449
+ !stateMap["trust-bound"].satisfied
1450
+ ) {
1451
+ actions.push({
1452
+ id: "bootstrap-npm",
1453
+ command: "buildchain paper bootstrap npm --json",
1454
+ description:
1455
+ "Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
1456
+ });
1457
+ } else if (!stateMap["alpha-complete"].satisfied) {
1458
+ actions.push({
1459
+ id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
1460
+ command: transactionFacts.selected
1461
+ ? "buildchain paper resume --json"
1462
+ : "buildchain paper alpha --json",
1463
+ description: transactionFacts.selected
1464
+ ? "Resume the exact sealed release transaction."
1465
+ : "Plan the protected dev-to-alpha publication PR.",
1466
+ });
1467
+ }
1468
+ return actions;
1469
+ }
1470
+
1471
+ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1472
+ const resolvedCwd = path.resolve(cwd);
1473
+ const governance = collectPaperGovernanceFacts(resolvedCwd);
1474
+ const evidence = collectPaperEvidenceFacts(
1475
+ resolvedCwd,
1476
+ governance.publication,
1477
+ governance.version,
1478
+ );
1479
+ const {
1480
+ configResult, loaded, publication, packageName, version, configFact,
1481
+ scaffoldFacts, contractLockError, scaffoldOk, governedOk,
1482
+ } = governance;
1483
+ const {
1484
+ admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
1485
+ sealedOk, sealedError, candidates, transactionFacts, packagePublished,
1486
+ alphaComplete, stagingVisible, productionVisible,
1487
+ } = evidence;
1333
1488
 
1334
1489
  const states = [
1335
1490
  state(
@@ -1439,54 +1594,7 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
1439
1594
  const highestEvidenceState =
1440
1595
  [...states].reverse().find((entry) => entry.satisfied)?.id || "none";
1441
1596
  const stateMap = Object.fromEntries(states.map((entry) => [entry.id, entry]));
1442
- const blockingNextActions = [];
1443
- if (!stateMap.scaffolded.satisfied) {
1444
- blockingNextActions.push({
1445
- id: "scaffold-paper",
1446
- command: "buildchain paper scaffold --help",
1447
- description: "Plan a no-overwrite Buildchain paper scaffold.",
1448
- });
1449
- } else if (!stateMap.governed.satisfied) {
1450
- blockingNextActions.push({
1451
- id: "restore-governance",
1452
- command: "buildchain paper preflight --json",
1453
- description:
1454
- "Repair the Git/contract-lock governance checks reported by preflight.",
1455
- });
1456
- } else if (!stateMap["content-ready"].satisfied) {
1457
- blockingNextActions.push({
1458
- id: "complete-paper-content",
1459
- command: "make check",
1460
- description: "Add every declared paper source and metadata input.",
1461
- });
1462
- } else if (!deterministic) {
1463
- blockingNextActions.push({
1464
- id: "build-reproducible-artifact",
1465
- command: "buildchain paper build --execute --json",
1466
- description:
1467
- "Run the existing two-clean-build reproducibility gate and promote exact bytes.",
1468
- });
1469
- } else if (
1470
- !stateMap.bootstrapped.satisfied ||
1471
- !stateMap["trust-bound"].satisfied
1472
- ) {
1473
- blockingNextActions.push({
1474
- id: "bootstrap-npm",
1475
- command: "buildchain paper bootstrap npm --json",
1476
- description:
1477
- "Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
1478
- });
1479
- } else if (!stateMap["alpha-complete"].satisfied) {
1480
- blockingNextActions.push({
1481
- id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
1482
- command: transactionFacts.selected
1483
- ? "buildchain paper resume --json"
1484
- : "buildchain paper alpha --json",
1485
- description: transactionFacts.selected
1486
- ? "Resume the exact sealed release transaction."
1487
- : "Plan the protected dev-to-alpha publication PR.",
1488
- });
1489
- }
1597
+ const blockingNextActions = paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts });
1490
1598
  return {
1491
1599
  schemaVersion: 1,
1492
1600
  contract: PAPER_STATUS_CONTRACT,
@@ -2050,146 +2158,8 @@ function localToolchainObservation(cwd, publication) {
2050
2158
  };
2051
2159
  }
2052
2160
 
2053
- export function collectPaperPreflight({
2054
- cwd = process.cwd(),
2055
- buildchainRoot = process.cwd(),
2056
- buildchainVersion = "",
2057
- buildchainRef = "v3",
2058
- buildchainSha = "",
2059
- registry = NPM_REGISTRY,
2060
- offline = false,
2061
- agentEntryMode = "contract",
2062
- } = {}) {
2063
- const resolvedCwd = path.resolve(cwd);
2064
- const status = collectPaperStatus({ cwd: resolvedCwd });
2065
- const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2066
- const configResult = paperConfig(resolvedCwd);
2067
- let validation;
2068
- let validationError = "";
2069
- try {
2070
- validation = validateBuildchainConfig(resolvedCwd, {
2071
- requireLifecycleStages: ["verify"],
2072
- });
2073
- } catch (error) {
2074
- validationError = error.message;
2075
- }
2076
- const runtime = runtimeFacts({
2077
- buildchainRoot,
2078
- buildchainVersion,
2079
- buildchainRef: provisioning.value?.runtime?.ref || buildchainRef,
2080
- buildchainSha,
2081
- });
2082
- const agentEntry = collectPaperAgentEntry({
2083
- cwd: resolvedCwd,
2084
- buildchainSha: runtime.resolvedSha,
2085
- mode: agentEntryMode,
2086
- });
2087
- const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
2088
- let lockEvaluation = {
2089
- status: "missing-lock",
2090
- compatible: false,
2091
- drift: false,
2092
- reasons: ["Buildchain contract lock is missing"],
2093
- };
2094
- try {
2095
- const lock = readBuildchainContractLock(lockPath);
2096
- if (lock) {
2097
- const current = runtimeContractWorld(buildchainRoot);
2098
- lockEvaluation = evaluateBuildchainContractLock({
2099
- lock,
2100
- current,
2101
- runtimeRef: runtime.ref,
2102
- runtimeSha: runtime.resolvedSha,
2103
- runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
2104
- });
2105
- }
2106
- } catch (error) {
2107
- lockEvaluation = {
2108
- status: "invalid-lock",
2109
- compatible: false,
2110
- drift: false,
2111
- reasons: [error.message],
2112
- };
2113
- }
2114
- const source = {
2115
- repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
2116
- head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
2117
- tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
2118
- branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
2119
- clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
2120
- };
2121
- const packageName = status.identity.package;
2122
- const repository = status.identity.repository;
2123
- const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
2124
- repository,
2125
- workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
2126
- });
2127
- const npm =
2128
- offline || !packageName
2129
- ? {
2130
- package: {
2131
- status: "unknown",
2132
- exists: null,
2133
- version: "",
2134
- registry,
2135
- errorCode: offline ? "offline" : "package-unresolved",
2136
- },
2137
- auth: {
2138
- status: "unknown",
2139
- authenticated: null,
2140
- identity: "",
2141
- errorCode: offline ? "offline" : "package-unresolved",
2142
- },
2143
- trust: {
2144
- status: "unknown",
2145
- configured: null,
2146
- publishers: [],
2147
- errorCode: offline ? "offline" : "package-unresolved",
2148
- },
2149
- }
2150
- : {
2151
- package: liveNpmPackageObservation(
2152
- packageName,
2153
- registry,
2154
- resolvedCwd,
2155
- ),
2156
- auth: liveNpmAuthObservation(registry, resolvedCwd),
2157
- trust: liveNpmTrustObservation(
2158
- packageName,
2159
- registry,
2160
- resolvedCwd,
2161
- expectedPublisher,
2162
- ),
2163
- };
2164
- const repositoryPermissions = offline
2165
- ? {
2166
- status: "unknown",
2167
- repository,
2168
- canWrite: null,
2169
- errorCode: "offline",
2170
- }
2171
- : liveRepositoryPermissionObservation(repository, resolvedCwd);
2172
- const repositoryActions = offline
2173
- ? {
2174
- status: "unknown",
2175
- defaultWorkflowPermissions: "",
2176
- canApprovePullRequestReviews: null,
2177
- errorCode: "offline",
2178
- }
2179
- : liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
2180
- const generatedWriteAuthority = offline
2181
- ? {
2182
- status: "unknown",
2183
- configured: null,
2184
- mode: "",
2185
- errorCode: "offline",
2186
- }
2187
- : liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
2188
- const toolchain = localToolchainObservation(
2189
- resolvedCwd,
2190
- configResult.loaded?.config?.publication,
2191
- );
2192
- const checks = [
2161
+ function paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }) {
2162
+ return [
2193
2163
  ...agentEntry.checks.map((entry) => ({
2194
2164
  ...entry,
2195
2165
  blocking: true,
@@ -2256,6 +2226,11 @@ export function collectPaperPreflight({
2256
2226
  ? `Buildchain runtime contract is ${lockEvaluation.status}.`
2257
2227
  : `Buildchain runtime contract is not admitted: ${(lockEvaluation.reasons || []).join("; ")}`,
2258
2228
  },
2229
+ ];
2230
+ }
2231
+
2232
+ function paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }) {
2233
+ return [
2259
2234
  {
2260
2235
  id: "repository.write-permission",
2261
2236
  status:
@@ -2309,6 +2284,11 @@ export function collectPaperPreflight({
2309
2284
  ? "No GitHub App or compatible narrow generated-write credential is configured."
2310
2285
  : `Generated-write authority is unknown (${generatedWriteAuthority.errorCode}).`,
2311
2286
  },
2287
+ ];
2288
+ }
2289
+
2290
+ function paperPreflightPublicationChecks({ npm, status }) {
2291
+ return [
2312
2292
  {
2313
2293
  id: "npm.package",
2314
2294
  status:
@@ -2365,24 +2345,19 @@ export function collectPaperPreflight({
2365
2345
  : `${status.conflicts.length} release-state conflict(s) require repair.`,
2366
2346
  },
2367
2347
  ];
2368
- const blockingChecks = checks.filter(
2369
- (entry) => entry.blocking && ["fail", "unknown"].includes(entry.status),
2370
- );
2371
- const externalMutationChecks = checks.filter(
2372
- (entry) =>
2373
- entry.scope === "external-mutation" &&
2374
- ["fail", "pending", "unknown"].includes(entry.status),
2375
- );
2376
- const nextActions = [];
2348
+ }
2349
+
2350
+ function paperPreflightNextActions({ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status }) {
2351
+ const actions = [];
2377
2352
  if (validationError) {
2378
- nextActions.push({
2353
+ actions.push({
2379
2354
  id: "repair-config",
2380
2355
  command: "buildchain validate --require-lifecycle-stages verify",
2381
2356
  description: validationError,
2382
2357
  });
2383
2358
  }
2384
2359
  if (!source.clean) {
2385
- nextActions.push({
2360
+ actions.push({
2386
2361
  id: "commit-source",
2387
2362
  command: "git status --short",
2388
2363
  description:
@@ -2390,7 +2365,7 @@ export function collectPaperPreflight({
2390
2365
  });
2391
2366
  }
2392
2367
  if (!lockEvaluation.compatible) {
2393
- nextActions.push({
2368
+ actions.push({
2394
2369
  id: "refresh-contract-lock",
2395
2370
  command: "buildchain paper scaffold --json",
2396
2371
  description:
@@ -2398,7 +2373,7 @@ export function collectPaperPreflight({
2398
2373
  });
2399
2374
  }
2400
2375
  if (npm.package.exists === false || npm.trust.configured === false) {
2401
- nextActions.push({
2376
+ actions.push({
2402
2377
  id: "bootstrap-npm",
2403
2378
  command: "buildchain paper bootstrap npm --json",
2404
2379
  description:
@@ -2410,7 +2385,7 @@ export function collectPaperPreflight({
2410
2385
  (repositoryActions.defaultWorkflowPermissions !== "read" ||
2411
2386
  repositoryActions.canApprovePullRequestReviews !== false)
2412
2387
  ) {
2413
- nextActions.push({
2388
+ actions.push({
2414
2389
  id: "constrain-repository-actions",
2415
2390
  command: "",
2416
2391
  description:
@@ -2418,7 +2393,7 @@ export function collectPaperPreflight({
2418
2393
  });
2419
2394
  }
2420
2395
  if (generatedWriteAuthority.configured === false) {
2421
- nextActions.push({
2396
+ actions.push({
2422
2397
  id: "configure-generated-write-authority",
2423
2398
  command: "",
2424
2399
  description:
@@ -2426,14 +2401,151 @@ export function collectPaperPreflight({
2426
2401
  });
2427
2402
  }
2428
2403
  if (status.deterministicBuild.status !== "qualifying") {
2429
- nextActions.push({
2404
+ actions.push({
2430
2405
  id: "build-paper",
2431
2406
  command: "buildchain paper build --execute --json",
2432
2407
  description: "Run the existing qualifying two-clean-build gate.",
2433
2408
  });
2434
2409
  }
2435
- return {
2436
- schemaVersion: 1,
2410
+ return actions;
2411
+ }
2412
+
2413
+ export function collectPaperPreflight({
2414
+ cwd = process.cwd(),
2415
+ buildchainRoot = process.cwd(),
2416
+ buildchainVersion = "",
2417
+ buildchainRef = "v3",
2418
+ buildchainSha = "",
2419
+ registry = NPM_REGISTRY,
2420
+ offline = false,
2421
+ agentEntryMode = "contract",
2422
+ } = {}) {
2423
+ const resolvedCwd = path.resolve(cwd);
2424
+ const status = collectPaperStatus({ cwd: resolvedCwd });
2425
+ const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2426
+ const configResult = paperConfig(resolvedCwd);
2427
+ let validation;
2428
+ let validationError = "";
2429
+ try {
2430
+ validation = validateBuildchainConfig(resolvedCwd, { requireLifecycleStages: ["verify"] });
2431
+ } catch (error) {
2432
+ validationError = error.message;
2433
+ }
2434
+ const runtime = runtimeFacts({
2435
+ buildchainRoot, buildchainVersion,
2436
+ buildchainRef: provisioning.value?.runtime?.ref || buildchainRef, buildchainSha,
2437
+ });
2438
+ const agentEntry = collectPaperAgentEntry({ cwd: resolvedCwd, buildchainSha: runtime.resolvedSha, mode: agentEntryMode });
2439
+ const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
2440
+ let lockEvaluation = {
2441
+ status: "missing-lock",
2442
+ compatible: false,
2443
+ drift: false,
2444
+ reasons: ["Buildchain contract lock is missing"],
2445
+ };
2446
+ try {
2447
+ const lock = readBuildchainContractLock(lockPath);
2448
+ if (lock) {
2449
+ const current = runtimeContractWorld(buildchainRoot);
2450
+ lockEvaluation = evaluateBuildchainContractLock({
2451
+ lock,
2452
+ current,
2453
+ runtimeRef: runtime.ref,
2454
+ runtimeSha: runtime.resolvedSha,
2455
+ runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
2456
+ });
2457
+ }
2458
+ } catch (error) {
2459
+ lockEvaluation = {
2460
+ status: "invalid-lock",
2461
+ compatible: false,
2462
+ drift: false,
2463
+ reasons: [error.message],
2464
+ };
2465
+ }
2466
+ const source = {
2467
+ repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
2468
+ head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
2469
+ tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
2470
+ branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
2471
+ clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
2472
+ };
2473
+ const packageName = status.identity.package;
2474
+ const repository = status.identity.repository;
2475
+ const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
2476
+ repository, workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
2477
+ });
2478
+ const npm =
2479
+ offline || !packageName
2480
+ ? {
2481
+ package: {
2482
+ status: "unknown",
2483
+ exists: null,
2484
+ version: "",
2485
+ registry,
2486
+ errorCode: offline ? "offline" : "package-unresolved",
2487
+ },
2488
+ auth: {
2489
+ status: "unknown",
2490
+ authenticated: null,
2491
+ identity: "",
2492
+ errorCode: offline ? "offline" : "package-unresolved",
2493
+ },
2494
+ trust: {
2495
+ status: "unknown",
2496
+ configured: null,
2497
+ publishers: [],
2498
+ errorCode: offline ? "offline" : "package-unresolved",
2499
+ },
2500
+ }
2501
+ : {
2502
+ package: liveNpmPackageObservation(packageName, registry, resolvedCwd),
2503
+ auth: liveNpmAuthObservation(registry, resolvedCwd),
2504
+ trust: liveNpmTrustObservation(packageName, registry, resolvedCwd, expectedPublisher),
2505
+ };
2506
+ const repositoryPermissions = offline
2507
+ ? {
2508
+ status: "unknown",
2509
+ repository,
2510
+ canWrite: null,
2511
+ errorCode: "offline",
2512
+ }
2513
+ : liveRepositoryPermissionObservation(repository, resolvedCwd);
2514
+ const repositoryActions = offline
2515
+ ? {
2516
+ status: "unknown",
2517
+ defaultWorkflowPermissions: "",
2518
+ canApprovePullRequestReviews: null,
2519
+ errorCode: "offline",
2520
+ }
2521
+ : liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
2522
+ const generatedWriteAuthority = offline
2523
+ ? {
2524
+ status: "unknown",
2525
+ configured: null,
2526
+ mode: "",
2527
+ errorCode: "offline",
2528
+ }
2529
+ : liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
2530
+ const toolchain = localToolchainObservation(resolvedCwd, configResult.loaded?.config?.publication);
2531
+ const checks = [
2532
+ ...paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }),
2533
+ ...paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }),
2534
+ ...paperPreflightPublicationChecks({ npm, status }),
2535
+ ];
2536
+ const blockingChecks = checks.filter(
2537
+ (entry) => entry.blocking && ["fail", "unknown"].includes(entry.status),
2538
+ );
2539
+ const externalMutationChecks = checks.filter(
2540
+ (entry) =>
2541
+ entry.scope === "external-mutation" &&
2542
+ ["fail", "pending", "unknown"].includes(entry.status),
2543
+ );
2544
+ const nextActions = paperPreflightNextActions({
2545
+ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status,
2546
+ });
2547
+ return {
2548
+ schemaVersion: 1,
2437
2549
  contract: PAPER_PREFLIGHT_CONTRACT,
2438
2550
  ok: blockingChecks.length === 0,
2439
2551
  localReady: blockingChecks.length === 0,
@@ -2725,467 +2837,30 @@ function bootstrapPackageShape({ packageName, version }) {
2725
2837
  };
2726
2838
  }
2727
2839
 
2728
- export function executePaperNpmBootstrap({
2729
- cwd = process.cwd(),
2730
- packageName = "",
2731
- bootstrapVersion = DEFAULT_BOOTSTRAP_VERSION,
2732
- registry = NPM_REGISTRY,
2733
- repository = "",
2734
- workflow = "paper-release.yml",
2735
- environment = "",
2736
- execute = false,
2737
- confirmedPackage = "",
2738
- userconfig = "",
2739
- offline = false,
2740
- } = {}) {
2741
- const resolvedCwd = path.resolve(cwd);
2742
- if (registry !== NPM_REGISTRY) {
2743
- throw new Error(
2744
- `paper npm bootstrap requires the official registry ${NPM_REGISTRY}`,
2745
- );
2746
- }
2747
- if (bootstrapVersion !== DEFAULT_BOOTSTRAP_VERSION) {
2748
- throw new Error(
2749
- `paper npm bootstrap version is fixed at ${DEFAULT_BOOTSTRAP_VERSION}`,
2750
- );
2751
- }
2752
- const configResult = paperConfig(resolvedCwd);
2753
- if (configResult.error) throw new Error(configResult.error);
2754
- const name = normalizePackageName(
2755
- packageName ||
2756
- configResult.loaded.config.publish?.package ||
2757
- configResult.loaded.config.publish?.mainPackage,
2758
- );
2759
- const repo = normalizeRepository(
2760
- repository || resolvePaperRepository(resolvedCwd),
2761
- );
2762
- const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
2763
- if (!provisioning.valid) {
2764
- throw new Error(
2765
- `paper npm bootstrap requires a valid provisioning authority: ${provisioning.errors.join("; ")}`,
2766
- );
2767
- }
2768
- const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value);
2769
- if (
2770
- provisioning.value.package?.name !== name ||
2771
- expectedPublisher.repository !== repo ||
2772
- expectedPublisher.workflow !== toPosix(workflow).replace(/^\/+/, "") ||
2773
- expectedPublisher.environment !== String(environment || "")
2774
- ) {
2775
- throw new Error(
2776
- "paper npm bootstrap coordinates differ from the exact provisioning authority",
2777
- );
2778
- }
2779
- if (execute && confirmedPackage !== name) {
2780
- throw new Error(
2781
- `real npm bootstrap requires --confirm-public-package ${name}`,
2782
- );
2783
- }
2784
- if (execute && offline) {
2785
- throw new Error("real npm bootstrap cannot run with --offline");
2786
- }
2787
- if (execute && (!repo || !workflow)) {
2788
- throw new Error(
2789
- "real npm bootstrap requires GitHub repository and workflow coordinates",
2790
- );
2791
- }
2792
- const packageObservation = offline
2793
- ? {
2794
- status: "unknown",
2795
- exists: null,
2796
- version: "",
2797
- registry,
2798
- errorCode: "offline",
2799
- }
2800
- : liveNpmPackageObservation(name, registry, resolvedCwd);
2801
- const auth = offline
2802
- ? {
2803
- status: "unknown",
2804
- authenticated: null,
2805
- identity: "",
2806
- errorCode: "offline",
2807
- }
2808
- : liveNpmAuthObservation(registry, resolvedCwd);
2809
- const plan = {
2810
- schemaVersion: 1,
2811
- contract: PAPER_NPM_BOOTSTRAP_CONTRACT,
2812
- ok: true,
2813
- dryRun: !execute,
2814
- externalMutation: execute,
2815
- package: {
2816
- name,
2817
- bootstrapVersion,
2818
- registry,
2819
- existsBefore: packageObservation.exists,
2820
- observedVersion: packageObservation.version,
2821
- },
2822
- repository: {
2823
- github: repo,
2824
- workflow,
2825
- environment,
2826
- },
2827
- authority: {
2828
- digest: provisioning.value.authorityDigest,
2829
- policyDigest: provisioning.value.policy.policyDigest,
2830
- },
2831
- auth: {
2832
- authenticated: auth.authenticated,
2833
- identity: auth.identity,
2834
- errorCode: auth.errorCode,
2835
- },
2836
- publish: {
2837
- status: packageObservation.exists === true ? "existing" : "planned",
2838
- distTag: "bootstrap",
2839
- access: "public",
2840
- },
2841
- trust: {
2842
- status: "planned",
2843
- urls: [],
2844
- },
2845
- receipts: [],
2846
- nextActions: execute
2847
- ? []
2848
- : [
2849
- {
2850
- id: "confirm-public-bootstrap",
2851
- command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
2852
- description:
2853
- "After reviewing this dry-run, bootstrap the exact public package and configure GitHub Trusted Publishing.",
2854
- },
2855
- ],
2856
- };
2857
- if (!execute) {
2858
- const dryRunRoot = fs.mkdtempSync(
2859
- path.join(os.tmpdir(), "buildchain-paper-bootstrap-dry-run-"),
2860
- );
2861
- try {
2862
- fs.writeFileSync(
2863
- path.join(dryRunRoot, "package.json"),
2864
- jsonText(
2865
- bootstrapPackageShape({
2866
- packageName: name,
2867
- version: bootstrapVersion,
2868
- }),
2869
- ),
2870
- );
2871
- const configArgs = userconfig ? ["--userconfig", userconfig] : [];
2872
- const pack = commandResult(
2873
- "npm",
2874
- [
2875
- "pack",
2876
- "--dry-run",
2877
- "--json",
2878
- "--ignore-scripts",
2879
- `--registry=${registry}`,
2880
- ...configArgs,
2881
- ],
2882
- { cwd: dryRunRoot },
2883
- );
2884
- const publish = commandResult(
2885
- "npm",
2886
- [
2887
- "publish",
2888
- "--dry-run",
2889
- "--ignore-scripts",
2890
- "--access",
2891
- "public",
2892
- "--tag",
2893
- "bootstrap",
2894
- `--registry=${registry}`,
2895
- ...configArgs,
2896
- ],
2897
- { cwd: dryRunRoot },
2898
- );
2899
- return {
2900
- ...plan,
2901
- ok: pack.ok && publish.ok,
2902
- errorCode: !pack.ok
2903
- ? "npm-pack-dry-run-failed"
2904
- : !publish.ok
2905
- ? "npm-publish-dry-run-failed"
2906
- : "",
2907
- dryRunChecks: {
2908
- minimalPackageOnly: true,
2909
- pack: {
2910
- status: pack.ok ? "pass" : "fail",
2911
- entryCount: (() => {
2912
- const parsed = safeParseJson(pack.stdout);
2913
- const value = Array.isArray(parsed) ? parsed[0] : parsed;
2914
- return Array.isArray(value?.files) ? value.files.length : 0;
2915
- })(),
2916
- },
2917
- publish: {
2918
- status: publish.ok ? "pass" : "fail",
2919
- registry,
2920
- access: "public",
2921
- distTag: "bootstrap",
2922
- },
2923
- },
2924
- };
2925
- } finally {
2926
- fs.rmSync(dryRunRoot, { recursive: true, force: true });
2927
- }
2928
- }
2929
- if (packageObservation.exists === null) {
2930
- return {
2931
- ...plan,
2932
- ok: false,
2933
- errorCode: "npm-package-status-unknown",
2934
- publish: {
2935
- ...plan.publish,
2936
- status: "blocked",
2937
- },
2938
- trust: {
2939
- status: "blocked",
2940
- urls: [],
2941
- },
2942
- nextActions: [
2943
- {
2944
- id: "verify-npm-package",
2945
- command: `npm view ${name} version --json --registry=${registry}`,
2946
- description:
2947
- "Resolve whether the exact public package exists before any publish mutation.",
2948
- },
2949
- ],
2950
- };
2951
- }
2952
- if (auth.authenticated !== true) {
2953
- return {
2954
- ...plan,
2955
- ok: false,
2956
- errorCode: "npm-auth-required",
2957
- publish: {
2958
- ...plan.publish,
2959
- status: "blocked",
2960
- },
2961
- trust: {
2962
- status: "blocked",
2963
- urls: [],
2964
- },
2965
- nextActions: [
2966
- {
2967
- id: "authenticate-npm",
2968
- command: `npm whoami --registry=${registry}`,
2969
- description:
2970
- "Authenticate npm without sharing token contents, then rerun the exact bootstrap command.",
2971
- },
2972
- ],
2973
- };
2974
- }
2975
- const tempRoot = fs.mkdtempSync(
2976
- path.join(os.tmpdir(), "buildchain-paper-bootstrap-"),
2977
- );
2978
- try {
2979
- fs.writeFileSync(
2980
- path.join(tempRoot, "package.json"),
2981
- jsonText(
2982
- bootstrapPackageShape({ packageName: name, version: bootstrapVersion }),
2983
- ),
2984
- );
2985
- const configArgs = userconfig ? ["--userconfig", userconfig] : [];
2986
- const pack = commandResult(
2987
- "npm",
2988
- [
2989
- "pack",
2990
- "--dry-run",
2991
- "--json",
2992
- "--ignore-scripts",
2993
- `--registry=${registry}`,
2994
- ...configArgs,
2995
- ],
2996
- { cwd: tempRoot },
2997
- );
2998
- if (!pack.ok) {
2999
- return {
3000
- ...plan,
3001
- ok: false,
3002
- errorCode: "npm-pack-dry-run-failed",
3003
- publish: { ...plan.publish, status: "blocked" },
3004
- trust: { status: "blocked", urls: [] },
3005
- };
3006
- }
3007
- const publishDryRun = commandResult(
3008
- "npm",
3009
- [
3010
- "publish",
3011
- "--dry-run",
3012
- "--ignore-scripts",
3013
- "--access",
3014
- "public",
3015
- "--tag",
3016
- "bootstrap",
3017
- `--registry=${registry}`,
3018
- ...configArgs,
3019
- ],
3020
- { cwd: tempRoot },
3021
- );
3022
- if (!publishDryRun.ok) {
3023
- return {
3024
- ...plan,
3025
- ok: false,
3026
- errorCode: "npm-publish-dry-run-failed",
3027
- publish: { ...plan.publish, status: "blocked" },
3028
- trust: { status: "blocked", urls: [] },
3029
- };
3030
- }
3031
- let publishStatus =
3032
- packageObservation.exists === true ? "existing" : "published";
3033
- if (packageObservation.exists !== true) {
3034
- const published = commandResult(
3035
- "npm",
3036
- [
3037
- "publish",
3038
- "--ignore-scripts",
3039
- "--access",
3040
- "public",
3041
- "--tag",
3042
- "bootstrap",
3043
- `--registry=${registry}`,
3044
- ...configArgs,
3045
- ],
3046
- { cwd: tempRoot, timeout: 60000 },
3047
- );
3048
- if (!published.ok) {
3049
- const failure = {
3050
- ...plan,
3051
- ok: false,
3052
- errorCode: "npm-publish-failed",
3053
- publish: { ...plan.publish, status: "failed" },
3054
- trust: { status: "blocked", urls: [] },
3055
- };
3056
- failure.receipts = [
3057
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, failure),
3058
- ];
3059
- return failure;
3060
- }
3061
- }
3062
- const trustArgs = [
3063
- "trust",
3064
- "github",
3065
- name,
3066
- "--repo",
3067
- repo,
3068
- "--file",
3069
- workflow,
3070
- ...(environment ? ["--env", environment] : []),
3071
- "--allow-publish",
3072
- "--yes",
3073
- "--json",
3074
- `--registry=${registry}`,
3075
- ...configArgs,
3076
- ];
3077
- const trustCommand = commandResult("npm", trustArgs, {
3078
- cwd: resolvedCwd,
3079
- timeout: 60000,
3080
- });
3081
- const parsedTrust = safeParseJson(trustCommand.stdout);
3082
- const urls = [
3083
- ...extractUrls(
3084
- parsedTrust || `${trustCommand.stdout}\n${trustCommand.stderr}`,
3085
- ),
3086
- ];
3087
- const trustList =
3088
- trustCommand.ok && urls.length === 0
3089
- ? liveNpmTrustObservation(
3090
- name,
3091
- registry,
3092
- resolvedCwd,
3093
- expectedPublisher,
3094
- )
3095
- : {
3096
- status: "unknown",
3097
- configured: null,
3098
- publishers: [],
3099
- errorCode:
3100
- urls.length > 0 ? "web-action-required" : "npm-trust-failed",
3101
- };
3102
- const trustStatus =
3103
- trustCommand.ok && trustList.configured === true
3104
- ? "configured"
3105
- : urls.length > 0
3106
- ? "action-required"
3107
- : "failed";
3108
- const packageAfter = liveNpmPackageObservation(name, registry, resolvedCwd);
3109
- const packageVerified = packageAfter.exists === true;
3110
- const receipt = {
3111
- ...plan,
3112
- ok: trustStatus !== "failed" && packageVerified,
3113
- dryRun: false,
3114
- externalMutation: true,
3115
- errorCode:
3116
- trustStatus === "failed"
3117
- ? "npm-trust-failed"
3118
- : packageVerified
3119
- ? ""
3120
- : "npm-package-readback-failed",
3121
- package: {
3122
- ...plan.package,
3123
- existsAfter: packageAfter.exists,
3124
- observedVersionAfter: packageAfter.version,
3125
- readbackStatus: packageAfter.status,
3126
- },
3127
- publish: {
3128
- ...plan.publish,
3129
- status: publishStatus,
3130
- },
3131
- trust: {
3132
- status: trustStatus,
3133
- urls,
3134
- expectedPublisher,
3135
- exactBinding: trustList.exactBinding === true,
3136
- publishers: trustList.publishers,
3137
- },
3138
- nextActions:
3139
- trustStatus === "configured" && packageVerified
3140
- ? [
3141
- {
3142
- id: "paper-preflight",
3143
- command: "buildchain paper preflight --json",
3144
- description:
3145
- "Verify package existence and Trusted Publisher binding from live read-only sources.",
3146
- },
3147
- ]
3148
- : trustStatus === "action-required" && packageVerified
3149
- ? [
3150
- {
3151
- id: "complete-npm-web-action",
3152
- command: "",
3153
- description:
3154
- "Open the exact npm URL returned below, complete the web step, then rerun preflight.",
3155
- urls,
3156
- },
3157
- ]
3158
- : !packageVerified
3159
- ? [
3160
- {
3161
- id: "verify-npm-package-readback",
3162
- command: `npm view ${name} version --json --registry=${registry}`,
3163
- description:
3164
- "The mutation returned but the official registry did not prove package existence; do not infer success.",
3165
- },
3166
- ]
3167
- : [
3168
- {
3169
- id: "retry-npm-trust",
3170
- command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
3171
- description:
3172
- "The package exists; retry only the idempotent Trusted Publisher configuration path.",
3173
- },
3174
- ],
3175
- };
3176
- receipt.receipts = [
3177
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, receipt),
3178
- ];
3179
- if (
3180
- packageVerified &&
3181
- (trustStatus === "configured" || trustStatus === "action-required")
3182
- ) {
3183
- receipt.receipts.push(
3184
- writePaperReceipt(resolvedCwd, PAPER_PATHS.npmTrust, receipt),
3185
- );
3186
- }
3187
- return receipt;
3188
- } finally {
3189
- fs.rmSync(tempRoot, { recursive: true, force: true });
3190
- }
2840
+ export function executePaperNpmBootstrap(options = {}) {
2841
+ return executePaperNpmBootstrapOperation(options, {
2842
+ DEFAULT_BOOTSTRAP_VERSION,
2843
+ NPM_REGISTRY,
2844
+ PAPER_NPM_BOOTSTRAP_CONTRACT,
2845
+ PAPER_PATHS,
2846
+ bootstrapPackageShape,
2847
+ commandResult,
2848
+ expectedPaperTrustedPublisher,
2849
+ extractUrls,
2850
+ fs,
2851
+ jsonText,
2852
+ liveNpmAuthObservation,
2853
+ liveNpmPackageObservation,
2854
+ liveNpmTrustObservation,
2855
+ normalizePackageName,
2856
+ normalizeRepository,
2857
+ os,
2858
+ paperConfig,
2859
+ path,
2860
+ resolvePaperRepository,
2861
+ safeParseJson,
2862
+ toPosix,
2863
+ validatePaperProvisioningAuthority,
2864
+ writePaperReceipt,
2865
+ });
3191
2866
  }