Validate completed reports before cancellation projection, retain valid Evidence and captured command diagnostics, and convert raw cleanup partials at their application owner. Record partial Evidence consistently across MCP handlers. Keep page inspection separate from unfinished bundle analysis. Refs #4261
121 lines
4.0 KiB
TypeScript
121 lines
4.0 KiB
TypeScript
import { isAbsolute } from "node:path";
|
|
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
|
import type { BinaryLayoutPort } from "./BinaryLayoutPort.js";
|
|
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisInputError,
|
|
AnalysisOutputError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import { createEvidence, type Evidence } from "../../domain/evidence.js";
|
|
import {
|
|
inspectBinaryLayoutInputSchema,
|
|
binaryLayoutSchema,
|
|
} from "../../domain/native/binaryLayout.js";
|
|
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
|
import { err, ok, type Result } from "../../domain/result.js";
|
|
import { completeProviderEvidence } from "../ProviderOperationExecution.js";
|
|
|
|
const OPERATION = "inspect_binary_layout";
|
|
|
|
/** Shared CLI/MCP validation and Evidence composition for offline layout inspection. */
|
|
export class BinaryLayoutService {
|
|
constructor(readonly provider: BinaryLayoutPort) {}
|
|
|
|
close(): Promise<void> {
|
|
return this.provider.close?.() ?? Promise.resolve();
|
|
}
|
|
|
|
/** Bind complete observations to the caller-selected artifact before creating Evidence. */
|
|
async inspect(
|
|
rawInput: unknown,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
return this.#inspect(rawInput, options, (input) =>
|
|
this.provider.inspect(input, options),
|
|
);
|
|
}
|
|
|
|
/** Complete the owned one-shot lifecycle before validating and publishing Evidence. */
|
|
inspectAndClose(
|
|
rawInput: unknown,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
return this.#inspect(rawInput, options, (input) =>
|
|
this.provider.inspectAndClose(input, options),
|
|
);
|
|
}
|
|
|
|
async #inspect(
|
|
rawInput: unknown,
|
|
options: ExecutionOptions | undefined,
|
|
inspect: (
|
|
input: Parameters<BinaryLayoutPort["inspect"]>[0],
|
|
) => ReturnType<BinaryLayoutPort["inspect"]>,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
if (options?.signal?.aborted)
|
|
return err(new AnalysisCancelledError(OPERATION));
|
|
const input = inspectBinaryLayoutInputSchema.safeParse(rawInput);
|
|
if (!input.success)
|
|
return err(
|
|
analysisInputErrorFromIssues(OPERATION, input.error.issues, rawInput, {
|
|
cause: input.error,
|
|
}),
|
|
);
|
|
if (!isAbsolute(input.data.path))
|
|
return err(
|
|
new AnalysisInputError(OPERATION, undefined, [
|
|
{
|
|
path: ["path"],
|
|
reason: "invalid_format",
|
|
message: "Expected an absolute filesystem path on this host.",
|
|
},
|
|
]),
|
|
);
|
|
const inspected = await inspect(input.data);
|
|
if (!inspected.ok) return inspected;
|
|
const diagnostics = binaryLayoutSchema.shape.diagnostics.safeParse(
|
|
inspected.value.diagnostics,
|
|
);
|
|
const outputOptions = diagnostics.success
|
|
? { capturedOutput: diagnostics.data }
|
|
: undefined;
|
|
const report = binaryLayoutSchema.safeParse(inspected.value);
|
|
if (!report.success || report.data.artifact.path !== input.data.path)
|
|
return err(
|
|
new AnalysisOutputError(
|
|
OPERATION,
|
|
"Binary layout provider returned malformed evidence or changed the selected artifact identity.",
|
|
outputOptions,
|
|
),
|
|
);
|
|
const value = report.data;
|
|
return completeProviderEvidence(
|
|
ok(
|
|
createEvidence(
|
|
{
|
|
path: value.artifact.path,
|
|
format: "elf",
|
|
architecture: "x86_64",
|
|
sha256: value.artifact.sha256,
|
|
},
|
|
this.provider.identity,
|
|
{
|
|
operation: OPERATION,
|
|
parameters: input.data,
|
|
result: value,
|
|
// The decoded report is the only representation; do not repeat it.
|
|
rawResult: null,
|
|
confidence: "observed",
|
|
limitations: value.limitations,
|
|
locations: [{ kind: "artifact-path", path: value.artifact.path }],
|
|
},
|
|
),
|
|
),
|
|
OPERATION,
|
|
options,
|
|
outputOptions,
|
|
);
|
|
}
|
|
}
|