mirror of
https://github.com/morluto/rea
synced 2026-10-26 22:28:27 -04:00
213 lines
7.5 KiB
TypeScript
213 lines
7.5 KiB
TypeScript
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisCapabilityUnavailableError,
|
|
AnalysisResourceConstraintError,
|
|
AnalysisTimeoutError,
|
|
} from "../domain/analysisErrorCore.js";
|
|
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
|
import { NATIVE_MACOS_PROVIDER_IDENTITY as IDENTITY } from "./NativeMacOSProviderMetadata.js";
|
|
import {
|
|
DEFAULT_NATIVE_COMMAND_TIMEOUT_MS,
|
|
fixedNativeAllowance,
|
|
nativeDeadlineRecovery,
|
|
} from "../domain/native/nativeDeadlines.js";
|
|
import {
|
|
NATIVE_COMMAND_OUTPUT_BUDGET_BYTES,
|
|
NATIVE_FAILURE_DIAGNOSTIC_BYTES,
|
|
type NativeCommandFailure,
|
|
} from "./CommandRunner.js";
|
|
|
|
/** Operation-owned text for a command deadline or unavailable requirement. */
|
|
export interface NativeCommandFailurePresentation {
|
|
readonly unavailableMessage?: string;
|
|
readonly timeoutRecovery?: {
|
|
readonly userMessage: string;
|
|
readonly remediationAction: string;
|
|
};
|
|
}
|
|
|
|
/** Translate the command owner's facts once, preserving capture and cleanup authority. */
|
|
export const translateNativeCommandFailure = (
|
|
operation: string,
|
|
failure: NativeCommandFailure,
|
|
timeoutMs = DEFAULT_NATIVE_COMMAND_TIMEOUT_MS,
|
|
presentation: NativeCommandFailurePresentation = {},
|
|
): AnalysisError => {
|
|
const cleanupOwners = [
|
|
...failure.cleanupFailures,
|
|
...(failure.cleanupOwner === undefined
|
|
? []
|
|
: [
|
|
{
|
|
owner: failure.cleanupOwner,
|
|
marker: failure.cleanupMarker,
|
|
reason:
|
|
failure.cleanupFailure ??
|
|
"native command cleanup remains incomplete",
|
|
resources: failure.cleanupResources,
|
|
},
|
|
]),
|
|
];
|
|
const markers = cleanupOwners.flatMap(({ marker }) =>
|
|
marker === undefined ? [] : [marker],
|
|
);
|
|
const bindCleanup = (error: AnalysisError): AnalysisError =>
|
|
cleanupOwners.length === 0 ||
|
|
cleanupOwners.some(({ marker }) => marker === undefined)
|
|
? error
|
|
: error.bindCleanupDependencies([...new Set(markers)]);
|
|
const preparation = failure.preparationFailure;
|
|
if (preparation !== undefined) {
|
|
const marker = preparation.cleanupFailure;
|
|
const compiler = preparation.compilerOutcome;
|
|
const options = {
|
|
cause: failure,
|
|
...(preparation.capturedOutput === undefined
|
|
? {}
|
|
: { capturedOutput: preparation.capturedOutput }),
|
|
...(marker === undefined
|
|
? {}
|
|
: {
|
|
cleanup: {
|
|
reason: marker.message,
|
|
resources: marker.cleanupResources,
|
|
},
|
|
}),
|
|
diagnostics: {
|
|
phase: "process-preparation",
|
|
tool: failure.tool,
|
|
reason: failure.reason,
|
|
detail: preparation.message,
|
|
...(compiler === undefined
|
|
? {}
|
|
: {
|
|
compiler_outcome: {
|
|
outcome: compiler.outcome,
|
|
code: compiler.code,
|
|
signal: compiler.signal,
|
|
...(compiler.timeoutMs === undefined
|
|
? {}
|
|
: { timeout_ms: compiler.timeoutMs }),
|
|
},
|
|
}),
|
|
},
|
|
};
|
|
const error =
|
|
preparation.cancellation === undefined
|
|
? new ProviderAdapterError(IDENTITY.id, operation, options)
|
|
: failure.reason === "timeout"
|
|
? new AnalysisTimeoutError(operation, timeoutMs, {
|
|
...options,
|
|
cause: preparation.cancellation.reason,
|
|
...(presentation.timeoutRecovery ??
|
|
nativeDeadlineRecovery({
|
|
timeoutMs,
|
|
setting: "native_command_timeout_ms",
|
|
cliFlag: "--native-command-timeout-ms",
|
|
defaultMs: DEFAULT_NATIVE_COMMAND_TIMEOUT_MS,
|
|
phase: "macOS native command deadline",
|
|
unchanged: `The ${String(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES)}-byte command output allowance and the ${String(NATIVE_FAILURE_DIAGNOSTIC_BYTES)}-byte failure-diagnostic excerpt have no runtime override.`,
|
|
})),
|
|
})
|
|
: new AnalysisCancelledError(operation, {
|
|
...options,
|
|
cause: preparation.cancellation.reason,
|
|
});
|
|
return bindCleanup(
|
|
marker === undefined ? error : error.bindCleanupDependencies([marker]),
|
|
);
|
|
}
|
|
const capturedOutput =
|
|
failure.capture === null
|
|
? undefined
|
|
: {
|
|
stdout: failure.capture.stdout,
|
|
stderr: failure.capture.stderr,
|
|
truncated: failure.capture.truncated,
|
|
stdout_bytes: failure.capture.stdoutBytes,
|
|
stderr_bytes: failure.capture.stderrBytes,
|
|
exit_code: failure.capture.exitCode,
|
|
signal: failure.capture.signal,
|
|
};
|
|
const cleanup =
|
|
failure.cleanupFailure === null
|
|
? undefined
|
|
: {
|
|
reason: failure.cleanupFailure,
|
|
resources: [...failure.cleanupResources],
|
|
};
|
|
const errorOptions = {
|
|
cause: failure,
|
|
...(capturedOutput === undefined ? {} : { capturedOutput }),
|
|
...(cleanup === undefined ? {} : { cleanup }),
|
|
};
|
|
if (failure.reason === "unavailable") {
|
|
const reason =
|
|
presentation.unavailableMessage ??
|
|
`${failure.tool} is unavailable through xcrun.`;
|
|
return bindCleanup(
|
|
new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, reason, {
|
|
...errorOptions,
|
|
userMessage: reason,
|
|
}),
|
|
);
|
|
}
|
|
if (failure.reason === "cancelled")
|
|
return bindCleanup(
|
|
new AnalysisCancelledError(operation, {
|
|
...errorOptions,
|
|
...(failure.cancellation === undefined
|
|
? {}
|
|
: { cause: failure.cancellation.reason }),
|
|
}),
|
|
);
|
|
if (failure.reason === "timeout")
|
|
return bindCleanup(
|
|
new AnalysisTimeoutError(operation, timeoutMs, {
|
|
...errorOptions,
|
|
...(presentation.timeoutRecovery ??
|
|
nativeDeadlineRecovery({
|
|
timeoutMs,
|
|
setting: "native_command_timeout_ms",
|
|
cliFlag: "--native-command-timeout-ms",
|
|
defaultMs: DEFAULT_NATIVE_COMMAND_TIMEOUT_MS,
|
|
phase: "macOS native command deadline",
|
|
unchanged: `The ${String(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES)}-byte command output allowance and the ${String(NATIVE_FAILURE_DIAGNOSTIC_BYTES)}-byte failure-diagnostic excerpt have no runtime override.`,
|
|
})),
|
|
}),
|
|
);
|
|
if (failure.reason === "output-limit")
|
|
return bindCleanup(
|
|
new AnalysisResourceConstraintError(
|
|
operation,
|
|
"memory",
|
|
`Native command output exceeded ${String(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES)} bytes. Complete output is required for analysis. This release has no runtime override. native_command_timeout_ms does not raise this allowance.`,
|
|
{ max_output_bytes: NATIVE_COMMAND_OUTPUT_BUDGET_BYTES },
|
|
{
|
|
...errorOptions,
|
|
...fixedNativeAllowance(
|
|
"The native command output capture allowance",
|
|
"capture",
|
|
`${String(NATIVE_COMMAND_OUTPUT_BUDGET_BYTES)} bytes`,
|
|
),
|
|
},
|
|
),
|
|
);
|
|
return bindCleanup(
|
|
new ProviderAdapterError(IDENTITY.id, operation, {
|
|
cause: failure,
|
|
...errorOptions,
|
|
diagnostics: {
|
|
tool: failure.tool,
|
|
reason: failure.reason,
|
|
exit_code: failure.capture?.exitCode ?? failure.exitCode,
|
|
signal: failure.capture?.signal ?? null,
|
|
stdout_bytes: failure.capture?.stdoutBytes ?? 0,
|
|
stderr_bytes: failure.capture?.stderrBytes ?? 0,
|
|
...(failure.detail === null ? {} : { detail: failure.detail }),
|
|
},
|
|
}),
|
|
);
|
|
};
|