mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-06 14:02:36 +01:00
43 lines
3.9 KiB
TypeScript
43 lines
3.9 KiB
TypeScript
import { chatPrivateInputRequirements, chatPrivateInputNameIdentity } from "./chat-private-inputs.js";
|
|
import { createHash } from "node:crypto";
|
|
import { readFileSync, realpathSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { findPackageJSON } from "node:module";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import { fingerprintChatDependencyClosure } from "./chat-catalog-fingerprint.js";
|
|
import type { ChatCatalogDescriptor } from "./chat-catalog-registry.js";
|
|
import type { MatrixExecution } from "./types.js";
|
|
|
|
/** Independent boundary lane: never relax assertChatCatalogFingerprintLane. */
|
|
export function chatBoundaryFingerprint(descriptor: ChatCatalogDescriptor, execution: MatrixExecution, env: NodeJS.ProcessEnv = process.env, retainedPrivateInputNames?: ReturnType<typeof chatPrivateInputNameIdentity>) {
|
|
if (descriptor.journeyKind !== "browser-boundary" || execution.environment.id !== "local" || execution.suite.id !== "chat-features")
|
|
throw new Error("Boundary fingerprint requires an explicitly selected local browser boundary");
|
|
const privateInputNameIdentity = retainedPrivateInputNames === undefined ? chatPrivateInputNameIdentity(execution.id, env) : retainedPrivateInputNames;
|
|
let installedIdentity: unknown = null;
|
|
if (descriptor.boundaryTarget === "empty-installed-target") {
|
|
const cli = env.PAPERCLIP_CHAT_BOUNDARY_CLI; const expectedCli = env.PAPERCLIP_CHAT_BOUNDARY_CLI_SHA256;
|
|
const expectedServer = env.PAPERCLIP_CHAT_BOUNDARY_SERVER_SHA256;
|
|
if (!cli || !path.isAbsolute(cli) || !expectedCli || !expectedServer) throw new Error("Empty-installation boundary requires actual immutable installed CLI and server identity pins");
|
|
if (realpathSync(cli) !== cli) throw new Error("Boundary installed CLI must be canonical and unlinked");
|
|
const cliManifest = JSON.parse(readFileSync(path.resolve(path.dirname(cli), "../package.json"), "utf8"));
|
|
const serverManifestPath = findPackageJSON("@paperclipai/server", pathToFileURL(cli));
|
|
if (!serverManifestPath) throw new Error("Boundary CLI has no resolvable installed server");
|
|
const serverManifest = JSON.parse(readFileSync(serverManifestPath, "utf8"));
|
|
const serverEntry = path.join(path.dirname(serverManifestPath), "dist/index.js");
|
|
const digest = (name: string) => createHash("sha256").update(readFileSync(name)).digest("hex");
|
|
const cliSha256 = digest(cli); const serverSha256 = digest(serverEntry);
|
|
if (cliManifest.name !== "paperclipai" || serverManifest.name !== "@paperclipai/server" || cliManifest.version !== serverManifest.version
|
|
|| cliSha256 !== expectedCli || serverSha256 !== expectedServer) throw new Error("Boundary installed CLI/server differ from their declared pins");
|
|
installedIdentity = { version: cliManifest.version, cliSha256, serverSha256 };
|
|
} else if (descriptor.boundaryTarget !== "authenticated-harness") throw new Error("Boundary target identity is undeclared");
|
|
const root = fileURLToPath(new URL("../../", import.meta.url));
|
|
const result = fingerprintChatDependencyClosure({ root,
|
|
entries: [`tests/runner-e2e/${descriptor.modulePath.slice(2)}`, "tests/runner-e2e/chat-boundary-flow.ts", "tests/runner-e2e/chat-boundary-fingerprint.ts",
|
|
"tests/runner-e2e/runner.spec.ts", "tests/runner-e2e/launch.ts", ...descriptor.sourceFiles.map(file => `tests/runner-e2e/${file}`)],
|
|
selected: { descriptor, journeyKind: "browser-boundary", executionId: execution.id, privateInputRequirements: chatPrivateInputRequirements(descriptor), privateInputNameIdentity, requiredCredentialNames: execution.requiredCredentials, environment: execution.environment.id, installedIdentity },
|
|
selectedDispatchFile: "tests/runner-e2e/chat-catalog-registry.ts",
|
|
normalizedMetadata: { "tests/runner-e2e/chat-catalog-descriptors.json": descriptor },
|
|
});
|
|
return { ...result, privateInputNameIdentity, privateInputRequirements: chatPrivateInputRequirements(descriptor) };
|
|
}
|