test: add P1 manual configuration walkthrough
This commit is contained in:
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env node
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { closeSync, constants, fsyncSync, lstatSync, openSync, realpathSync } from "node:fs";
|
||||
import { chmod, lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
|
||||
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// This acceptance-only adapter deliberately imports the built production runner.
|
||||
import { ThtRunner } from "../dist/tht/tht-runner.js";
|
||||
|
||||
const modulePath = fileURLToPath(import.meta.url);
|
||||
const defaultRepositoryRoot = realpathSync(resolve(dirname(modulePath), "../.."));
|
||||
const HEX40 = /^[0-9a-f]{40}$/;
|
||||
const HEX64 = /^[0-9a-f]{64}$/;
|
||||
|
||||
function fixedRoot(repositoryRoot) { return join(realpathSync(repositoryRoot), ".artifacts", "manual-acceptance", "p1"); }
|
||||
function below(parent, child) { const rel = relative(parent, child); return rel !== "" && !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel); }
|
||||
function assertNoSymlinks(root, path, allowMissingLeaf = false) {
|
||||
const rel = relative(root, path);
|
||||
if (rel.startsWith("..") || isAbsolute(rel)) throw new Error("path is outside owned root");
|
||||
let cursor = root;
|
||||
for (const [index, part] of rel.split(sep).filter(Boolean).entries()) {
|
||||
cursor = join(cursor, part);
|
||||
try { if (lstatSync(cursor).isSymbolicLink()) throw new Error("owned path contains a symlink"); }
|
||||
catch (error) {
|
||||
if (allowMissingLeaf && error.code === "ENOENT" && index === rel.split(sep).filter(Boolean).length - 1) return;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
async function ownership(repositoryRoot, ownershipPath) {
|
||||
const root = fixedRoot(repositoryRoot);
|
||||
const expected = join(root, "ownership.json");
|
||||
if (resolve(ownershipPath) !== expected) throw new Error("ownership path is not owned");
|
||||
const rootEntry = await lstat(root); const ownershipEntry = await lstat(expected);
|
||||
if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink() || !ownershipEntry.isFile() || ownershipEntry.isSymbolicLink()) throw new Error("ownership is unsafe");
|
||||
if (await realpath(root) !== root) throw new Error("ownership root is not canonical");
|
||||
let value; try { value = JSON.parse(await readFile(expected, "utf8")); } catch { throw new Error("ownership is malformed"); }
|
||||
if (value?.schemaVersion !== 1 || value.kind !== "p1-manual-acceptance" || !HEX64.test(value.nonce ?? "")
|
||||
|| value.repositoryRoot !== realpathSync(repositoryRoot) || value.root !== root || value.status !== "PENDING"
|
||||
|| value.listener?.host !== "127.0.0.1" || value.listener?.port !== 8791) throw new Error("ownership identity mismatch");
|
||||
return { root, value };
|
||||
}
|
||||
async function atomicCopy(source, output) {
|
||||
const staging = join(dirname(output), `.${basename(output)}.${randomBytes(12).toString("hex")}.tmp`);
|
||||
let handle;
|
||||
try {
|
||||
const bytes = await readFile(source);
|
||||
handle = await open(staging, "wx", 0o600); await handle.writeFile(bytes); await handle.sync(); await handle.close(); handle = undefined;
|
||||
await chmod(staging, 0o600); await rename(staging, output);
|
||||
const directory = openSync(dirname(output), constants.O_RDONLY); try { fsyncSync(directory); } finally { closeSync(directory); }
|
||||
} finally { if (handle) await handle.close().catch(() => {}); await rm(staging, { force: true }).catch(() => {}); }
|
||||
}
|
||||
|
||||
export async function renderOwnedSnapshot({ repositoryRoot = defaultRepositoryRoot, ownershipPath, snapshotPath, outputPath, env = process.env }) {
|
||||
const repo = realpathSync(repositoryRoot); const { root } = await ownership(repo, resolve(repo, ownershipPath));
|
||||
const snapshot = resolve(repo, snapshotPath); const output = resolve(repo, outputPath);
|
||||
const snapshotsRoot = join(root, "installation", "registry", "snapshots");
|
||||
const renderedRoot = join(root, "rendered");
|
||||
if (!isAbsolute(snapshotPath) || !below(snapshotsRoot, snapshot)) throw new Error("snapshot is not an owned absolute path");
|
||||
const match = /^([0-9a-f]{40})\/([a-z][a-z0-9-]{2,62})\.yaml$/.exec(relative(snapshotsRoot, snapshot).split(sep).join("/"));
|
||||
if (!match || !HEX40.test(match[1])) throw new Error("snapshot is not commit addressed");
|
||||
assertNoSymlinks(root, snapshot); const snapshotEntry = await lstat(snapshot);
|
||||
if (!snapshotEntry.isFile() || snapshotEntry.isSymbolicLink() || await realpath(snapshot) !== snapshot) throw new Error("snapshot is unsafe");
|
||||
if (!below(renderedRoot, output) || dirname(output) !== renderedRoot || !output.endsWith(".yaml")) throw new Error("output is not an owned rendered path");
|
||||
assertNoSymlinks(root, dirname(output));
|
||||
try { if ((await lstat(output)).isSymbolicLink()) throw new Error("output is unsafe"); } catch (error) { if (error.code !== "ENOENT") throw error; }
|
||||
await mkdir(join(snapshotsRoot, "runtime"), { recursive: true, mode: 0o700 });
|
||||
const prior = {};
|
||||
for (const [key, value] of Object.entries(env)) { prior[key] = process.env[key]; if (value === undefined) delete process.env[key]; else process.env[key] = value; }
|
||||
const runner = new ThtRunner({
|
||||
thtBin: join(repo, "harness", ".venv", "bin", "tht"), harnessDir: join(repo, "harness"),
|
||||
configPath: join(root, "installation", "base.yaml"), dataRoot: join(root, "installation", "data"),
|
||||
runtimeSnapshotRoot: join(snapshotsRoot, "runtime"), secretRoots: [join(root, "fixture-secrets")],
|
||||
semanticRuntime: { internalQdrantUrl: "http://qdrant:6333", internalEmbeddingUrl: "http://embedding:11434", internalEmbeddingModel: "qwen3-embedding:0.6b", internalEmbeddingDimensions: 1024 },
|
||||
});
|
||||
let lease;
|
||||
try { lease = runner.acquireWorkspaceRuntime(snapshot); await atomicCopy(lease.path, output); }
|
||||
finally {
|
||||
if (lease) lease.release();
|
||||
for (const key of Object.keys(env)) { if (prior[key] === undefined) delete process.env[key]; else process.env[key] = prior[key]; }
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function parseArgs(argv) {
|
||||
if (argv.length !== 6) throw new Error("usage: p1-render-snapshot.mjs --ownership PATH --snapshot ABSOLUTE_PATH --output PATH");
|
||||
const result = {}; for (let i=0;i<argv.length;i+=2) { if (!["--ownership","--snapshot","--output"].includes(argv[i]) || result[argv[i]]) throw new Error("invalid arguments"); result[argv[i]]=argv[i+1]; }
|
||||
if (!result["--ownership"] || !result["--snapshot"] || !result["--output"]) throw new Error("missing arguments"); return result;
|
||||
}
|
||||
if (process.argv[1] && realpathSync(process.argv[1]) === modulePath) {
|
||||
try { const args=parseArgs(process.argv.slice(2)); await renderOwnedSnapshot({ ownershipPath:args["--ownership"], snapshotPath:args["--snapshot"], outputPath:args["--output"] }); console.log(`rendered ${resolve(args["--output"])}`); }
|
||||
catch(error) { console.error(`p1 render refused: ${error.message}`); process.exitCode=1; }
|
||||
}
|
||||
Reference in New Issue
Block a user