mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 22:06:10 +00:00
* fix(discovery): carry provider repair instructions into capability discovery Provider readiness reported only a failure description, so capability discovery copied that description into tool_availability remediation and dropped repairs the owners already had: the JEB discovery-deadline setting, the ADB/Apktool execute-permission repair kept in diagnostics, and the IDA and Kuna configuration recovery. Unavailable ProviderAvailability now requires the owner's remediation next to its reason, and readiness-derived unavailable capability descriptors carry it too. Every producer supplies it (ADB, Apktool, JADX, JEB, Ghidra, Hopper, IDA, Kuna, pcap, owned-process prerequisites, profile rejection); the inventory uses it verbatim, falling back to the reason only for static capability facts that declare no repair. ADB/Apktool configuration failures own remediation as a field instead of diagnostics, and execution refusals project it as remediation.action. JADX doctor reads the availability's remediation instead of re-deriving it. binary_session complete candidate availability and unavailable capability groups expose remediation (CLI capabilities/providers too); summary omits it and lists it in omitted_fields. The superseded IDA configuration_remediation and windows_native_failure.remediation diagnostics are removed. Fixes #8506 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(discovery): keep owner repairs through provider selection and capability maps Address review findings on the readiness-remediation contract: - Provider selection rejections carry the availability owner's remediation, CLI/MCP error details project it, and an explicit selection refused by readiness uses it as remediation.action. IDA's unreadable registration and Ghidra's Windows readiness repair no longer degrade to generic selection advice now that the diagnostics copies are gone. - Ghidra's Windows readiness and existing-project capability maps carry the same repair as the availability. - pcap readiness keeps the limiter owner's repair (now a typed remediationAction) and falls back to Wireshark guidance only when the failure has none. - ADB/Apktool repairs are owner constants that are complete sentences. The execution refusal keeps its previous message-plus-repair recovery, which the compiled CLI contract asserts. - Regression tests compare with the owning formatter or constant and add public MCP and CLI coverage for an explicit IDA selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
174 lines
6.0 KiB
TypeScript
174 lines
6.0 KiB
TypeScript
import type { RetainedResourceScope } from "../application/AnalysisProvider.js";
|
|
import type {
|
|
DoctorProviderCheck,
|
|
DoctorProviderInspection,
|
|
} from "../application/Doctor.js";
|
|
import type { ProviderAvailability } from "../application/AnalysisProvider.js";
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import {
|
|
inspectJadxAvailability,
|
|
JADX_FAILURE_KINDS,
|
|
jadxFailureClassification,
|
|
type JadxFailureKind,
|
|
} from "./JadxConfiguration.js";
|
|
import { JADX_JAR_CONFIGURATION_REMEDIATION } from "./JadxRelease.js";
|
|
|
|
/** Project the validated JADX configuration into the doctor contract. */
|
|
export const inspectJadxProvider = async (
|
|
environment: Readonly<Record<string, string | undefined>>,
|
|
resourceOwner: RetainedResourceScope,
|
|
signal?: AbortSignal,
|
|
): Promise<DoctorProviderInspection> => {
|
|
const configured = environment.REA_JADX_MCP_JAR !== undefined;
|
|
const availability = await inspectJadxAvailability(environment, {
|
|
resourceOwner,
|
|
signal,
|
|
});
|
|
// diagnostics is a loosely-typed JsonValue map; guard the published path
|
|
// even though the success path always sets jar_path today.
|
|
const jarPath =
|
|
availability.status === "available" &&
|
|
typeof availability.diagnostics["jar_path"] === "string"
|
|
? availability.diagnostics["jar_path"]
|
|
: undefined;
|
|
const available =
|
|
availability.status === "available" && jarPath !== undefined;
|
|
// Preserve the caller-selected JAR path (it may be a stable symlink) and
|
|
// the validated JAVA_HOME selection; the resolved jarPath stays in
|
|
// diagnostics as inspection evidence.
|
|
const configuredJar = environment.REA_JADX_MCP_JAR;
|
|
return {
|
|
id: "jadx",
|
|
configured,
|
|
available,
|
|
providerVersion: null,
|
|
registrationEnvironment:
|
|
jarPath === undefined || configuredJar === undefined
|
|
? {}
|
|
: {
|
|
REA_JADX_MCP_JAR: configuredJar,
|
|
...optionalEntry("JAVA_HOME", environment.JAVA_HOME),
|
|
...optionalEntry(
|
|
"REA_JADX_HEAP_MIB",
|
|
environment.REA_JADX_HEAP_MIB,
|
|
),
|
|
...optionalEntry(
|
|
"REA_JADX_ACTIVE_PROCESSOR_COUNT",
|
|
environment.REA_JADX_ACTIVE_PROCESSOR_COUNT,
|
|
),
|
|
...optionalEntry(
|
|
"REA_JADX_OPERATION_TIMEOUT_MS",
|
|
environment.REA_JADX_OPERATION_TIMEOUT_MS,
|
|
),
|
|
},
|
|
checks: [configurationCheck(configured, availability, jarPath)],
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Publish a caller-selected variable only when explicitly set; never persist
|
|
* ambient inheritance. Values are validated before this point.
|
|
*/
|
|
const optionalEntry = (
|
|
name: string,
|
|
value: string | undefined,
|
|
): Record<string, string> => (value === undefined ? {} : { [name]: value });
|
|
|
|
const configurationCheck = (
|
|
configured: boolean,
|
|
availability: ProviderAvailability,
|
|
jarPath: string | undefined,
|
|
): DoctorProviderCheck => {
|
|
const code = availability.status === "unavailable" ? availability.code : null;
|
|
if (availability.status === "available" && jarPath !== undefined) {
|
|
const operationTimeout = availability.diagnostics["operation_timeout_ms"];
|
|
return {
|
|
name: "configuration",
|
|
ok: true,
|
|
code: null,
|
|
detail: `JADX MCP bridge JAR validated at ${jarPath}; its path is recorded for agent registrations. This checks configuration, not whether a selected APK fits in the worker's memory.`,
|
|
remediation: null,
|
|
classification: "config_drift",
|
|
...(typeof operationTimeout === "number"
|
|
? { details: { operation_timeout_ms: operationTimeout } }
|
|
: {}),
|
|
};
|
|
}
|
|
// Availability carries the configuration boundary's remediation and tags
|
|
// the failure kind; project its classification here instead of
|
|
// reinterpreting the phase/setting evidence. Every failed check keeps a
|
|
// remediation.
|
|
const kind =
|
|
availability.status === "unavailable"
|
|
? asFailureKind(availability.diagnostics["failure_kind"])
|
|
: null;
|
|
return {
|
|
name: "configuration",
|
|
ok: false,
|
|
code,
|
|
detail:
|
|
availability.status === "available"
|
|
? "Validated JADX configuration reported no JAR path."
|
|
: availability.reason,
|
|
remediation:
|
|
availability.status === "available"
|
|
? JADX_JAR_CONFIGURATION_REMEDIATION
|
|
: availability.remediation,
|
|
classification: jadxFailureClassification(kind, configured),
|
|
...(kind === "java_probe" && availability.status === "unavailable"
|
|
? { details: probeDetails(availability.diagnostics) }
|
|
: kind !== null &&
|
|
kind.startsWith("windows_native_") &&
|
|
availability.status === "unavailable"
|
|
? { details: windowsNativeFailureDetails(availability.diagnostics) }
|
|
: {}),
|
|
};
|
|
};
|
|
|
|
const windowsNativeFailureDetails = (
|
|
diagnostics: Readonly<Record<string, JsonValue>>,
|
|
): Record<string, JsonValue> => {
|
|
const details: Record<string, JsonValue> = {};
|
|
for (const key of ["failure_stage", "remediation"] as const) {
|
|
const value = diagnostics[key];
|
|
if (value !== undefined) details[key] = value;
|
|
}
|
|
return details;
|
|
};
|
|
|
|
/** Validate the loose diagnostics tag against the boundary's failure kinds. */
|
|
const asFailureKind = (value: JsonValue | undefined): JadxFailureKind | null =>
|
|
typeof value === "string" &&
|
|
(JADX_FAILURE_KINDS as readonly string[]).includes(value)
|
|
? (value as JadxFailureKind)
|
|
: null;
|
|
|
|
/**
|
|
* Project bounded Java probe metadata for the doctor report. The full
|
|
* stdout/stderr can reach the 1 MiB probe budget, so only the bounded
|
|
* outcome metadata is carried; the message already references the retained
|
|
* output.
|
|
*/
|
|
const probeDetails = (
|
|
diagnostics: Readonly<Record<string, JsonValue>>,
|
|
): Record<string, JsonValue> => {
|
|
const details: Record<string, JsonValue> = {};
|
|
for (const key of [
|
|
"java_executable",
|
|
"failure",
|
|
"output_truncated",
|
|
"exit_code",
|
|
"process_error_code",
|
|
"signal",
|
|
"killed",
|
|
"output_limit_bytes",
|
|
"deadline_ms",
|
|
"jdk_compiler",
|
|
"java_major",
|
|
] as const) {
|
|
const value = diagnostics[key];
|
|
if (value !== undefined) details[key] = value;
|
|
}
|
|
return details;
|
|
};
|