mirror of
https://github.com/morluto/rea.git
synced 2026-11-02 21:06:04 +00:00
chore: merge main into the streamable HTTP transport branch
Picks up upstream's test-runner configuration and the command-finalization test fixes so the fork lane stops failing on unrelated flaky shards. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
cfbf595437
135 files changed
+33233
-1087
No files matched your search
@@ -1,28 +1,9 @@
|
||||
import { ArtifactResourceScope } from "../../dist/artifacts/ArtifactResourceScope.js";
|
||||
import { AnalysisCleanupLease } from "../../dist/artifacts/AnalysisCleanupLease.js";
|
||||
import { inspectGhidraInstallation } from "../../dist/ghidra/GhidraInstallation.js";
|
||||
import { closeVerifierResources } from "./verifier-cleanup.mjs";
|
||||
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
|
||||
|
||||
/** Complete one prerequisite inspection and release its exact retained owners. */
|
||||
export async function inspectVerifierGhidraInstallation(options, host) {
|
||||
const resources = new ArtifactResourceScope();
|
||||
const resourceOwner = {
|
||||
retain(owner, resource) {
|
||||
resources.retain({
|
||||
kind: "lease",
|
||||
resource,
|
||||
lease: AnalysisCleanupLease.forCloseOwner(owner, resource),
|
||||
});
|
||||
},
|
||||
run: (operation) => resources.run(operation),
|
||||
};
|
||||
let primaryError;
|
||||
try {
|
||||
return await inspectGhidraInstallation({ ...options, resourceOwner }, host);
|
||||
} catch (cause) {
|
||||
primaryError = cause;
|
||||
throw cause;
|
||||
} finally {
|
||||
await closeVerifierResources([() => resources.close()], primaryError);
|
||||
}
|
||||
export function inspectVerifierGhidraInstallation(options, host) {
|
||||
return withVerifierResourceOwner((resourceOwner) =>
|
||||
inspectGhidraInstallation({ ...options, resourceOwner }, host),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { validateJadxConfiguration } from "../../src/android/JadxConfiguration.js";
|
||||
|
||||
type ConfigurationArguments = Parameters<typeof validateJadxConfiguration>;
|
||||
|
||||
/** Validate prerequisites with a real owner, joining cleanup before completion. */
|
||||
export function validateVerifierJadxConfiguration(
|
||||
configuration: ConfigurationArguments[0],
|
||||
environment: ConfigurationArguments[1],
|
||||
operation: ConfigurationArguments[2],
|
||||
options?: Omit<ConfigurationArguments[3], "resourceOwner" | "operation">,
|
||||
): ReturnType<typeof validateJadxConfiguration>;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { validateJadxConfiguration } from "../../dist/android/JadxConfiguration.js";
|
||||
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
|
||||
|
||||
/** Validate Java and the engine archive before beginning real verification. */
|
||||
export function validateVerifierJadxConfiguration(
|
||||
configuration,
|
||||
environment,
|
||||
operation,
|
||||
options = {},
|
||||
) {
|
||||
return withVerifierResourceOwner((resourceOwner) =>
|
||||
validateJadxConfiguration(configuration, environment, operation, {
|
||||
...options,
|
||||
resourceOwner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export function isOwnedTestTemporaryName(name: string): boolean;
|
||||
export function normalizeTestTemporaryPrefix(prefix: string): string;
|
||||
@@ -0,0 +1,18 @@
|
||||
const OWNED_ENTRY = /^(?:rea-|bb-session-)/u;
|
||||
const SAFE_PREFIX = /^[A-Za-z0-9][A-Za-z0-9._-]*-$/u;
|
||||
|
||||
/** Recognize test resources independently of their readiness or contents. */
|
||||
export const isOwnedTestTemporaryName = (name) => OWNED_ENTRY.test(name);
|
||||
|
||||
/** Preserve caller labels while making every shared fixture root recognizable. */
|
||||
export function normalizeTestTemporaryPrefix(prefix) {
|
||||
if (
|
||||
typeof prefix !== "string" ||
|
||||
!SAFE_PREFIX.test(prefix) ||
|
||||
prefix.length > 80
|
||||
)
|
||||
throw new TypeError(
|
||||
"Test temporary directory prefixes must be safe basenames ending in '-'",
|
||||
);
|
||||
return isOwnedTestTemporaryName(prefix) ? prefix : `rea-${prefix}`;
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { ArtifactResourceScope } from "../../src/artifacts/ArtifactResourceScope.js";
|
||||
import type { FileCloseResourceOwner } from "../../src/application/AnalysisProvider.js";
|
||||
|
||||
/** Complete a prerequisite operation, including retained cleanup on failure. */
|
||||
export function withVerifierResourceOwner<T>(
|
||||
operation: (owner: FileCloseResourceOwner) => Promise<T>,
|
||||
): Promise<T>;
|
||||
|
||||
/** Hash one verifier input and transfer failed-close ownership to its scope. */
|
||||
export function hashVerifierFile(
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
import { hashStableFile } from "../../dist/filesystem/StableFileHash.js";
|
||||
import { ArtifactResourceScope } from "../../dist/artifacts/ArtifactResourceScope.js";
|
||||
import { AnalysisCleanupLease } from "../../dist/artifacts/AnalysisCleanupLease.js";
|
||||
import { closeVerifierResources } from "./verifier-cleanup.mjs";
|
||||
|
||||
/** Finish a prerequisite probe and release its exact retained cleanup owners. */
|
||||
export async function withVerifierResourceOwner(operation) {
|
||||
const resources = new ArtifactResourceScope();
|
||||
const resourceOwner = {
|
||||
retain(owner, resource) {
|
||||
resources.retain({
|
||||
kind: "lease",
|
||||
resource,
|
||||
lease: AnalysisCleanupLease.forCloseOwner(owner, resource),
|
||||
});
|
||||
},
|
||||
run: (work) => resources.run(work),
|
||||
};
|
||||
let primaryError;
|
||||
try {
|
||||
return await operation(resourceOwner);
|
||||
} catch (cause) {
|
||||
primaryError = cause;
|
||||
throw cause;
|
||||
} finally {
|
||||
await closeVerifierResources([() => resources.close()], primaryError);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hash one verifier input while retaining any descriptor whose close fails. */
|
||||
export function hashVerifierFile(path, resources) {
|
||||
|
||||
@@ -4,15 +4,16 @@ import { dirname, join, relative, resolve } from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { isOwnedTestTemporaryName } from "./lib/test-temporary-ownership.mjs";
|
||||
|
||||
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const vitestPath = resolve(repositoryRoot, "node_modules/vitest/vitest.mjs");
|
||||
const OWNED_ENTRY = /^(?:rea-|bb-session-)/u;
|
||||
|
||||
const findOwnedEntries = async (root, directory = root) => {
|
||||
const owned = [];
|
||||
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
||||
const path = join(directory, entry.name);
|
||||
if (OWNED_ENTRY.test(entry.name)) {
|
||||
if (isOwnedTestTemporaryName(entry.name)) {
|
||||
owned.push(relative(root, path));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,8 @@ import { verifyAndroidCliCancellation } from "./cli-cancellation.mjs";
|
||||
import { verifyAndroidMetadata } from "./metadata.mjs";
|
||||
import { verifyAndroidClassSearch } from "./class-search.mjs";
|
||||
import { verifyAndroidFailureRecovery } from "./failure-recovery.mjs";
|
||||
import {
|
||||
resolveJadxConfigurationInputs,
|
||||
validateJadxConfiguration,
|
||||
} from "../../../dist/android/JadxConfiguration.js";
|
||||
import { resolveJadxConfigurationInputs } from "../../../dist/android/JadxConfiguration.js";
|
||||
import { validateVerifierJadxConfiguration } from "../../lib/jadx-configuration-inspection.mjs";
|
||||
import { verifyWindowsAndroidLifecycle } from "./windows-lifecycle.mjs";
|
||||
|
||||
const repository = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
@@ -43,7 +41,7 @@ const configuration = await resolveJadxConfigurationInputs(
|
||||
process.env,
|
||||
"inspect_android_package",
|
||||
);
|
||||
await validateJadxConfiguration(
|
||||
await validateVerifierJadxConfiguration(
|
||||
configuration,
|
||||
process.env,
|
||||
"inspect_android_package",
|
||||
|
||||
@@ -17,6 +17,7 @@ import { delimiter, dirname, isAbsolute, join } from "node:path";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { TOOL_CONTRACTS } from "../../../dist/contracts/toolContracts.js";
|
||||
import { analysisErrorProjectionSchema } from "../../../dist/contracts/errorSchemas.js";
|
||||
|
||||
/** Real unusable Java runtimes and selectors must lead to applicable recovery. */
|
||||
export async function verifyAndroidFailureRecovery({
|
||||
@@ -103,18 +104,46 @@ export async function verifyAndroidFailureRecovery({
|
||||
label: "JRE without compiler",
|
||||
env: { ...environment, JAVA_HOME: jre },
|
||||
observation: "jdk.compiler",
|
||||
diagnostics: {
|
||||
failure_kind: "java_runtime",
|
||||
failure_code: "runtime_missing",
|
||||
jdk_compiler: false,
|
||||
},
|
||||
message: /full JDK/u,
|
||||
remediation: /java --list-modules/u,
|
||||
},
|
||||
{
|
||||
label: "missing java",
|
||||
env: { ...withoutJavaHome, PATH: empty },
|
||||
observation:
|
||||
process.platform === "win32" ? "Cannot find java.exe" : "ENOENT",
|
||||
diagnostics: {
|
||||
failure_kind: "java_runtime",
|
||||
failure_code: "runtime_missing",
|
||||
...(process.platform === "win32"
|
||||
? {}
|
||||
: { process_error_code: "ENOENT" }),
|
||||
},
|
||||
message:
|
||||
process.platform === "win32"
|
||||
? /Cannot find java\.exe/u
|
||||
: /selected Java runtime/u,
|
||||
remediation:
|
||||
process.platform === "win32"
|
||||
? /JAVA_HOME|PATH/u
|
||||
: /selected launcher/u,
|
||||
},
|
||||
{
|
||||
label: "invalid engine archive with a working JDK",
|
||||
env: { ...environment, REA_JADX_MCP_JAR: invalidJar },
|
||||
observation: "Cannot inspect REA_JADX_MCP_JAR",
|
||||
jarFailure: true,
|
||||
diagnostics: {
|
||||
failure_kind: "jar",
|
||||
failure_code: "version_unresolved",
|
||||
},
|
||||
message: /REA_JADX_MCP_JAR/u,
|
||||
remediation: /jadx-headless-mcp/u,
|
||||
forbiddenGuidance: /Select a full JDK/u,
|
||||
},
|
||||
];
|
||||
if (process.platform === "darwin") {
|
||||
@@ -126,6 +155,12 @@ export async function verifyAndroidFailureRecovery({
|
||||
label: "macOS Java stub",
|
||||
env: { ...withoutJavaHome, PATH: "/usr/bin:/bin" },
|
||||
observation: "Unable to locate a Java Runtime",
|
||||
diagnostics: {
|
||||
failure_kind: "java_probe",
|
||||
failure_code: "runtime_unavailable",
|
||||
},
|
||||
message: /selected Java runtime/u,
|
||||
remediation: /selected launcher/u,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -145,23 +180,20 @@ export async function verifyAndroidFailureRecovery({
|
||||
assert.fail(`${scenario.label} unexpectedly succeeded`);
|
||||
} catch (cause) {
|
||||
assert.equal(cause.code, 1);
|
||||
failure = JSON.parse(cause.stdout);
|
||||
failure = analysisErrorProjectionSchema.parse(JSON.parse(cause.stdout));
|
||||
}
|
||||
assert.equal(failure.code, "capability_unavailable");
|
||||
assert.ok(
|
||||
JSON.stringify(failure).includes(scenario.observation),
|
||||
JSON.stringify(failure),
|
||||
);
|
||||
assert.match(
|
||||
failure.message,
|
||||
scenario.jarFailure ? /REA_JADX_MCP_JAR/u : /full JDK/u,
|
||||
);
|
||||
assert.match(
|
||||
failure.remediation.action,
|
||||
scenario.jarFailure ? /jadx-headless-mcp/u : /java --list-modules/u,
|
||||
);
|
||||
if (scenario.jarFailure)
|
||||
assert.doesNotMatch(failure.message, /Select a full JDK/u);
|
||||
assert.ok(failure.details.diagnostics);
|
||||
for (const [key, value] of Object.entries(scenario.diagnostics))
|
||||
assert.deepEqual(failure.details.diagnostics[key], value);
|
||||
assert.match(failure.message, scenario.message);
|
||||
assert.match(failure.remediation.action, scenario.remediation);
|
||||
if (scenario.forbiddenGuidance)
|
||||
assert.doesNotMatch(failure.message, scenario.forbiddenGuidance);
|
||||
assert.doesNotMatch(JSON.stringify(failure), /rea doctor/u);
|
||||
await verifyAndroidReadiness({
|
||||
entrypoint,
|
||||
|
||||
@@ -13,10 +13,8 @@ import { dirname, join } from "node:path";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import {
|
||||
resolveJadxConfigurationInputs,
|
||||
validateJadxConfiguration,
|
||||
} from "../../../dist/android/JadxConfiguration.js";
|
||||
import { resolveJadxConfigurationInputs } from "../../../dist/android/JadxConfiguration.js";
|
||||
import { validateVerifierJadxConfiguration } from "../../lib/jadx-configuration-inspection.mjs";
|
||||
import { verifyWindowsGuardianIsolation } from "./windows-guardian-isolation.mjs";
|
||||
|
||||
/** Verify real Windows cancellation, disconnect and abrupt CLI owner exit. */
|
||||
@@ -45,7 +43,7 @@ async function verifyJavaSelection(environment) {
|
||||
environment,
|
||||
"inspect_android_package",
|
||||
);
|
||||
await validateJadxConfiguration(
|
||||
await validateVerifierJadxConfiguration(
|
||||
selected,
|
||||
environment,
|
||||
"inspect_android_package",
|
||||
@@ -65,7 +63,7 @@ async function verifyJavaSelection(environment) {
|
||||
pathEnvironment,
|
||||
"inspect_android_package",
|
||||
);
|
||||
await validateJadxConfiguration(
|
||||
await validateVerifierJadxConfiguration(
|
||||
onPath,
|
||||
pathEnvironment,
|
||||
"inspect_android_package",
|
||||
|
||||
Reference in new issue
Block a user