mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(verifiers): consolidate shared helper ownership
Use JSON.parse directly in package verification and retain one parsed Cursor recovery snapshot. Route native command source loading through the shared transform-service owner so completed command cleanup leaves no transform child. Preserve JSONC validation and caller inputs. Cover the actual public source-loading lifecycle with an authored command and verified process lineage.
This commit is contained in:
1 parent
791524c2fa
commit
48ff37f6ea
14 files changed
+149
-80
No files matched your search
@@ -1,12 +1,12 @@
|
||||
import { importVerifierSource } from "./verifier-source-loader.mjs";
|
||||
|
||||
let implementation;
|
||||
|
||||
/** Load the development command owner and its canonical process source graph. */
|
||||
export async function createVerifierCommands(options = {}) {
|
||||
implementation ??= import("tsx/esm/api").then(({ tsImport }) =>
|
||||
tsImport(
|
||||
new URL("./verifier-command-owner.mts", import.meta.url).href,
|
||||
import.meta.url,
|
||||
),
|
||||
implementation ??= importVerifierSource(
|
||||
new URL("./verifier-command-owner.mts", import.meta.url).href,
|
||||
import.meta.url,
|
||||
);
|
||||
const module = await implementation;
|
||||
return module.createVerifierCommands(options);
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { lstat } from "node:fs/promises";
|
||||
import { Ajv2020 } from "ajv/dist/2020.js";
|
||||
|
||||
// Only catalog verification needs built MCP contracts; the other helpers work
|
||||
// Only catalog verification needs built MCP contracts; path checks work
|
||||
// from source without a runtime build.
|
||||
const loadMcpVerifierResults = () => import("./mcp-verifier-results.mjs");
|
||||
|
||||
/** Parse JSON from a command output string. */
|
||||
export const json = (text) => JSON.parse(text);
|
||||
|
||||
/** Verify schema validity and that the MCP catalog matches session metadata. */
|
||||
export const verifyCompleteToolCatalog = async (client, options) => {
|
||||
const { requireMcpLifecycleResult, sortVerifierNames } =
|
||||
|
||||
@@ -3,8 +3,6 @@ import { isDeepStrictEqual } from "node:util";
|
||||
import { join } from "node:path";
|
||||
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
||||
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
/** Run artifact and JavaScript application analysis against packaged fixtures. */
|
||||
export async function verifyPackageArtifactAndElectron({
|
||||
commands,
|
||||
@@ -37,14 +35,14 @@ const verifyPackagedArtifact = async ({
|
||||
const artifactWriter = new ZipWriter(new Uint8ArrayWriter());
|
||||
await artifactWriter.add("app/main.js", new TextReader("main();"));
|
||||
await writeFile(artifactArchive, await artifactWriter.close());
|
||||
const artifactInspection = json(
|
||||
const artifactInspection = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect-artifact", artifactArchive, "--json"],
|
||||
environment,
|
||||
),
|
||||
);
|
||||
const repeatedArtifactInspection = json(
|
||||
const repeatedArtifactInspection = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect-artifact", artifactArchive, "--json"],
|
||||
@@ -95,7 +93,7 @@ const verifyPackagedElectronApplication = async ({
|
||||
join(applicationRoot, "preload.js"),
|
||||
'const { contextBridge, ipcRenderer } = require("electron");\ncontextBridge.exposeInMainWorld("rea", { ping: () => ipcRenderer.invoke("rea:ping") });\n',
|
||||
);
|
||||
const applicationAnalysis = json(
|
||||
const applicationAnalysis = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["analyze-javascript-application", applicationRoot, "--json"],
|
||||
@@ -110,7 +108,7 @@ const verifyPackagedElectronApplication = async ({
|
||||
?.paired_renderer_transmissions !== 1
|
||||
)
|
||||
throw new Error("packaged JavaScript application analysis CLI failed");
|
||||
const routedApplicationAnalysis = json(
|
||||
const routedApplicationAnalysis = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["analyze", applicationRoot, "--json"],
|
||||
@@ -138,7 +136,7 @@ const verifyPackagedArtifactExtraction = async ({
|
||||
);
|
||||
if (occurrence?.logical_path === undefined)
|
||||
throw new Error("packaged artifact inventory omitted the selected member");
|
||||
const extraction = json(
|
||||
const extraction = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["extract-artifact", artifactArchive, "--json"],
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
import {
|
||||
expandCapabilitiesOutput,
|
||||
expandProviderOutput,
|
||||
@@ -11,12 +10,14 @@ export async function verifyPackageCapabilitiesAndSearch({
|
||||
environment,
|
||||
}) {
|
||||
const capabilities = expandCapabilitiesOutput(
|
||||
json(await commands.run(cli, ["capabilities", "--json"], environment)),
|
||||
JSON.parse(
|
||||
await commands.run(cli, ["capabilities", "--json"], environment),
|
||||
),
|
||||
);
|
||||
if (!Array.isArray(capabilities.capabilities))
|
||||
throw new Error("packaged capabilities CLI failed");
|
||||
const providers = expandProviderOutput(
|
||||
json(await commands.run(cli, ["providers", "--json"], environment)),
|
||||
JSON.parse(await commands.run(cli, ["providers", "--json"], environment)),
|
||||
);
|
||||
const candidates = providers.analysis_provider_candidates;
|
||||
if (
|
||||
@@ -32,7 +33,7 @@ export async function verifyPackageCapabilitiesAndSearch({
|
||||
)
|
||||
throw new Error("packaged providers CLI failed");
|
||||
if (process.platform !== "linux") {
|
||||
const searchResult = json(
|
||||
const searchResult = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
const REQUIRED_HELP_COMMANDS = [
|
||||
"setup",
|
||||
"update",
|
||||
@@ -87,7 +85,7 @@ export async function verifyPackageDiscovery({ commands, cli, environment }) {
|
||||
["doctor", "--json"],
|
||||
environment,
|
||||
);
|
||||
const doctor = json(doctorExecution.stdout);
|
||||
const doctor = JSON.parse(doctorExecution.stdout);
|
||||
const { supportedSetupHost, hopperReadinessRequired, hopperSetupSupported } =
|
||||
packageSetupSupport(doctor.checks, {
|
||||
platform: process.platform,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { createEvidence } from "../dist/domain/evidence.js";
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
/** Round-trip historical reference import and evidence bundles. */
|
||||
export async function verifyPackageEvidence({
|
||||
@@ -12,7 +11,7 @@ export async function verifyPackageEvidence({
|
||||
referenceRoot,
|
||||
environment,
|
||||
}) {
|
||||
const referenceImport = json(
|
||||
const referenceImport = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["import-reference-source", referenceRoot, "--json"],
|
||||
@@ -39,21 +38,21 @@ export async function verifyPackageEvidence({
|
||||
const sourceBundlePath = join(evidenceRoot, "source.json");
|
||||
const canonicalBundlePath = join(evidenceRoot, "canonical.json");
|
||||
await writeFile(sourceBundlePath, serializeEvidenceBundle(sourceBundle));
|
||||
const importedBundle = json(
|
||||
const importedBundle = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["evidence-import", sourceBundlePath, "--json"],
|
||||
environment,
|
||||
),
|
||||
);
|
||||
const exportedBundle = json(
|
||||
const exportedBundle = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["evidence-export", sourceBundlePath, canonicalBundlePath, "--json"],
|
||||
environment,
|
||||
),
|
||||
);
|
||||
const comparedBundle = json(
|
||||
const comparedBundle = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["compare", sourceBundlePath, canonicalBundlePath, "--json"],
|
||||
|
||||
@@ -2,8 +2,6 @@ import { rm } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
/** Install the packed CLI globally with and without optional dependencies and verify PTY degradation. */
|
||||
export async function verifyPackageInstall({
|
||||
tarball,
|
||||
@@ -39,7 +37,7 @@ export async function verifyPackageInstall({
|
||||
"lib/node_modules/rea-agents/dist/process/capture/ProcessCaptureCapability.js",
|
||||
),
|
||||
).href;
|
||||
const processCaptureCapability = json(
|
||||
const processCaptureCapability = JSON.parse(
|
||||
(
|
||||
await commands.exec(
|
||||
process.execPath,
|
||||
@@ -84,7 +82,7 @@ export async function verifyPackageInstall({
|
||||
"lib/node_modules/rea-agents/dist/process/capture/ProcessCaptureCapability.js",
|
||||
),
|
||||
).href;
|
||||
const noOptionalCapability = json(
|
||||
const noOptionalCapability = JSON.parse(
|
||||
(
|
||||
await commands.exec(
|
||||
process.execPath,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { join } from "node:path";
|
||||
import { createEvidence } from "../dist/domain/evidence.js";
|
||||
import { buildManagedPeFixture } from "./fixtures/managed/pe.mjs";
|
||||
import { functionDossier } from "./fixtures/managed/function-dossier.mjs";
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
const buildManagedFixtures = async (workspace) => {
|
||||
const managedPath = join(workspace, "managed-fixture.exe");
|
||||
@@ -44,7 +43,7 @@ const verifyManagedArtifact = async ({
|
||||
managedPath,
|
||||
environment,
|
||||
}) => {
|
||||
const managedArtifact = json(
|
||||
const managedArtifact = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect-managed-artifact", managedPath, "--json"],
|
||||
@@ -66,7 +65,7 @@ const verifyManagedMembers = async ({
|
||||
managedPath,
|
||||
environment,
|
||||
}) => {
|
||||
const managedMembers = json(
|
||||
const managedMembers = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect-managed-members", managedPath, "--json"],
|
||||
@@ -111,7 +110,7 @@ const verifyManagedReconstruction = async ({
|
||||
],
|
||||
notes: ["packaged synthetic reconstruction import"],
|
||||
};
|
||||
const managedReconstruction = json(
|
||||
const managedReconstruction = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
@@ -138,7 +137,7 @@ const verifyManagedBoundaries = async ({
|
||||
managedPath,
|
||||
environment,
|
||||
}) => {
|
||||
const managedBoundaries = json(
|
||||
const managedBoundaries = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect-managed-native-boundaries", managedPath, "--json"],
|
||||
@@ -174,7 +173,7 @@ const verifyManagedApplicationGraph = async ({
|
||||
"inspect_managed_native_boundaries",
|
||||
),
|
||||
};
|
||||
const managedApplicationGraph = json(
|
||||
const managedApplicationGraph = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
@@ -260,7 +259,7 @@ const verifyManagedNativeVerification = async ({
|
||||
),
|
||||
],
|
||||
};
|
||||
const managedNativeVerification = json(
|
||||
const managedNativeVerification = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
@@ -289,7 +288,7 @@ const verifyManagedComparison = async ({
|
||||
managedRightPath,
|
||||
environment,
|
||||
}) => {
|
||||
const managedComparison = json(
|
||||
const managedComparison = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["compare-managed-members", managedPath, managedRightPath, "--json"],
|
||||
|
||||
@@ -11,7 +11,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { resolveMcpStartupBudgets } from "../dist/config/mcpStartupBudgets.js";
|
||||
import { parseEvidence } from "../dist/domain/evidence.js";
|
||||
import * as prompts from "./verify-package-prompts.mjs";
|
||||
import { json, verifyCompleteToolCatalog } from "./lib/verify-package-core.mjs";
|
||||
import { verifyCompleteToolCatalog } from "./lib/verify-package-core.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
|
||||
const verifyMcpToolsAndPrompts = async (client, mcpOptions) => {
|
||||
@@ -95,7 +95,7 @@ const verifyMcpNonLinuxCurrentDocument = async (
|
||||
mcpOptions,
|
||||
current,
|
||||
) => {
|
||||
if (json(prompts.mcpText(current)).normalized_result !== "fixture")
|
||||
if (JSON.parse(prompts.mcpText(current)).normalized_result !== "fixture")
|
||||
throw new Error("packaged MCP bridge call failed");
|
||||
const batch = await client.callTool(
|
||||
{
|
||||
@@ -104,7 +104,7 @@ const verifyMcpNonLinuxCurrentDocument = async (
|
||||
},
|
||||
mcpOptions,
|
||||
);
|
||||
const batchResult = json(prompts.mcpText(batch)).normalized_result;
|
||||
const batchResult = JSON.parse(prompts.mcpText(batch)).normalized_result;
|
||||
if (
|
||||
batch.isError === true ||
|
||||
batchResult?.total !== 1 ||
|
||||
@@ -178,7 +178,7 @@ const verifyMcpInlineArtifactEvidence = async (
|
||||
inspected.structuredContent?.normalized_result,
|
||||
);
|
||||
assert.deepEqual(
|
||||
json(prompts.mcpText(inspected)),
|
||||
JSON.parse(prompts.mcpText(inspected)),
|
||||
inspected.structuredContent,
|
||||
);
|
||||
const compared = await client.callTool(
|
||||
@@ -251,7 +251,7 @@ export async function verifyPackageMcp({
|
||||
evidenceRoot,
|
||||
artifactArchive,
|
||||
}) {
|
||||
const diagnosed = json(
|
||||
const diagnosed = JSON.parse(
|
||||
(
|
||||
await commands.exec(cli, ["mcp", "doctor", "--json"], {
|
||||
env: environment,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
/** Run platform-specific deep-analysis smoke tests. */
|
||||
export async function verifyPackagePlatform({
|
||||
commands,
|
||||
@@ -15,7 +13,7 @@ export async function verifyPackagePlatform({
|
||||
);
|
||||
assertLinuxPackageProviderFailure(unsupportedExecution);
|
||||
} else {
|
||||
const overview = json(
|
||||
const overview = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["analyze", process.execPath, "--provider", "hopper", "--json"],
|
||||
@@ -26,7 +24,7 @@ export async function verifyPackagePlatform({
|
||||
throw new Error(
|
||||
`packaged Hopper-backed analyze CLI failed: ${JSON.stringify(overview)}`,
|
||||
);
|
||||
const inspected = json(
|
||||
const inspected = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["inspect", process.execPath, "--provider", "hopper", "--json"],
|
||||
@@ -35,7 +33,7 @@ export async function verifyPackagePlatform({
|
||||
);
|
||||
if (!hasPositiveProcedureCount(inspected))
|
||||
throw new Error("packaged inspect CLI failed");
|
||||
const functionResult = json(
|
||||
const functionResult = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
@@ -57,7 +55,7 @@ export async function verifyPackagePlatform({
|
||||
throw new Error(
|
||||
`packaged function CLI failed: ${JSON.stringify(functionResult)}`,
|
||||
);
|
||||
const xrefs = json(
|
||||
const xrefs = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
["xrefs", process.execPath, "0x1000", "--provider", "hopper", "--json"],
|
||||
@@ -69,7 +67,7 @@ export async function verifyPackagePlatform({
|
||||
JSON.stringify(xrefs.normalized_result) !== JSON.stringify(["0x1000"])
|
||||
)
|
||||
throw new Error(`packaged xrefs CLI failed: ${JSON.stringify(xrefs)}`);
|
||||
const trace = json(
|
||||
const trace = JSON.parse(
|
||||
await commands.run(
|
||||
cli,
|
||||
[
|
||||
@@ -108,7 +106,7 @@ export const isUnsupportedHopperLaunchFailure = (failure) => {
|
||||
|
||||
/** Require an exact pre-execution rejection, observed helper exit, or missing-Xvfb diagnostic. */
|
||||
export const assertLinuxPackageProviderFailure = (execution) => {
|
||||
const failure = json(execution.stdout);
|
||||
const failure = JSON.parse(execution.stdout);
|
||||
const details = failure?.details;
|
||||
const diagnostic = details?.diagnostics;
|
||||
const missingDisplayDependency =
|
||||
|
||||
@@ -11,7 +11,7 @@ import { dirname, join } from "node:path";
|
||||
|
||||
import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js";
|
||||
import { PRODUCT_IDENTITY } from "../dist/identity.js";
|
||||
import { json, pathExists } from "./lib/verify-package-core.mjs";
|
||||
import { pathExists } from "./lib/verify-package-core.mjs";
|
||||
import { parse as parseJsonc } from "jsonc-parser";
|
||||
import { skillReferenceIssues } from "./lib/docs-facts.mjs";
|
||||
import {
|
||||
@@ -61,7 +61,7 @@ const verifySetupPlan = async ({
|
||||
["setup", "--all-detected", "--dry-run", "--json"],
|
||||
environment,
|
||||
);
|
||||
const planned = json(plannedExecution.stdout);
|
||||
const planned = JSON.parse(plannedExecution.stdout);
|
||||
if (supportedSetupHost) {
|
||||
const plannedActions = Array.isArray(planned?.plannedActions)
|
||||
? planned.plannedActions
|
||||
@@ -114,7 +114,7 @@ const verifyAbsentOpenCodeSetup = async ({
|
||||
["setup", "--yes", "--client", "opencode", "--skill=false", "--json"],
|
||||
{ ...environment, XDG_CONFIG_HOME: xdgConfigHome },
|
||||
);
|
||||
const result = json(execution.stdout);
|
||||
const result = JSON.parse(execution.stdout);
|
||||
const config = JSON.parse(await readFile(configPath, "utf8"));
|
||||
if (
|
||||
execution.status !== 0 ||
|
||||
@@ -163,8 +163,8 @@ const verifyAbsentClaudeCodeSetup = async ({
|
||||
args,
|
||||
childEnvironment,
|
||||
);
|
||||
const first = json(firstExecution.stdout);
|
||||
const config = json(await readFile(configPath, "utf8"));
|
||||
const first = JSON.parse(firstExecution.stdout);
|
||||
const config = JSON.parse(await readFile(configPath, "utf8"));
|
||||
const registration = first.doctor.identity?.registrations?.find(
|
||||
({ client }) => client === "claude_code",
|
||||
);
|
||||
@@ -186,7 +186,7 @@ const verifyAbsentClaudeCodeSetup = async ({
|
||||
args,
|
||||
childEnvironment,
|
||||
);
|
||||
const repeat = json(repeatExecution.stdout);
|
||||
const repeat = JSON.parse(repeatExecution.stdout);
|
||||
if (
|
||||
repeatExecution.status !== 0 ||
|
||||
repeat.status !== "ready" ||
|
||||
@@ -214,13 +214,13 @@ const verifySetupApply = async ({
|
||||
["setup", "--yes", "--all-detected", "--json"],
|
||||
environment,
|
||||
);
|
||||
const first = json(firstExecution.stdout);
|
||||
const first = JSON.parse(firstExecution.stdout);
|
||||
const alignedExecution = await commands.runWithStatus(
|
||||
cli,
|
||||
["setup", "--all-detected", "--json"],
|
||||
environment,
|
||||
);
|
||||
const aligned = json(alignedExecution.stdout);
|
||||
const aligned = JSON.parse(alignedExecution.stdout);
|
||||
const secondExecution = await commands.runWithStatus(
|
||||
cli,
|
||||
[
|
||||
@@ -236,7 +236,7 @@ const verifySetupApply = async ({
|
||||
],
|
||||
environment,
|
||||
);
|
||||
const second = json(secondExecution.stdout);
|
||||
const second = JSON.parse(secondExecution.stdout);
|
||||
if (supportedSetupHost) {
|
||||
if (
|
||||
first.status !== status ||
|
||||
@@ -267,7 +267,7 @@ const verifySetupApply = async ({
|
||||
|
||||
/** Verify the live registration and exact pre-setup backup bytes. */
|
||||
export const assertClientConfig = async (cli, configPath, original) => {
|
||||
const config = json(await readFile(configPath, "utf8"));
|
||||
const config = JSON.parse(await readFile(configPath, "utf8"));
|
||||
if (
|
||||
config.existing !== true ||
|
||||
config.mcpServers?.rea?.command !== cli ||
|
||||
@@ -285,8 +285,7 @@ const assertOpenCodeConfig = async ({
|
||||
}) => {
|
||||
const contents = await readFile(configPath, "utf8");
|
||||
const errors = [];
|
||||
const parsed = parseJsonc(contents, errors, { allowTrailingComma: true });
|
||||
const config = parsed;
|
||||
const config = parseJsonc(contents, errors, { allowTrailingComma: true });
|
||||
const mcp = config?.mcp;
|
||||
if (
|
||||
errors.length !== 0 ||
|
||||
@@ -357,7 +356,7 @@ const assertAlignedDoctor = async (commands, cli, environment) => {
|
||||
["doctor", "--json"],
|
||||
environment,
|
||||
);
|
||||
const alignedDoctor = json(alignedDoctorExecution.stdout);
|
||||
const alignedDoctor = JSON.parse(alignedDoctorExecution.stdout);
|
||||
if (
|
||||
alignedDoctorExecution.status !== (alignedDoctor.healthy ? 0 : 1) ||
|
||||
alignedDoctor.identity?.skill?.state !== "aligned" ||
|
||||
@@ -425,7 +424,8 @@ const verifySetupFailureRecovery = async ({
|
||||
["setup", "--yes", "--all-detected", "--json"],
|
||||
environment,
|
||||
);
|
||||
const failed = json(failedExecution.stdout);
|
||||
const failed = JSON.parse(failedExecution.stdout);
|
||||
const cursorAfterFailure = JSON.parse(await readFile(cursorConfig, "utf8"));
|
||||
if (
|
||||
failed.status !== "needs_human" ||
|
||||
failedExecution.status !== 1 ||
|
||||
@@ -433,8 +433,8 @@ const verifySetupFailureRecovery = async ({
|
||||
failed.appliedActions.length !== 0 ||
|
||||
!failed.remediation?.includes("Claude Desktop") ||
|
||||
(await readFile(claudeConfig, "utf8")) !== "malformed" ||
|
||||
json(await readFile(cursorConfig, "utf8")).existing !== true ||
|
||||
json(await readFile(cursorConfig, "utf8")).mcpServers?.rea !== undefined
|
||||
cursorAfterFailure.existing !== true ||
|
||||
cursorAfterFailure.mcpServers?.rea !== undefined
|
||||
)
|
||||
throw new Error(
|
||||
`packaged setup mutated clients after a preflight failure: ${JSON.stringify(failed)}`,
|
||||
@@ -445,11 +445,12 @@ const verifySetupFailureRecovery = async ({
|
||||
["setup", "--yes", "--all-detected", "--json"],
|
||||
environment,
|
||||
);
|
||||
const recovered = json(recoveredExecution.stdout);
|
||||
const recovered = JSON.parse(recoveredExecution.stdout);
|
||||
if (
|
||||
recovered.status !== status ||
|
||||
recoveredExecution.status !== (status === "ready" ? 0 : 1) ||
|
||||
json(await readFile(cursorConfig, "utf8")).mcpServers?.rea?.command !== cli
|
||||
JSON.parse(await readFile(cursorConfig, "utf8")).mcpServers?.rea
|
||||
?.command !== cli
|
||||
)
|
||||
throw new Error("packaged setup did not recover");
|
||||
};
|
||||
@@ -464,7 +465,7 @@ const verifyDanglingSymlink = async ({ commands, cli, environment, home }) => {
|
||||
["setup", "--yes", "--client", "gemini_cli", "--json"],
|
||||
environment,
|
||||
);
|
||||
const dangling = json(danglingExecution.stdout);
|
||||
const dangling = JSON.parse(danglingExecution.stdout);
|
||||
if (
|
||||
dangling.status !== "needs_human" ||
|
||||
danglingExecution.status !== 1 ||
|
||||
@@ -496,8 +497,8 @@ const verifyUninstall = async ({
|
||||
["uninstall", "--json"],
|
||||
environment,
|
||||
);
|
||||
const uninstall = json(uninstallExecution.stdout);
|
||||
const cursorAfterUninstall = json(await readFile(cursorTarget, "utf8"));
|
||||
const uninstall = JSON.parse(uninstallExecution.stdout);
|
||||
const cursorAfterUninstall = JSON.parse(await readFile(cursorTarget, "utf8"));
|
||||
const codexAfterUninstall = await readFile(codexTarget, "utf8");
|
||||
const openCodeErrors = [];
|
||||
const openCodeAfterUninstall = parseJsonc(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
|
||||
/** Verify that an unknown --provider is rejected by the packaged CLI. */
|
||||
export async function verifyUnknownProvider({ commands, cli, environment }) {
|
||||
const unknownProviderExecution = await commands.runWithStatus(
|
||||
@@ -7,7 +5,7 @@ export async function verifyUnknownProvider({ commands, cli, environment }) {
|
||||
["analyze", process.execPath, "--provider", "missing-provider", "--json"],
|
||||
environment,
|
||||
);
|
||||
const unknownProvider = json(unknownProviderExecution.stdout);
|
||||
const unknownProvider = JSON.parse(unknownProviderExecution.stdout);
|
||||
if (
|
||||
unknownProviderExecution.status !== 1 ||
|
||||
unknownProvider.details?.selection_reason !== "unknown_provider" ||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { readFile, writeFile, mkdir } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
import { json } from "./lib/verify-package-core.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
|
||||
/** Exercise the packaged updater with real npm and a local release registry fixture. */
|
||||
@@ -17,7 +16,7 @@ export async function verifyPackageUpdate({
|
||||
commands,
|
||||
}) {
|
||||
const metadataPath = join(packageRoot, "package.json");
|
||||
const metadata = json(await readFile(metadataPath, "utf8"));
|
||||
const metadata = JSON.parse(await readFile(metadataPath, "utf8"));
|
||||
const archive = await readFile(tarball);
|
||||
const entry = join(packageRoot, "scripts", "rea.mjs");
|
||||
const home = join(workspace, "update-home");
|
||||
@@ -144,7 +143,7 @@ process.stdout.write(JSON.stringify(result));
|
||||
if (result.status === "failed") process.exitCode = 1;
|
||||
`,
|
||||
);
|
||||
const result = json(
|
||||
const result = JSON.parse(
|
||||
(
|
||||
await commands.exec(process.execPath, [harness], {
|
||||
env: runtimeEnvironment,
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { expect } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
import { processTest } from "../../support/process/processFixture.js";
|
||||
|
||||
const commandsModule = new URL(
|
||||
"../../../scripts/lib/verify-commands.mjs",
|
||||
import.meta.url,
|
||||
).href;
|
||||
const verifierModule = new URL(
|
||||
"../../../scripts/lib/verifier-run.mjs",
|
||||
import.meta.url,
|
||||
).href;
|
||||
const repository = fileURLToPath(new URL("../../../", import.meta.url));
|
||||
const commandOutput = "authored native verifier command completed\n";
|
||||
|
||||
const reportSchema = z.object({
|
||||
command: z.object({ stdout: z.string(), stderr: z.string() }),
|
||||
verifier: z.object({
|
||||
process_lineage: z.discriminatedUnion("status", [
|
||||
z.object({
|
||||
status: z.literal("verified"),
|
||||
descendants: z.array(
|
||||
z.object({
|
||||
pid: z.number().int().positive(),
|
||||
parent_pid: z.number().int().positive(),
|
||||
process_group_id: z.number().int().positive(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
z.object({ status: z.literal("unavailable"), reason: z.string() }),
|
||||
]),
|
||||
}),
|
||||
});
|
||||
|
||||
// This exercises the public source loader and real command cleanup. The lineage
|
||||
// receipt is sampled inside the driver before its exit releases loader children.
|
||||
// Windows has no native Job Object lease for this verifier lineage observation.
|
||||
processTest.skipIf(process.platform === "win32")(
|
||||
"releases a cold native verifier source loader before reporting command cleanup complete",
|
||||
{ timeout: 30_000 },
|
||||
async ({ processes }) => {
|
||||
const result = await processes.run(
|
||||
process.execPath,
|
||||
[
|
||||
"--input-type=module",
|
||||
"-e",
|
||||
`
|
||||
import { createVerifierRun, completeVerifierRun } from ${JSON.stringify(verifierModule)};
|
||||
const run = createVerifierRun();
|
||||
const { createVerifierCommands } = await import(${JSON.stringify(commandsModule)});
|
||||
const commands = await createVerifierCommands();
|
||||
let command;
|
||||
try {
|
||||
command = await commands.exec(process.execPath, [
|
||||
"-e", ${JSON.stringify(`process.stdout.write(${JSON.stringify(commandOutput)})`)},
|
||||
], { timeout: 5000 });
|
||||
} finally {
|
||||
await commands.close();
|
||||
}
|
||||
const verifier = await completeVerifierRun(run);
|
||||
process.stdout.write(JSON.stringify({ command, verifier }));
|
||||
`,
|
||||
],
|
||||
{
|
||||
cwd: repository,
|
||||
// Installed tsx reads this switch before choosing its transform cache.
|
||||
// A fresh process must transform source even when disk cache is warm.
|
||||
env: { ...process.env, TSX_DISABLE_CACHE: "1" },
|
||||
timeoutMs: 20_000,
|
||||
},
|
||||
);
|
||||
expect(result.exitCode, result.stderr).toBe(0);
|
||||
expect(result.signal, result.stderr).toBeNull();
|
||||
const report = reportSchema.parse(JSON.parse(result.stdout));
|
||||
expect(report.command).toEqual({ stdout: commandOutput, stderr: "" });
|
||||
expect(report.verifier.process_lineage).toEqual({
|
||||
status: "verified",
|
||||
descendants: [],
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user