mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
Separate engine cleanup, investigation reset and terminal disposal in the shared application owner. Keep stable Evidence IDs and full Unknown revision history through engine close and target changes; serialize reset with all record writers and cleanup owners, optionally acknowledging a complete checkpoint before clearing. Bound retained representation without eviction. Validation: 43 focused files / 533 distinct tests, source/type/lint/schema/documentation checks, nine genuine source cases across eight families, real Ghidra session identity and cleanup, and nine actual local Codex CLI terra/high runs with independent full-result and checkpoint audits. See docs/verification/investigation-lifecycle.md for measurements, partial attempts and verification limits. Fixes #8347. BREAKING CHANGE: close_binary retains investigation records. Use reset_investigation to clear them. Cross-process recovery requires an explicitly acknowledged Evidence bundle checkpoint. Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
124 lines
4.7 KiB
TypeScript
124 lines
4.7 KiB
TypeScript
import { executableFormatHintSchema } from "../domain/dosCom.js";
|
|
import { absoluteLocalPathStringSchema } from "../domain/localPath.js";
|
|
import {
|
|
binaryArchitectureSchema,
|
|
rawBinaryEntryPointSchema,
|
|
rawBinarySelectionError,
|
|
} from "../domain/rawBinary.js";
|
|
import { z } from "zod";
|
|
import {
|
|
espFlashAddressSchema,
|
|
espFlashSelectionError,
|
|
parseEspFlashAddress,
|
|
} from "../domain/firmware/espFlashSelection.js";
|
|
import { analysisProviderSelectorSchema } from "../domain/providerSelection.js";
|
|
|
|
/** Caller-supplied analysis snapshot path; must name its destination absolutely. */
|
|
const snapshotPathSchema = absoluteLocalPathStringSchema.describe(
|
|
"Absolute local filesystem path for the analysis snapshot; relative paths are rejected.",
|
|
);
|
|
|
|
/** Caller-supplied analysis target path; must name the target absolutely. */
|
|
const binaryTargetPathSchema = absoluteLocalPathStringSchema.describe(
|
|
"Absolute local filesystem path for the analysis target; relative paths are rejected.",
|
|
);
|
|
|
|
/** Input contract for opening a target with an optional staged snapshot. */
|
|
export const openBinaryInputSchema = z
|
|
.strictObject({
|
|
path: binaryTargetPathSchema,
|
|
format: executableFormatHintSchema
|
|
.optional()
|
|
.describe(
|
|
"Explicit interpretation: dos-com or raw-binary (headerless), gamecube-dol (GameCube/Wii DOL, never auto-detected), or esp-flash-image (an ESP flash dump read with flash_offset and partition_table_offset). Omission preserves header-based detection.",
|
|
),
|
|
architecture: binaryArchitectureSchema
|
|
.optional()
|
|
.describe(
|
|
"Caller-supplied CPU family. Required with format=raw-binary; rejected otherwise. format=gamecube-dol takes powerpc from the header, so it must be omitted.",
|
|
),
|
|
entry_point: rawBinaryEntryPointSchema
|
|
.optional()
|
|
.describe(
|
|
"Caller-supplied load and entry address (0xhex). File offset 0 is mapped here. Required with format=raw-binary; rejected otherwise. Optional with format=gamecube-dol; must equal the DOL header entry.",
|
|
),
|
|
flash_offset: espFlashAddressSchema
|
|
.optional()
|
|
.describe(
|
|
"Flash address (0xhex) of the dump's first byte, when it does not start at flash address 0. Only with format=esp-flash-image.",
|
|
),
|
|
partition_table_offset: espFlashAddressSchema
|
|
.optional()
|
|
.describe(
|
|
"Flash address (0xhex, a multiple of 0x1000) of the partition table, which disables the table scan. Only with format=esp-flash-image.",
|
|
),
|
|
provider_id: analysisProviderSelectorSchema.optional(),
|
|
snapshot_path: snapshotPathSchema.optional(),
|
|
})
|
|
.superRefine((value, context) => {
|
|
const flash = espFlashSelectionError({
|
|
format: value.format,
|
|
flashOffset:
|
|
value.flash_offset === undefined
|
|
? undefined
|
|
: parseEspFlashAddress(value.flash_offset),
|
|
partitionTableOffset:
|
|
value.partition_table_offset === undefined
|
|
? undefined
|
|
: parseEspFlashAddress(value.partition_table_offset),
|
|
});
|
|
if (flash !== undefined)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: [
|
|
value.partition_table_offset === undefined
|
|
? "flash_offset"
|
|
: "partition_table_offset",
|
|
],
|
|
message: flash,
|
|
});
|
|
const pairing = rawBinarySelectionError({
|
|
...(value.format === undefined ? {} : { format: value.format }),
|
|
...(value.architecture === undefined
|
|
? {}
|
|
: { architecture: value.architecture }),
|
|
...(value.entry_point === undefined
|
|
? {}
|
|
: { entryPoint: value.entry_point }),
|
|
});
|
|
if (pairing === undefined) return;
|
|
context.addIssue({
|
|
code: "custom",
|
|
path:
|
|
value.format === "raw-binary"
|
|
? value.architecture === undefined
|
|
? ["architecture"]
|
|
: ["entry_point"]
|
|
: value.architecture === undefined
|
|
? ["entry_point"]
|
|
: ["architecture"],
|
|
message: pairing,
|
|
});
|
|
});
|
|
|
|
/** Open only app resources when no declared executable is available. */
|
|
export const openBundleResourcesInputSchema = z.strictObject({
|
|
path: binaryTargetPathSchema,
|
|
});
|
|
|
|
/** Input contract for closing a target after an optional atomic snapshot. */
|
|
export const closeBinaryInputSchema = z.strictObject({
|
|
snapshot_path: snapshotPathSchema.optional(),
|
|
overwrite: z.boolean().default(false),
|
|
});
|
|
|
|
/** Optional complete checkpoint before explicitly ending the current investigation. */
|
|
export const resetInvestigationInputSchema = z.strictObject({
|
|
save_path: absoluteLocalPathStringSchema
|
|
.describe(
|
|
"Absolute path for a complete Evidence bundle saved before clearing records.",
|
|
)
|
|
.optional(),
|
|
overwrite: z.boolean().default(false),
|
|
});
|