docs(cli): describe every command input
This commit is contained in:
parent
ad17fe7aa7
commit
d4be3e7f9a
|
|
@ -24,7 +24,7 @@
|
|||
<li><span><span class="tsd-kind-parameter">value</span>: <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/server/toolRegistrationOptions.ts#L62">src/server/toolRegistrationOptions.ts:62</a></li></ul></aside></div></li></ul></section></div>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/server/toolRegistrationOptions.ts#L67">src/server/toolRegistrationOptions.ts:67</a></li></ul></aside></div></li></ul></section></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export function cliCommandOptionNames(
|
|||
cli: unknown,
|
||||
name: string,
|
||||
): readonly string[];
|
||||
export function cliCommandDescriptionIssues(cli: unknown): readonly string[];
|
||||
export function serializeProductCatalog(
|
||||
catalog: ProductCatalog,
|
||||
): Promise<string>;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,31 @@ export const cliCommandOptionNames = (cli, name) => {
|
|||
);
|
||||
};
|
||||
|
||||
/** Report primary CLI fields whose generated JSON Schema lacks help text. */
|
||||
export const cliCommandDescriptionIssues = (cli) => {
|
||||
const commands = Cli.toCommands.get(cli);
|
||||
if (commands === undefined)
|
||||
throw new Error("Incur did not expose the registered REA CLI commands");
|
||||
const missing = [];
|
||||
for (const [commandName, command] of commands) {
|
||||
if ("_alias" in command) continue;
|
||||
for (const groupName of ["args", "options"]) {
|
||||
if (!(groupName in command) || command[groupName] === undefined) continue;
|
||||
const properties = z.toJSONSchema(command[groupName]).properties ?? {};
|
||||
for (const [propertyName, property] of Object.entries(properties)) {
|
||||
if (
|
||||
typeof property !== "object" ||
|
||||
property === null ||
|
||||
typeof property.description !== "string" ||
|
||||
property.description.trim() === ""
|
||||
)
|
||||
missing.push(`${commandName}.${groupName}.${propertyName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return missing;
|
||||
};
|
||||
|
||||
const assertSameNames = (label, actual, expected) => {
|
||||
const actualSet = new Set(actual);
|
||||
const expectedSet = new Set(expected);
|
||||
|
|
|
|||
417
src/cli.ts
417
src/cli.ts
|
|
@ -106,8 +106,17 @@ const registerCoreCommands = (
|
|||
logger: Logger,
|
||||
): void => {
|
||||
const overviewOptions = z.object({
|
||||
detail: z.enum(["concise", "detailed"]).default("concise"),
|
||||
limit: z.number().int().min(1).max(50).default(10),
|
||||
detail: z
|
||||
.enum(["concise", "detailed"])
|
||||
.default("concise")
|
||||
.describe("Overview detail level"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(50)
|
||||
.default(10)
|
||||
.describe("Maximum overview items to return"),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
|
|
@ -154,7 +163,11 @@ const registerCoreCommands = (
|
|||
address: z.string().describe("Procedure address"),
|
||||
}),
|
||||
options: z.object({
|
||||
snapshot: z.string().min(1).optional(),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Load and update a local analysis snapshot"),
|
||||
provider: providerSelectionOption,
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
|
|
@ -269,7 +282,11 @@ const registerXrefsCommand = (
|
|||
address: z.string().describe("Hexadecimal address"),
|
||||
}),
|
||||
options: z.object({
|
||||
snapshot: z.string().min(1).optional(),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Load and update a local analysis snapshot"),
|
||||
provider: providerSelectionOption,
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
|
|
@ -295,10 +312,29 @@ const registerTraceCommand = (
|
|||
query: z.string().min(1).describe("Literal feature query"),
|
||||
}),
|
||||
options: z.object({
|
||||
caseSensitive: z.boolean().default(false),
|
||||
limit: z.number().int().min(1).max(100).default(20),
|
||||
maxOperations: z.number().int().min(1).max(100).default(20),
|
||||
snapshot: z.string().min(1).optional(),
|
||||
caseSensitive: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Match the query with exact letter case"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(20)
|
||||
.describe("Maximum matching roots to examine"),
|
||||
maxOperations: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(20)
|
||||
.describe("Maximum analysis operations in the trace"),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Load and update a local analysis snapshot"),
|
||||
provider: providerSelectionOption,
|
||||
}),
|
||||
alias: {
|
||||
|
|
@ -351,11 +387,36 @@ const registerFunctionCommand = (
|
|||
address: z.string().describe("Procedure name or address"),
|
||||
}),
|
||||
options: z.object({
|
||||
includeAssembly: z.boolean().default(false),
|
||||
limit: z.number().int().min(1).max(500).default(100),
|
||||
maxPseudocodeChars: z.number().int().min(1).max(100_000).default(20_000),
|
||||
maxInstructions: z.number().int().min(1).max(5_000).default(500),
|
||||
snapshot: z.string().min(1).optional(),
|
||||
includeAssembly: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include bounded assembly instructions"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum referenced items to return"),
|
||||
maxPseudocodeChars: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100_000)
|
||||
.default(20_000)
|
||||
.describe("Maximum pseudocode characters to return"),
|
||||
maxInstructions: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(5_000)
|
||||
.default(500)
|
||||
.describe("Maximum assembly instructions to return"),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Load and update a local analysis snapshot"),
|
||||
provider: providerSelectionOption,
|
||||
}),
|
||||
alias: {
|
||||
|
|
@ -392,12 +453,36 @@ const registerSearchCommand = (
|
|||
pattern: z.string().min(1).describe("Literal text or regex pattern"),
|
||||
}),
|
||||
options: z.object({
|
||||
kind: z.enum(["strings", "procedures"]).default("strings"),
|
||||
mode: z.enum(["literal", "regex"]).default("literal"),
|
||||
caseSensitive: z.boolean().default(false),
|
||||
offset: z.number().int().min(0).default(0),
|
||||
limit: z.number().int().min(1).max(100).default(100),
|
||||
snapshot: z.string().min(1).optional(),
|
||||
kind: z
|
||||
.enum(["strings", "procedures"])
|
||||
.default("strings")
|
||||
.describe("Analyzed item kind to search"),
|
||||
mode: z
|
||||
.enum(["literal", "regex"])
|
||||
.default("literal")
|
||||
.describe("Interpret the pattern as literal text or a regex"),
|
||||
caseSensitive: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Match the pattern with exact letter case"),
|
||||
offset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based matching-item offset"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(100)
|
||||
.describe("Maximum matching items to return"),
|
||||
snapshot: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Load and update a local analysis snapshot"),
|
||||
provider: providerSelectionOption,
|
||||
}),
|
||||
alias: { caseSensitive: "case-sensitive" },
|
||||
|
|
@ -495,12 +580,38 @@ const registerArtifactCommands = (
|
|||
path: z.string().describe("Application or package path"),
|
||||
}),
|
||||
options: z.object({
|
||||
offset: z.number().int().min(0).default(0),
|
||||
limit: z.number().int().min(1).max(500).default(100),
|
||||
integrityPolicy: z.enum(["fail", "record-and-continue"]).default("fail"),
|
||||
integrityContinueApproved: z.boolean().default(false),
|
||||
maxIntegrityMismatches: z.number().int().min(1).max(100).default(10),
|
||||
nativeMountApproved: z.boolean().default(false),
|
||||
offset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based offset for graph nodes, occurrences, and edges"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum nodes, occurrences, and edges per result section"),
|
||||
integrityPolicy: z
|
||||
.enum(["fail", "record-and-continue"])
|
||||
.default("fail")
|
||||
.describe("Behavior when declared artifact integrity does not match"),
|
||||
integrityContinueApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve continuing after recorded integrity mismatches"),
|
||||
maxIntegrityMismatches: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(10)
|
||||
.describe("Maximum integrity mismatches to record before stopping"),
|
||||
nativeMountApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve read-only native mounting when required"),
|
||||
}),
|
||||
alias: {
|
||||
integrityPolicy: "integrity-policy",
|
||||
|
|
@ -537,7 +648,8 @@ const registerArtifactCommands = (
|
|||
occurrenceIds: z
|
||||
.array(z.string().regex(/^occ_[a-f0-9]{64}$/u))
|
||||
.min(1)
|
||||
.max(500),
|
||||
.max(500)
|
||||
.describe("Exact artifact occurrence IDs selected for extraction"),
|
||||
}),
|
||||
alias: { outputRoot: "output-root", occurrenceIds: "occurrence-ids" },
|
||||
run: ({ args }) =>
|
||||
|
|
@ -556,6 +668,49 @@ const registerArtifactCommands = (
|
|||
});
|
||||
};
|
||||
|
||||
const managedOffset = (subject: string) =>
|
||||
z.number().int().min(0).default(0).describe(`Zero-based ${subject} offset`);
|
||||
|
||||
const managedLimit = (subject: string, maximum: number, fallback: number) =>
|
||||
z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(maximum)
|
||||
.default(fallback)
|
||||
.describe(`Maximum ${subject} to return`);
|
||||
|
||||
const managedInspectionLimits = {
|
||||
maxFileBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(4_096)
|
||||
.max(1_073_741_824)
|
||||
.default(268_435_456)
|
||||
.describe("Maximum managed artifact file size accepted for inspection"),
|
||||
maxMetadataBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(256)
|
||||
.max(268_435_456)
|
||||
.default(67_108_864)
|
||||
.describe("Maximum CLI metadata region size accepted for inspection"),
|
||||
maxTableRows: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_000_000)
|
||||
.default(100_000)
|
||||
.describe("Maximum rows accepted in any CLI metadata table"),
|
||||
maxHeapItemBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576)
|
||||
.describe("Maximum bytes accepted for one CLI metadata heap item"),
|
||||
};
|
||||
|
||||
const registerManagedCommands = (
|
||||
cli: ReturnType<typeof Cli.create>,
|
||||
logger: Logger,
|
||||
|
|
@ -566,31 +721,46 @@ const registerManagedCommands = (
|
|||
path: z.string().describe("Managed PE executable or assembly path"),
|
||||
}),
|
||||
options: z.object({
|
||||
referenceOffset: z.number().int().min(0).default(0),
|
||||
referenceLimit: z.number().int().min(1).max(500).default(100),
|
||||
resourceOffset: z.number().int().min(0).default(0),
|
||||
resourceLimit: z.number().int().min(1).max(500).default(100),
|
||||
attributeOffset: z.number().int().min(0).default(0),
|
||||
attributeLimit: z.number().int().min(1).max(500).default(100),
|
||||
maxFileBytes: z
|
||||
referenceOffset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(4_096)
|
||||
.max(1_073_741_824)
|
||||
.default(268_435_456),
|
||||
maxMetadataBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(256)
|
||||
.max(268_435_456)
|
||||
.default(67_108_864),
|
||||
maxTableRows: z.number().int().min(1).max(1_000_000).default(100_000),
|
||||
maxHeapItemBytes: z
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based assembly-reference offset"),
|
||||
referenceLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum assembly references to return"),
|
||||
resourceOffset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based manifest-resource offset"),
|
||||
resourceLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum manifest resources to return"),
|
||||
attributeOffset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based assembly-attribute offset"),
|
||||
attributeLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum assembly attributes to return"),
|
||||
...managedInspectionLimits,
|
||||
}),
|
||||
alias: {
|
||||
referenceOffset: "reference-offset",
|
||||
|
|
@ -632,48 +802,38 @@ const registerManagedCommands = (
|
|||
path: z.string().describe("Managed PE executable or assembly path"),
|
||||
}),
|
||||
options: z.object({
|
||||
typeOffset: z.number().int().min(0).default(0),
|
||||
typeLimit: z.number().int().min(1).max(500).default(100),
|
||||
methodOffset: z.number().int().min(0).default(0),
|
||||
methodLimit: z.number().int().min(1).max(500).default(100),
|
||||
fieldOffset: z.number().int().min(0).default(0),
|
||||
fieldLimit: z.number().int().min(1).max(500).default(100),
|
||||
memberRefOffset: z.number().int().min(0).default(0),
|
||||
memberRefLimit: z.number().int().min(1).max(500).default(100),
|
||||
edgeOffset: z.number().int().min(0).default(0),
|
||||
edgeLimit: z.number().int().min(1).max(1_000).default(250),
|
||||
instructionAnchorLimit: z.number().int().min(0).max(500).default(100),
|
||||
maxFileBytes: z
|
||||
typeOffset: managedOffset("type definition"),
|
||||
typeLimit: managedLimit("type definitions", 500, 100),
|
||||
methodOffset: managedOffset("method definition"),
|
||||
methodLimit: managedLimit("method definitions", 500, 100),
|
||||
fieldOffset: managedOffset("field definition"),
|
||||
fieldLimit: managedLimit("field definitions", 500, 100),
|
||||
memberRefOffset: managedOffset("member reference"),
|
||||
memberRefLimit: managedLimit("member references", 500, 100),
|
||||
edgeOffset: managedOffset("member edge"),
|
||||
edgeLimit: managedLimit("member edges", 1_000, 250),
|
||||
instructionAnchorLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(4_096)
|
||||
.max(1_073_741_824)
|
||||
.default(268_435_456),
|
||||
maxMetadataBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(256)
|
||||
.max(268_435_456)
|
||||
.default(67_108_864),
|
||||
maxTableRows: z.number().int().min(1).max(1_000_000).default(100_000),
|
||||
maxHeapItemBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
.min(0)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum CIL instruction anchors per method"),
|
||||
...managedInspectionLimits,
|
||||
maxMethodBodyBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
.default(1_048_576)
|
||||
.describe("Maximum CIL method body size accepted for inspection"),
|
||||
maxMethodInstructions: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100_000)
|
||||
.default(10_000),
|
||||
.default(10_000)
|
||||
.describe("Maximum decoded CIL instructions per method"),
|
||||
}),
|
||||
alias: {
|
||||
typeOffset: "type-offset",
|
||||
|
|
@ -729,31 +889,13 @@ const registerManagedCommands = (
|
|||
path: z.string().describe("Managed PE executable or assembly path"),
|
||||
}),
|
||||
options: z.object({
|
||||
moduleRefOffset: z.number().int().min(0).default(0),
|
||||
moduleRefLimit: z.number().int().min(1).max(500).default(100),
|
||||
importOffset: z.number().int().min(0).default(0),
|
||||
importLimit: z.number().int().min(1).max(500).default(100),
|
||||
implementationOffset: z.number().int().min(0).default(0),
|
||||
implementationLimit: z.number().int().min(1).max(500).default(100),
|
||||
maxFileBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(4_096)
|
||||
.max(1_073_741_824)
|
||||
.default(268_435_456),
|
||||
maxMetadataBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(256)
|
||||
.max(268_435_456)
|
||||
.default(67_108_864),
|
||||
maxTableRows: z.number().int().min(1).max(1_000_000).default(100_000),
|
||||
maxHeapItemBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
moduleRefOffset: managedOffset("module reference"),
|
||||
moduleRefLimit: managedLimit("module references", 500, 100),
|
||||
importOffset: managedOffset("platform import"),
|
||||
importLimit: managedLimit("platform imports", 500, 100),
|
||||
implementationOffset: managedOffset("native implementation"),
|
||||
implementationLimit: managedLimit("native implementations", 500, 100),
|
||||
...managedInspectionLimits,
|
||||
}),
|
||||
alias: {
|
||||
moduleRefOffset: "module-ref-offset",
|
||||
|
|
@ -800,46 +942,54 @@ const registerManagedCommands = (
|
|||
.describe("Candidate managed PE executable or assembly"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxMethodMatches: z.number().int().min(1).max(50_000).default(10_000),
|
||||
maxFieldMatches: z.number().int().min(0).max(50_000).default(5_000),
|
||||
maxCandidates: z.number().int().min(1).max(500).default(50),
|
||||
typeLimit: z.number().int().min(1).max(500).default(500),
|
||||
methodLimit: z.number().int().min(1).max(500).default(500),
|
||||
fieldLimit: z.number().int().min(1).max(500).default(500),
|
||||
memberRefLimit: z.number().int().min(1).max(500).default(500),
|
||||
edgeLimit: z.number().int().min(1).max(1_000).default(1_000),
|
||||
instructionAnchorLimit: z.number().int().min(0).max(500).default(500),
|
||||
maxFileBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(4_096)
|
||||
.max(1_073_741_824)
|
||||
.default(268_435_456),
|
||||
maxMetadataBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(256)
|
||||
.max(268_435_456)
|
||||
.default(67_108_864),
|
||||
maxTableRows: z.number().int().min(1).max(1_000_000).default(100_000),
|
||||
maxHeapItemBytes: z
|
||||
maxMethodMatches: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
.max(50_000)
|
||||
.default(10_000)
|
||||
.describe("Maximum exact method matches to return"),
|
||||
maxFieldMatches: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(50_000)
|
||||
.default(5_000)
|
||||
.describe("Maximum exact field matches to return"),
|
||||
maxCandidates: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(50)
|
||||
.describe("Maximum ambiguous candidates per unmatched member"),
|
||||
typeLimit: managedLimit("type definitions per artifact", 500, 500),
|
||||
methodLimit: managedLimit("method definitions per artifact", 500, 500),
|
||||
fieldLimit: managedLimit("field definitions per artifact", 500, 500),
|
||||
memberRefLimit: managedLimit("member references per artifact", 500, 500),
|
||||
edgeLimit: managedLimit("member edges per artifact", 1_000, 1_000),
|
||||
instructionAnchorLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(500)
|
||||
.default(500)
|
||||
.describe("Maximum CIL instruction anchors per compared method"),
|
||||
...managedInspectionLimits,
|
||||
maxMethodBodyBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16_777_216)
|
||||
.default(1_048_576),
|
||||
.default(1_048_576)
|
||||
.describe("Maximum CIL method body size accepted for comparison"),
|
||||
maxMethodInstructions: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100_000)
|
||||
.default(100_000),
|
||||
.default(100_000)
|
||||
.describe("Maximum decoded CIL instructions per compared method"),
|
||||
}),
|
||||
alias: {
|
||||
maxMethodMatches: "max-method-matches",
|
||||
|
|
@ -1006,7 +1156,10 @@ const registerNativeCommands = (
|
|||
description: "Parse app plist metadata without launching Hopper",
|
||||
args: z.object({ path: z.string().describe("App or Mach-O path") }),
|
||||
options: z.object({
|
||||
relativePath: z.string().default("Contents/Info.plist"),
|
||||
relativePath: z
|
||||
.string()
|
||||
.default("Contents/Info.plist")
|
||||
.describe("Plist path relative to the app root"),
|
||||
}),
|
||||
alias: { relativePath: "relative-path" },
|
||||
run: ({ args, options }) =>
|
||||
|
|
@ -1023,7 +1176,11 @@ const registerNativeCommands = (
|
|||
description: "Demangle a bounded Swift symbol batch without Hopper",
|
||||
args: z.object({
|
||||
path: z.string().describe("Artifact path used for evidence identity"),
|
||||
symbols: z.array(z.string().min(1)).min(1).max(500),
|
||||
symbols: z
|
||||
.array(z.string().min(1))
|
||||
.min(1)
|
||||
.max(500)
|
||||
.describe("Swift mangled symbols to demangle"),
|
||||
}),
|
||||
run: ({ args }) =>
|
||||
logCliCommand(logger, "demangle-swift", () =>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,13 @@ import type { Logger } from "./logger.js";
|
|||
import { CLI_COMMANDS } from "./cliCommandNames.js";
|
||||
|
||||
const scopeOptions = {
|
||||
allowedOrigins: z.array(z.string().min(1)).optional(),
|
||||
approved: z.boolean().default(false),
|
||||
allowedOrigins: z
|
||||
.array(z.string().min(1))
|
||||
.optional()
|
||||
.describe(
|
||||
"Exact origins to observe; defaults to REA_BROWSER_ALLOWED_ORIGINS_JSON",
|
||||
),
|
||||
approved: z.boolean().default(false).describe("Approve passive observation"),
|
||||
};
|
||||
|
||||
/** Register WebMCP, capture-diff, and screenshot CLI equivalents. */
|
||||
|
|
@ -43,19 +48,47 @@ const registerWebMcp = (
|
|||
): void => {
|
||||
cli.command(CLI_COMMANDS.discoverWebMcpTools, {
|
||||
description: "Passively discover page-declared WebMCP tools",
|
||||
args: z.object({ endpoint: z.string(), targetId: z.string() }),
|
||||
args: z.object({
|
||||
endpoint: z.string().describe("Configured loopback CDP HTTP endpoint"),
|
||||
targetId: z.string().describe("Target ID from list-browser-targets"),
|
||||
}),
|
||||
options: z.object({
|
||||
...scopeOptions,
|
||||
observationMs: z.number().int().min(0).max(10_000).default(100),
|
||||
maxTools: z.number().int().min(1).max(5_000).default(500),
|
||||
observationMs: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(10_000)
|
||||
.default(100)
|
||||
.describe("Observation duration in milliseconds"),
|
||||
maxTools: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(5_000)
|
||||
.default(500)
|
||||
.describe("Maximum page-declared WebMCP tools to return"),
|
||||
maxSchemaBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_024 * 1_024)
|
||||
.default(256 * 1_024),
|
||||
maxSchemaNodes: z.number().int().min(1).max(100_000).default(5_000),
|
||||
maxSchemaDepth: z.number().int().min(1).max(100).default(20),
|
||||
.default(256 * 1_024)
|
||||
.describe("Maximum serialized size of one declared tool schema"),
|
||||
maxSchemaNodes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100_000)
|
||||
.default(5_000)
|
||||
.describe("Maximum nodes in one declared tool schema"),
|
||||
maxSchemaDepth: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(20)
|
||||
.describe("Maximum nesting depth in one declared tool schema"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "discover-webmcp-tools", async () => {
|
||||
|
|
@ -90,9 +123,18 @@ const registerCaptureDiff = (
|
|||
): void => {
|
||||
cli.command(CLI_COMMANDS.compareWebCaptures, {
|
||||
description: "Compare two normalized web capture JSON values",
|
||||
args: z.object({ beforeJson: z.string(), afterJson: z.string() }),
|
||||
args: z.object({
|
||||
beforeJson: z.string().describe("Earlier normalized web capture JSON"),
|
||||
afterJson: z.string().describe("Later normalized web capture JSON"),
|
||||
}),
|
||||
options: z.object({
|
||||
maxChanges: z.number().int().min(1).max(20_000).default(2_000),
|
||||
maxChanges: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(20_000)
|
||||
.default(2_000)
|
||||
.describe("Maximum normalized changes to return"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "compare-web-captures", async () => {
|
||||
|
|
@ -119,16 +161,23 @@ const registerScreenshot = (
|
|||
): void => {
|
||||
cli.command(CLI_COMMANDS.captureWebScreenshot, {
|
||||
description: "Capture an explicitly approved visible page viewport",
|
||||
args: z.object({ endpoint: z.string(), targetId: z.string() }),
|
||||
args: z.object({
|
||||
endpoint: z.string().describe("Configured loopback CDP HTTP endpoint"),
|
||||
targetId: z.string().describe("Target ID from list-browser-targets"),
|
||||
}),
|
||||
options: z.object({
|
||||
...scopeOptions,
|
||||
screenshotApproved: z.boolean().default(false),
|
||||
screenshotApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve capturing the visible page viewport"),
|
||||
maximumImageBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(8 * 1_024 * 1_024)
|
||||
.default(4 * 1_024 * 1_024),
|
||||
.default(4 * 1_024 * 1_024)
|
||||
.describe("Maximum captured PNG size"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "capture-web-screenshot", async () => {
|
||||
|
|
@ -160,15 +209,25 @@ const registerScreenshotDiff = (
|
|||
): void => {
|
||||
cli.command(CLI_COMMANDS.compareWebScreenshots, {
|
||||
description: "Compare two self-verifying PNG artifact JSON values",
|
||||
args: z.object({ beforeJson: z.string(), afterJson: z.string() }),
|
||||
args: z.object({
|
||||
beforeJson: z.string().describe("Earlier screenshot artifact JSON"),
|
||||
afterJson: z.string().describe("Later screenshot artifact JSON"),
|
||||
}),
|
||||
options: z.object({
|
||||
channelThreshold: z.number().int().min(0).max(255).default(0),
|
||||
channelThreshold: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(255)
|
||||
.default(0)
|
||||
.describe("Per-channel difference threshold for changed pixels"),
|
||||
maximumPixels: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(32_000_000)
|
||||
.default(16_000_000),
|
||||
.default(16_000_000)
|
||||
.describe("Maximum decoded pixels per screenshot"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "compare-web-screenshots", async () => {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,23 @@ const browserScopeOptions = {
|
|||
approved: z.boolean().default(false).describe("Approve passive observation"),
|
||||
};
|
||||
|
||||
const boundedCount = (
|
||||
subject: string,
|
||||
maximum: number,
|
||||
fallback: number,
|
||||
minimum = 1,
|
||||
) =>
|
||||
z
|
||||
.number()
|
||||
.int()
|
||||
.min(minimum)
|
||||
.max(maximum)
|
||||
.default(fallback)
|
||||
.describe(`Maximum ${subject}`);
|
||||
|
||||
const boundedBytes = (subject: string, maximum: number, fallback: number) =>
|
||||
boundedCount(`${subject} in bytes`, maximum, fallback);
|
||||
|
||||
/** Register CLI equivalents of the passive browser MCP tools. */
|
||||
export const registerBrowserCommands = (
|
||||
cli: ReturnType<typeof Cli.create>,
|
||||
|
|
@ -53,8 +70,13 @@ const registerTargetList = (
|
|||
}),
|
||||
options: z.object({
|
||||
...browserScopeOptions,
|
||||
offset: z.number().int().min(0).default(0),
|
||||
limit: z.number().int().min(1).max(200).default(100),
|
||||
offset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based browser-target offset"),
|
||||
limit: boundedCount("browser targets to return", 200, 100),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "list-browser-targets", async () => {
|
||||
|
|
@ -92,88 +114,114 @@ const registerPageInspection = (
|
|||
}),
|
||||
options: z.object({
|
||||
...browserScopeOptions,
|
||||
observationMs: z.number().int().min(0).max(10_000).default(500),
|
||||
includeAccessibilityText: z.boolean().default(false),
|
||||
includeConsoleText: z.boolean().default(false),
|
||||
consoleTextApproved: z.boolean().default(false),
|
||||
includeJsonBodyShapes: z.boolean().default(false),
|
||||
jsonBodySchemaApproved: z.boolean().default(false),
|
||||
includeWebsocketShapes: z.boolean().default(false),
|
||||
websocketShapeApproved: z.boolean().default(false),
|
||||
includeScriptSources: z.boolean().default(false),
|
||||
includeStorageKeys: z.boolean().default(false),
|
||||
maxFrames: z.number().int().min(1).max(1_000).default(200),
|
||||
maxDomNodes: z.number().int().min(1).max(10_000).default(2_000),
|
||||
maxAxNodes: z.number().int().min(1).max(10_000).default(2_000),
|
||||
maxAxTextFieldBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024)
|
||||
.default(1_024),
|
||||
maxTotalAxTextBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_024 * 1_024)
|
||||
.default(64 * 1_024),
|
||||
maxScripts: z.number().int().min(1).max(1_000).default(200),
|
||||
maxResources: z.number().int().min(1).max(10_000).default(2_000),
|
||||
maxWorkers: z.number().int().min(1).max(5_000).default(500),
|
||||
maxStorageKeys: z.number().int().min(1).max(10_000).default(1_000),
|
||||
maxScriptSourceBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(4 * 1_024 * 1_024)
|
||||
.default(1_024 * 1_024),
|
||||
maxTotalScriptSourceBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024 * 1_024)
|
||||
.default(4 * 1_024 * 1_024),
|
||||
maxNetworkEvents: z.number().int().min(1).max(10_000).default(1_000),
|
||||
maxConsoleEvents: z.number().int().min(1).max(2_000).default(200),
|
||||
maxConsoleTextFieldBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024)
|
||||
.default(1_024),
|
||||
maxTotalConsoleTextBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_024 * 1_024)
|
||||
.default(64 * 1_024),
|
||||
maxJsonBodyBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(4 * 1_024 * 1_024)
|
||||
.default(1_024 * 1_024),
|
||||
maxTotalJsonBodyBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024 * 1_024)
|
||||
.default(4 * 1_024 * 1_024),
|
||||
maxJsonShapeNodes: z.number().int().min(1).max(100_000).default(5_000),
|
||||
maxJsonShapeDepth: z.number().int().min(1).max(100).default(20),
|
||||
maxWebsocketEvents: z.number().int().min(1).max(5_000).default(500),
|
||||
maxWebsocketShapeBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_024 * 1_024)
|
||||
.default(64 * 1_024),
|
||||
maxTotalWebsocketShapeBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024 * 1_024)
|
||||
.default(1_024 * 1_024),
|
||||
observationMs: boundedCount(
|
||||
"observation duration in milliseconds",
|
||||
10_000,
|
||||
500,
|
||||
0,
|
||||
),
|
||||
includeAccessibilityText: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include bounded accessibility text"),
|
||||
includeConsoleText: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include bounded console message text"),
|
||||
consoleTextApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve capturing console message text"),
|
||||
includeJsonBodyShapes: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include structural shapes of JSON response bodies"),
|
||||
jsonBodySchemaApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe(
|
||||
"Approve inspecting JSON response bodies for structural shapes",
|
||||
),
|
||||
includeWebsocketShapes: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include structural shapes of WebSocket payloads"),
|
||||
websocketShapeApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe(
|
||||
"Approve inspecting WebSocket payloads for structural shapes",
|
||||
),
|
||||
includeScriptSources: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include bounded JavaScript source text"),
|
||||
includeStorageKeys: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include storage key names without values"),
|
||||
maxFrames: boundedCount("page frames", 1_000, 200),
|
||||
maxDomNodes: boundedCount("DOM nodes", 10_000, 2_000),
|
||||
maxAxNodes: boundedCount("accessibility nodes", 10_000, 2_000),
|
||||
maxAxTextFieldBytes: boundedBytes(
|
||||
"one accessibility text field",
|
||||
16 * 1_024,
|
||||
1_024,
|
||||
),
|
||||
maxTotalAxTextBytes: boundedBytes(
|
||||
"total accessibility text",
|
||||
1_024 * 1_024,
|
||||
64 * 1_024,
|
||||
),
|
||||
maxScripts: boundedCount("scripts", 1_000, 200),
|
||||
maxResources: boundedCount("resources", 10_000, 2_000),
|
||||
maxWorkers: boundedCount("workers", 5_000, 500),
|
||||
maxStorageKeys: boundedCount("storage keys", 10_000, 1_000),
|
||||
maxScriptSourceBytes: boundedBytes(
|
||||
"one script source",
|
||||
4 * 1_024 * 1_024,
|
||||
1_024 * 1_024,
|
||||
),
|
||||
maxTotalScriptSourceBytes: boundedBytes(
|
||||
"total script source",
|
||||
16 * 1_024 * 1_024,
|
||||
4 * 1_024 * 1_024,
|
||||
),
|
||||
maxNetworkEvents: boundedCount("network events", 10_000, 1_000),
|
||||
maxConsoleEvents: boundedCount("console events", 2_000, 200),
|
||||
maxConsoleTextFieldBytes: boundedBytes(
|
||||
"one console text field",
|
||||
16 * 1_024,
|
||||
1_024,
|
||||
),
|
||||
maxTotalConsoleTextBytes: boundedBytes(
|
||||
"total console text",
|
||||
1_024 * 1_024,
|
||||
64 * 1_024,
|
||||
),
|
||||
maxJsonBodyBytes: boundedBytes(
|
||||
"one JSON response body",
|
||||
4 * 1_024 * 1_024,
|
||||
1_024 * 1_024,
|
||||
),
|
||||
maxTotalJsonBodyBytes: boundedBytes(
|
||||
"total JSON response bodies",
|
||||
16 * 1_024 * 1_024,
|
||||
4 * 1_024 * 1_024,
|
||||
),
|
||||
maxJsonShapeNodes: boundedCount("JSON shape nodes", 100_000, 5_000),
|
||||
maxJsonShapeDepth: boundedCount("JSON shape depth", 100, 20),
|
||||
maxWebsocketEvents: boundedCount("WebSocket events", 5_000, 500),
|
||||
maxWebsocketShapeBytes: boundedBytes(
|
||||
"one WebSocket shape",
|
||||
1_024 * 1_024,
|
||||
64 * 1_024,
|
||||
),
|
||||
maxTotalWebsocketShapeBytes: boundedBytes(
|
||||
"total WebSocket shapes",
|
||||
16 * 1_024 * 1_024,
|
||||
1_024 * 1_024,
|
||||
),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inspect-web-page", async () => {
|
||||
|
|
@ -245,32 +293,45 @@ const registerBundleAnalysis = (
|
|||
}),
|
||||
options: z.object({
|
||||
...browserScopeOptions,
|
||||
sourceCaptureApproved: z.boolean().default(false),
|
||||
observationMs: z.number().int().min(0).max(10_000).default(500),
|
||||
fetchSourceMaps: z.boolean().default(false),
|
||||
sourceMapFetchApproved: z.boolean().default(false),
|
||||
maxFindings: z.number().int().min(1).max(10_000).default(1_000),
|
||||
maxAstNodes: z.number().int().min(1).max(2_000_000).default(250_000),
|
||||
maxSourceMaps: z.number().int().min(1).max(1_000).default(100),
|
||||
maxSourceMapBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024 * 1_024)
|
||||
.default(4 * 1_024 * 1_024),
|
||||
maxTotalSourceMapBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(64 * 1_024 * 1_024)
|
||||
.default(16 * 1_024 * 1_024),
|
||||
maxSourceMapMappings: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100_000)
|
||||
.default(10_000),
|
||||
maxOriginalSources: z.number().int().min(1).max(20_000).default(2_000),
|
||||
sourceCaptureApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve capturing bounded script source text"),
|
||||
observationMs: boundedCount(
|
||||
"observation duration in milliseconds",
|
||||
10_000,
|
||||
500,
|
||||
0,
|
||||
),
|
||||
fetchSourceMaps: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Fetch source maps referenced by captured scripts"),
|
||||
sourceMapFetchApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe(
|
||||
"Approve fetching referenced source maps from allowed origins",
|
||||
),
|
||||
maxFindings: boundedCount("static-analysis findings", 10_000, 1_000),
|
||||
maxAstNodes: boundedCount("JavaScript AST nodes", 2_000_000, 250_000),
|
||||
maxSourceMaps: boundedCount("source maps", 1_000, 100),
|
||||
maxSourceMapBytes: boundedBytes(
|
||||
"one source map",
|
||||
16 * 1_024 * 1_024,
|
||||
4 * 1_024 * 1_024,
|
||||
),
|
||||
maxTotalSourceMapBytes: boundedBytes(
|
||||
"total source maps",
|
||||
64 * 1_024 * 1_024,
|
||||
16 * 1_024 * 1_024,
|
||||
),
|
||||
maxSourceMapMappings: boundedCount(
|
||||
"decoded source-map mappings",
|
||||
100_000,
|
||||
10_000,
|
||||
),
|
||||
maxOriginalSources: boundedCount("original source files", 20_000, 2_000),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "analyze-web-bundle", async () => {
|
||||
|
|
@ -321,8 +382,12 @@ const registerObservationSession = (
|
|||
}),
|
||||
options: z.object({
|
||||
...browserScopeOptions,
|
||||
observationMs: z.number().int().min(1).max(60_000).default(10_000),
|
||||
maxTimelineEvents: z.number().int().min(1).max(20_000).default(2_000),
|
||||
observationMs: boundedCount(
|
||||
"observation duration in milliseconds",
|
||||
60_000,
|
||||
10_000,
|
||||
),
|
||||
maxTimelineEvents: boundedCount("timeline events", 20_000, 2_000),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "observe-web-session", async () => {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const scopeOptions = {
|
|||
.array(z.string().min(1))
|
||||
.optional()
|
||||
.describe("Filesystem roots; defaults to REA_ELECTRON_FILE_ROOTS_JSON"),
|
||||
approved: z.boolean().default(false),
|
||||
approved: z.boolean().default(false).describe("Approve passive observation"),
|
||||
};
|
||||
|
||||
/** Register CLI equivalents of the Electron MCP tools. */
|
||||
|
|
@ -76,11 +76,26 @@ const registerElectronObservationCommands = (
|
|||
): void => {
|
||||
cli.command(CLI_COMMANDS.listElectronTargets, {
|
||||
description: "List root-confined file pages from Electron CDP",
|
||||
args: z.object({ endpoint: z.string() }),
|
||||
args: z.object({
|
||||
endpoint: z
|
||||
.string()
|
||||
.describe("Configured loopback Electron CDP endpoint"),
|
||||
}),
|
||||
options: z.object({
|
||||
...scopeOptions,
|
||||
offset: z.number().int().min(0).default(0),
|
||||
limit: z.number().int().min(1).max(200).default(100),
|
||||
offset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based Electron-target offset"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(200)
|
||||
.default(100)
|
||||
.describe("Maximum Electron targets to return"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "list-electron-targets", async () => {
|
||||
|
|
@ -105,29 +120,78 @@ const registerElectronObservationCommands = (
|
|||
});
|
||||
cli.command(CLI_COMMANDS.inspectElectronPage, {
|
||||
description: "Passively inspect one root-confined Electron file page",
|
||||
args: z.object({ endpoint: z.string(), targetId: z.string() }),
|
||||
args: z.object({
|
||||
endpoint: z
|
||||
.string()
|
||||
.describe("Configured loopback Electron CDP endpoint"),
|
||||
targetId: z.string().describe("Target ID from list-electron-targets"),
|
||||
}),
|
||||
options: z.object({
|
||||
...scopeOptions,
|
||||
observationMs: z.number().int().min(0).max(10_000).default(100),
|
||||
includeScriptSources: z.boolean().default(false),
|
||||
sourceCaptureApproved: z.boolean().default(false),
|
||||
maxFrames: z.number().int().min(1).max(1_000).default(200),
|
||||
maxDomNodes: z.number().int().min(1).max(10_000).default(2_000),
|
||||
maxScripts: z.number().int().min(1).max(2_000).default(500),
|
||||
maxResources: z.number().int().min(1).max(10_000).default(2_000),
|
||||
maxWorkers: z.number().int().min(1).max(5_000).default(500),
|
||||
observationMs: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.max(10_000)
|
||||
.default(100)
|
||||
.describe("Observation duration in milliseconds"),
|
||||
includeScriptSources: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Include bounded JavaScript source text"),
|
||||
sourceCaptureApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve capturing bounded script source text"),
|
||||
maxFrames: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(1_000)
|
||||
.default(200)
|
||||
.describe("Maximum page frames"),
|
||||
maxDomNodes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(10_000)
|
||||
.default(2_000)
|
||||
.describe("Maximum DOM nodes"),
|
||||
maxScripts: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(2_000)
|
||||
.default(500)
|
||||
.describe("Maximum scripts"),
|
||||
maxResources: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(10_000)
|
||||
.default(2_000)
|
||||
.describe("Maximum resources"),
|
||||
maxWorkers: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(5_000)
|
||||
.default(500)
|
||||
.describe("Maximum workers"),
|
||||
maxScriptSourceBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(4 * 1_024 * 1_024)
|
||||
.default(1_024 * 1_024),
|
||||
.default(1_024 * 1_024)
|
||||
.describe("Maximum size of one captured script source"),
|
||||
maxTotalScriptSourceBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(16 * 1_024 * 1_024)
|
||||
.default(4 * 1_024 * 1_024),
|
||||
.default(4 * 1_024 * 1_024)
|
||||
.describe("Maximum total captured script source size"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inspect-electron-page", async () => {
|
||||
|
|
@ -174,39 +238,105 @@ const registerJavaScriptApplicationCommand = (
|
|||
path: z.string().describe("Absolute ASAR or extracted application path"),
|
||||
}),
|
||||
options: z.object({
|
||||
approved: z.boolean().default(false),
|
||||
format: z.enum(["auto", "asar", "directory"]).default("auto"),
|
||||
sourceMapReadApproved: z.boolean().default(false),
|
||||
maxEntries: z.number().int().min(1).default(8_000),
|
||||
approved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve reading the application artifact"),
|
||||
format: z
|
||||
.enum(["auto", "asar", "directory"])
|
||||
.default("auto")
|
||||
.describe("Application artifact format"),
|
||||
sourceMapReadApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe(
|
||||
"Approve reading local source maps referenced by the application",
|
||||
),
|
||||
maxEntries: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(8_000)
|
||||
.describe("Maximum artifact entries to inspect"),
|
||||
maxTotalArtifactBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(512 * 1_024 * 1_024),
|
||||
.default(512 * 1_024 * 1_024)
|
||||
.describe("Maximum total uncompressed artifact content to inspect"),
|
||||
maxArtifactEntryBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(128 * 1_024 * 1_024),
|
||||
maxCompressionRatio: z.number().min(1).default(1_000),
|
||||
maxDepth: z.number().int().min(1).default(64),
|
||||
maxPathBytes: z.number().int().min(1).default(4_096),
|
||||
maxTextFiles: z.number().int().min(1).default(5_000),
|
||||
.default(128 * 1_024 * 1_024)
|
||||
.describe("Maximum uncompressed content for one artifact entry"),
|
||||
maxCompressionRatio: z
|
||||
.number()
|
||||
.min(1)
|
||||
.default(1_000)
|
||||
.describe(
|
||||
"Maximum accepted compressed-to-uncompressed expansion ratio",
|
||||
),
|
||||
maxDepth: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(64)
|
||||
.describe("Maximum artifact directory depth"),
|
||||
maxPathBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(4_096)
|
||||
.describe("Maximum encoded artifact path length"),
|
||||
maxTextFiles: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(5_000)
|
||||
.describe("Maximum text files to parse"),
|
||||
maxTotalTextBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(128 * 1_024 * 1_024),
|
||||
.default(128 * 1_024 * 1_024)
|
||||
.describe("Maximum total text content to parse"),
|
||||
maxTextFileBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(8 * 1_024 * 1_024),
|
||||
maxAstNodes: z.number().int().min(1).default(2_000_000),
|
||||
maxFindings: z.number().int().min(1).default(8_000),
|
||||
maxModules: z.number().int().min(1).default(20_000),
|
||||
maxSourceMapSources: z.number().int().min(1).default(5_000),
|
||||
maxParseMilliseconds: z.number().int().min(1).default(30_000),
|
||||
.default(8 * 1_024 * 1_024)
|
||||
.describe("Maximum size of one parsed text file"),
|
||||
maxAstNodes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(2_000_000)
|
||||
.describe("Maximum JavaScript AST nodes to parse"),
|
||||
maxFindings: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(8_000)
|
||||
.describe("Maximum static-analysis findings to return"),
|
||||
maxModules: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(20_000)
|
||||
.describe("Maximum application modules to project"),
|
||||
maxSourceMapSources: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(5_000)
|
||||
.describe("Maximum original sources from source maps"),
|
||||
maxParseMilliseconds: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.default(30_000)
|
||||
.describe("Maximum JavaScript parsing time in milliseconds"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(
|
||||
|
|
|
|||
|
|
@ -79,8 +79,19 @@ export const registerEvidenceCommands = (
|
|||
right: z.string().describe("Right Evidence bundle JSON path"),
|
||||
}),
|
||||
options: z.object({
|
||||
offset: z.number().int().min(0).default(0),
|
||||
limit: z.number().int().min(1).max(500).default(100),
|
||||
offset: z
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.default(0)
|
||||
.describe("Zero-based changed-record offset"),
|
||||
limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(100)
|
||||
.describe("Maximum changed records to return"),
|
||||
}),
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "compare", async () => {
|
||||
|
|
|
|||
|
|
@ -17,31 +17,74 @@ const investigationOptionsSchema = z.object({
|
|||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve persistent workspace writes"),
|
||||
workspaceName: z.string().trim().min(1).max(200).default("default"),
|
||||
expectedRevision: z.number().int().min(1).optional(),
|
||||
workspaceName: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(200)
|
||||
.default("default")
|
||||
.describe("Logical name recorded in the investigation workspace"),
|
||||
expectedRevision: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.optional()
|
||||
.describe("Required current workspace revision for an atomic update"),
|
||||
replayRunId: z
|
||||
.string()
|
||||
.regex(/^run_[a-f0-9]{64}$/u)
|
||||
.optional()
|
||||
.describe("Replay one verified complete workspace run without rescanning"),
|
||||
maxEntries: z.number().int().min(1).max(50_000).default(10_000),
|
||||
maxEntries: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(50_000)
|
||||
.default(10_000)
|
||||
.describe("Maximum artifact entries to inspect per version"),
|
||||
maxTotalBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(Number.MAX_SAFE_INTEGER)
|
||||
.default(1_073_741_824),
|
||||
.default(1_073_741_824)
|
||||
.describe("Maximum total uncompressed artifact content to inspect"),
|
||||
maxEntryBytes: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(Number.MAX_SAFE_INTEGER)
|
||||
.default(268_435_456),
|
||||
pageSize: z.number().int().min(1).max(500).default(500),
|
||||
changeLimit: z.number().int().min(1).max(500).default(500),
|
||||
integrityPolicy: z.enum(["fail", "record-and-continue"]).default("fail"),
|
||||
integrityContinueApproved: z.boolean().default(false),
|
||||
maxIntegrityMismatches: z.number().int().min(1).max(100).default(10),
|
||||
.default(268_435_456)
|
||||
.describe("Maximum uncompressed content to inspect for one entry"),
|
||||
pageSize: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(500)
|
||||
.describe("Maximum graph items loaded per investigation page"),
|
||||
changeLimit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(500)
|
||||
.default(500)
|
||||
.describe("Maximum version changes to record"),
|
||||
integrityPolicy: z
|
||||
.enum(["fail", "record-and-continue"])
|
||||
.default("fail")
|
||||
.describe("Behavior when declared artifact integrity does not match"),
|
||||
integrityContinueApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve continuing after recorded integrity mismatches"),
|
||||
maxIntegrityMismatches: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1)
|
||||
.max(100)
|
||||
.default(10)
|
||||
.describe("Maximum integrity mismatches to record before stopping"),
|
||||
});
|
||||
|
||||
/** Register the persistent cross-version CLI workflow. */
|
||||
|
|
|
|||
|
|
@ -58,17 +58,40 @@ export const registerPolicyCommands = (
|
|||
cli.command(CLI_COMMANDS.policy, {
|
||||
description: "Inspect, explain, or revoke unified local permission grants",
|
||||
args: z.object({
|
||||
action: z.enum(["status", "list", "explain", "revoke"]),
|
||||
value: z.string().optional(),
|
||||
action: z
|
||||
.enum(["status", "list", "explain", "revoke"])
|
||||
.describe("Permission-policy operation to perform"),
|
||||
value: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Capability to explain or grant ID to revoke"),
|
||||
}),
|
||||
options: z.object({
|
||||
root: z.string().optional(),
|
||||
executable: z.string().optional(),
|
||||
environmentNames: z.array(z.string()).default([]),
|
||||
origins: z.array(z.string()).default([]),
|
||||
network: z.enum(["none", "loopback", "external"]).default("none"),
|
||||
mount: z.boolean().default(false),
|
||||
write: z.boolean().default(false),
|
||||
root: z.string().optional().describe("Filesystem root to authorize"),
|
||||
executable: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Executable path to authorize"),
|
||||
environmentNames: z
|
||||
.array(z.string())
|
||||
.default([])
|
||||
.describe("Environment variable names the operation may read"),
|
||||
origins: z
|
||||
.array(z.string())
|
||||
.default([])
|
||||
.describe("Exact browser origins the operation may access"),
|
||||
network: z
|
||||
.enum(["none", "loopback", "external"])
|
||||
.default("none")
|
||||
.describe("Maximum network scope to authorize"),
|
||||
mount: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Authorize mounting an artifact filesystem"),
|
||||
write: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Explain the write form of the requested capability"),
|
||||
yes: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
import { ensureGeneratedFile } from "../scripts/lib/generated-file.mjs";
|
||||
import {
|
||||
createCliInventory,
|
||||
cliCommandDescriptionIssues,
|
||||
cliCommandOptionNames,
|
||||
createProductCatalog,
|
||||
serializeProductCatalog,
|
||||
|
|
@ -161,6 +162,10 @@ describe("canonical product catalog", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it("describes every primary command argument and option", () => {
|
||||
expect(cliCommandDescriptionIssues(createCli())).toEqual([]);
|
||||
});
|
||||
|
||||
it("exposes the shared provider selector on every deep-analysis command", () => {
|
||||
const cli = createCli();
|
||||
for (const name of [
|
||||
|
|
|
|||
Loading…
Reference in New Issue