#!/usr/bin/env node import { createHash } from "node:crypto"; import { spawnSync } from "node:child_process"; import { copyFile, cp, lstat, mkdir, mkdtemp, readFile, rm, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const here = dirname(fileURLToPath(import.meta.url)); const platformRoot = resolve(here, "../.."); const engineRoot = resolve( process.env.NODEDC_ENGINE_SOURCE_ROOT || resolve(platformRoot, "../NODEDC_ENGINE_INFRA"), ); const artifactRoot = resolve( process.env.NODEDC_DEPLOY_ARTIFACT_DIR || resolve(here, "../deploy-artifacts"), ); const baselineArtifact = resolve( here, "../deploy-artifacts/nodedc-engine-provider-authority-diagnostics-20260723-029.tgz", ); const baselineArtifactSha256 = "b4a0a1f707bf9814a4decc3f7f261b8afffe5727af2129c14bcdc76f81675e38"; const descriptorRel = "nodedc-source/services/node-intelligence/activation.json"; const baselineDescriptorSha256 = "84b0e15a10cedf334ad04d6f31c908da2dc155503c9974f0eeb75b01e20fb884"; const predecessorGatewaySha256 = "5331f6dc8dc306f641370a2968eb025ee3e278e27ae9a217e4cfc2901fec369c"; const targetGatewaySha256 = "4f600a2781be9118bec891fa2a6f20d7f55e0892ef2f06af24059193cebd28f4"; const targetDescriptorSha256 = "63e7619c6971d02102583bb5d80d33ece293b952f113200fa0b76f0e88c2dd32"; const [patchId = "", ...extra] = process.argv.slice(2); if (extra.length || !/^engine-l2-closed-loop-\d{8}-\d{3}$/.test(patchId)) { throw new Error( "usage: build-engine-l2-closed-loop-artifact.mjs " + "", ); } const sourceCommit = "dda405cff27977622af0c218abb4d4420c408536"; const entries = Object.freeze([ "nodedc-source/server/index.js", "nodedc-source/server/l2/graphRepository.js", "nodedc-source/server/realtime/ws.js", "nodedc-source/server/routes/engineAgentGateway.js", "nodedc-source/server/routes/n8n.js", "nodedc-source/server/routes/ndcAgentMcp.js", "nodedc-source/src/App.tsx", "nodedc-source/src/n8n/N8nSubworkflowHost.tsx", "nodedc-source/src/realtime/useMultiplayer.ts", "nodedc-source/src/utils/n8nApi.ts", "nodedc-source/dist/index.html", "nodedc-source/dist/assets", descriptorRel, ]); const targetSha256 = Object.freeze({ "nodedc-source/server/index.js": "1896e1cade61579863c50ff3f52f2e81ff27f2a511a9d362db81925ee21cefad", "nodedc-source/server/l2/graphRepository.js": "94ad08e1bb7e7be04854f1f911e06ee1acd6e09631f33f4c01e42e230665ac33", "nodedc-source/server/realtime/ws.js": "82cfa833e05c2fc6d6049dd4564af06164b5c784fdfb82c243ca67519f4509c2", "nodedc-source/server/routes/engineAgentGateway.js": "4f600a2781be9118bec891fa2a6f20d7f55e0892ef2f06af24059193cebd28f4", "nodedc-source/server/routes/n8n.js": "752cb1524160adc1c95163c34e139b615c063d2ce8980f2a63879bddbd0f1e08", "nodedc-source/server/routes/ndcAgentMcp.js": "353a10291ceb93c3641ece60ec6e809a394be86f5ceb97cb44755178940409dd", "nodedc-source/src/App.tsx": "b3e61a89330b774f309660208d05143d299ab582f18765c1c14e2122a62c4d5e", "nodedc-source/src/n8n/N8nSubworkflowHost.tsx": "683aa44ba92aeac4879889e2bdb5319282976d7680d620701578830ce9677087", "nodedc-source/src/realtime/useMultiplayer.ts": "a4d001d9914098e50a78d8abe0a66344d23680b7a19b3573aa7b6f48c8bb9c35", "nodedc-source/src/utils/n8nApi.ts": "6f16d4992c51ffcc1e71a7bd172fd9ceb440d6e1a74d69ef1db62e0ac4230b3c", "nodedc-source/dist/index.html": "90d72f8790dc8cf951066b1210447c84d640373117bae23f3a4cb3227fb96d6d", "nodedc-source/dist/assets/index-Bim2pv1P.css": "18322addd45c126a7b8396f36b005f3085fddba3e9b346dd2c910f6fa6987ebf", "nodedc-source/dist/assets/index-CqvJfRRS.js": "06e6c23b03ea2ba8a4890e1f1714fd08ebaf18d735834200af6ab430af360ca8", [descriptorRel]: targetDescriptorSha256, }); const artifact = join(artifactRoot, `nodedc-${patchId}.tgz`); await assertFresh(artifact); assertSourceCommit(); await assertExactSources(); const targetDescriptor = await buildTargetDescriptor(); const stage = await mkdtemp(join(tmpdir(), "nodedc-engine-l2-closed-loop-")); try { const payload = join(stage, "payload"); for (const relativePath of entries) { const destination = join(payload, relativePath); await mkdir(dirname(destination), { recursive: true }); if (relativePath === descriptorRel) { await writeFile(destination, targetDescriptor, { encoding: "utf8", flag: "wx", mode: 0o644, }); continue; } const source = join(engineRoot, relativePath); const info = await lstat(source); if (info.isSymbolicLink()) { throw new Error(`engine_l2_closed_loop_source_symlink:${relativePath}`); } if (info.isDirectory()) { await cp(source, destination, { recursive: true, force: false, errorOnExist: true, }); } else if (info.isFile()) { await copyFile(source, destination); } else { throw new Error(`engine_l2_closed_loop_source_unsafe:${relativePath}`); } } await writeFile( join(stage, "manifest.env"), `id=${patchId}\ncomponent=engine\ntype=app-overlay\n`, { encoding: "utf8", flag: "wx", mode: 0o644 }, ); await writeFile(join(stage, "files.txt"), `${entries.join("\n")}\n`, { encoding: "utf8", flag: "wx", mode: 0o644, }); await mkdir(artifactRoot, { recursive: true }); run("python3", ["-c", canonicalTarScript(), artifact, stage]); console.log(JSON.stringify({ ok: true, patchId, artifact, sha256: digest(await readFile(artifact)), sourceCommit, entries, targetSha256, services: ["nodedc-backend", "app"], healthchecks: [ "http://127.0.0.1:8080/", "http://127.0.0.1:3001/health", ], mcpVersion: "0.7.0", graphContract: "semantic-revision-cas-v1", transition: "failed-030-partial-source-reconciliation", predecessorDescriptorSha256: baselineDescriptorSha256, targetDescriptorSha256, predecessorGatewaySha256, targetGatewaySha256, recoveryArtifact: "nodedc-engine-l2-closed-loop-20260723-030.tgz", actorPlanes: ["external_codex_mcp", "ai_workspace", "engine_ui"], untouched: [ "L2 graph data", "credentials", "databases", "n8n runtime", "AI Workspace connector", ], }, null, 2)); } finally { await rm(stage, { recursive: true, force: true }); } function assertSourceCommit() { const actual = run("git", ["-C", engineRoot, "rev-parse", "HEAD"]).stdout.trim(); if (actual !== sourceCommit) { throw new Error( `engine_l2_closed_loop_source_commit_mismatch:expected=${sourceCommit}:actual=${actual}`, ); } } async function assertExactSources() { const sourceEntries = entries.filter((entry) => entry !== descriptorRel); const expectedPaths = Object.keys(targetSha256) .filter((entry) => entry !== descriptorRel) .sort(); const actualPaths = []; for (const entry of sourceEntries) { const source = join(engineRoot, entry); const info = await lstat(source); if (info.isSymbolicLink() || (!info.isFile() && !info.isDirectory())) { throw new Error(`engine_l2_closed_loop_source_unsafe:${entry}`); } if (info.isDirectory()) { const result = run("find", [source, "-type", "f", "-print"]); for (const path of result.stdout.split("\n").filter(Boolean)) { actualPaths.push(path.slice(engineRoot.length + 1)); } } else { actualPaths.push(entry); } } actualPaths.sort(); if (JSON.stringify(actualPaths) !== JSON.stringify(expectedPaths)) { throw new Error( `engine_l2_closed_loop_source_set_mismatch:` + `expected=${expectedPaths.join(",")}:actual=${actualPaths.join(",")}`, ); } for (const relativePath of expectedPaths) { const actual = digest(await readFile(join(engineRoot, relativePath))); if (actual !== targetSha256[relativePath]) { throw new Error( `engine_l2_closed_loop_target_mismatch:${relativePath}:` + `expected=${targetSha256[relativePath]}:actual=${actual}`, ); } } } async function buildTargetDescriptor() { const baselineBytes = await readFile(baselineArtifact); if (digest(baselineBytes) !== baselineArtifactSha256) { throw new Error("engine_l2_closed_loop_baseline_artifact_mismatch"); } const baseline = run("tar", [ "-xOf", baselineArtifact, `payload/${descriptorRel}`, ]).stdout; if (digest(Buffer.from(baseline, "utf8")) !== baselineDescriptorSha256) { throw new Error("engine_l2_closed_loop_baseline_descriptor_mismatch"); } const descriptor = JSON.parse(baseline); if ( descriptor?.schemaVersion !== "nodedc.engine-node-intelligence-transition/v1" || descriptor?.action !== "activate" || descriptor?.releaseId !== "2.33.2-974a9fb3492f" || descriptor?.source?.gatewaySha256 !== predecessorGatewaySha256 || descriptor?.source?.upstreamProjectionSha256 !== "2dfa6b4f37d9bfa8b92a8109d4060d02dd2634ceb8f7924504b83ccf3fdd1523" ) { throw new Error("engine_l2_closed_loop_baseline_descriptor_contract_mismatch"); } descriptor.source.gatewaySha256 = targetGatewaySha256; const rendered = `${JSON.stringify(descriptor, null, 2)}\n`; if (digest(Buffer.from(rendered, "utf8")) !== targetDescriptorSha256) { throw new Error("engine_l2_closed_loop_target_descriptor_mismatch"); } return rendered; } async function assertFresh(path) { try { await lstat(path); } catch (error) { if (error?.code === "ENOENT") return; throw error; } throw new Error("artifact_already_exists"); } function canonicalTarScript() { return [ "import gzip,io,pathlib,sys,tarfile", "root=pathlib.Path(sys.argv[2])", "with open(sys.argv[1],'xb') as out:", " with gzip.GzipFile(filename='',mode='wb',fileobj=out,compresslevel=9,mtime=0) as gz:", " with tarfile.open(fileobj=gz,mode='w',format=tarfile.PAX_FORMAT) as tar:", " for top in ('manifest.env','files.txt','payload'):", " p=root/top; paths=[p]+(sorted(p.rglob('*')) if p.is_dir() else [])", " for x in paths:", " info=tar.gettarinfo(str(x),arcname=x.relative_to(root).as_posix())", " info.uid=info.gid=0; info.uname=info.gname='root'; info.mtime=0; info.mode=0o755 if info.isdir() else 0o644", " with (open(x,'rb') if info.isfile() else io.BytesIO()) as src: tar.addfile(info,src if info.isfile() else None)", ].join("\n"); } function digest(value) { return createHash("sha256").update(value).digest("hex"); } function run(command, args) { const result = spawnSync(command, args, { encoding: "utf8", maxBuffer: 128 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"], }); if (result.status !== 0) { throw new Error(`${command}_failed:${result.stderr || result.stdout}`); } return result; }