mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(cli)!: preserve native value input diagnostics (#7889)
This commit is contained in:
1 parent
83cd1e04c9
commit
76c7698ac9
5 files changed
+71
-20
No files matched your search
@@ -0,0 +1,27 @@
|
||||
import { expect, it, vi } from "vitest";
|
||||
import { EnhancedTools } from "./EnhancedTools.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
|
||||
it("preserves field diagnostics for rejected enhanced tool inputs", async () => {
|
||||
const execute = vi.fn();
|
||||
const tools = new EnhancedTools({ execute });
|
||||
const result = await tools.execute("trace_native_values", { procedure: "" });
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) throw new Error("expected an input failure");
|
||||
expect(projectAnalysisError(result.error)).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
operation: "trace_native_values",
|
||||
issues: [{ path: ["procedure"], reason: "out_of_range", minimum: 1 }],
|
||||
},
|
||||
});
|
||||
expect(execute).not.toHaveBeenCalled();
|
||||
|
||||
const sibling = await tools.execute("batch_decompile", { addresses: "bad" });
|
||||
expect(sibling.ok).toBe(false);
|
||||
if (sibling.ok) throw new Error("expected an input failure");
|
||||
expect(projectAnalysisError(sibling.error)).toMatchObject({
|
||||
details: { issues: [{ path: ["addresses"], reason: "invalid_type" }] },
|
||||
});
|
||||
expect(execute).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { z } from "zod";
|
||||
|
||||
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
import type { ContainingProcedureResolution } from "../domain/native/nativeObservation.js";
|
||||
@@ -27,6 +27,9 @@ export type ValidatedEnhancedCall = {
|
||||
|
||||
export const invalidEnhancedInput = (
|
||||
name: EnhancedToolName,
|
||||
cause: Error,
|
||||
cause: z.ZodError,
|
||||
input: unknown,
|
||||
): EnhancedResult =>
|
||||
Promise.resolve(err(new AnalysisInputError(name, { cause })));
|
||||
Promise.resolve(
|
||||
err(analysisInputErrorFromIssues(name, cause.issues, input, { cause })),
|
||||
);
|
||||
@@ -126,14 +126,14 @@ export class EnhancedTools {
|
||||
const parsed = enhancedInputSchemas.trace_native_values.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
if (name === "trace_native_ui_action") {
|
||||
const parsed =
|
||||
enhancedInputSchemas.trace_native_ui_action.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
if (name === "analyze_function" || name === "inspect_native_api")
|
||||
return this.#executeFunctionAnalysis(name, input, signal);
|
||||
@@ -145,56 +145,56 @@ export class EnhancedTools {
|
||||
);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "get_objc_classes": {
|
||||
const parsed = enhancedInputSchemas.get_objc_classes.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "get_objc_protocols": {
|
||||
const parsed = enhancedInputSchemas.get_objc_protocols.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "batch_decompile": {
|
||||
const parsed = enhancedInputSchemas.batch_decompile.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "decompile_all": {
|
||||
const parsed = enhancedInputSchemas.decompile_all.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "get_call_graph": {
|
||||
const parsed = enhancedInputSchemas.get_call_graph.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "analyze_swift_types": {
|
||||
const parsed =
|
||||
enhancedInputSchemas.analyze_swift_types.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "find_xrefs_to_name": {
|
||||
const parsed = enhancedInputSchemas.find_xrefs_to_name.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
case "binary_overview": {
|
||||
const parsed = enhancedInputSchemas.binary_overview.safeParse(input);
|
||||
return parsed.success
|
||||
? this.executeValidated({ name, input: parsed.data }, signal)
|
||||
: invalidEnhancedInput(name, parsed.error);
|
||||
: invalidEnhancedInput(name, parsed.error, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,11 +266,12 @@ export class EnhancedTools {
|
||||
): EnhancedResult {
|
||||
if (name === "trace_call_path") {
|
||||
const parsed = enhancedInputSchemas.trace_call_path.safeParse(input);
|
||||
if (!parsed.success) return invalidEnhancedInput(name, parsed.error);
|
||||
if (!parsed.success)
|
||||
return invalidEnhancedInput(name, parsed.error, input);
|
||||
return this.executeValidated({ name, input: parsed.data }, signal);
|
||||
}
|
||||
const parsed = enhancedInputSchemas.trace_feature.safeParse(input);
|
||||
if (!parsed.success) return invalidEnhancedInput(name, parsed.error);
|
||||
if (!parsed.success) return invalidEnhancedInput(name, parsed.error, input);
|
||||
return this.executeValidated({ name, input: parsed.data }, signal);
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ export class EnhancedTools {
|
||||
signal?: AbortSignal,
|
||||
): EnhancedResult {
|
||||
const parsed = nativeProcedureInputSchema.safeParse(input);
|
||||
if (!parsed.success) return invalidEnhancedInput(name, parsed.error);
|
||||
if (!parsed.success) return invalidEnhancedInput(name, parsed.error, input);
|
||||
return this.executeValidated({ name, input: parsed.data }, signal);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { nativeDispatchMetadataInputSchema } from "../domain/native/objcSwiftMetadata.js";
|
||||
import { nativeValueTraceInputSchema } from "../domain/native/nativeValueTrace.js";
|
||||
import { z } from "incur";
|
||||
import {
|
||||
bytePatternFields,
|
||||
@@ -177,9 +178,9 @@ export const registerCoreNativeCommands = (
|
||||
description: "Trace bounded native def-use and call dependencies",
|
||||
args: z.object({
|
||||
path: z.string().describe("Local native executable or app path"),
|
||||
procedure: z
|
||||
.string()
|
||||
.describe("Explicit native procedure symbol or address"),
|
||||
procedure: nativeValueTraceInputSchema.shape.procedure.describe(
|
||||
"Explicit native procedure symbol or address",
|
||||
),
|
||||
}),
|
||||
options: z.object({
|
||||
maxDepth: z
|
||||
|
||||
@@ -776,3 +776,22 @@ describe("required CLI selector discovery", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("advertises and rejects an empty native-value procedure before opening a target", async () => {
|
||||
const cli = createCli({});
|
||||
const schema = await advertisedSchema(cli, "trace-native-values");
|
||||
expect(schema.args?.properties.procedure).toMatchObject({ minLength: 1 });
|
||||
const result = await serve(cli, [
|
||||
"trace-native-values",
|
||||
"/missing",
|
||||
"",
|
||||
"--json",
|
||||
]);
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(JSON.parse(result.stdout)).toMatchObject({
|
||||
code: "VALIDATION_ERROR",
|
||||
fieldErrors: expect.arrayContaining([
|
||||
expect.objectContaining({ path: "procedure" }),
|
||||
]),
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user