mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
feat(cli): accept JavaScript source selections from a JSON array or file (#5029)
* feat(cli): accept JavaScript source selections from a JSON array or file Add --source-paths-json to analyze and analyze-javascript-application. It takes an inline JSON array or a JSON file path of exact inventoried paths and appends them after any --source-path values, so a long selection no longer has to fit on one command line (cmd.exe through npx on Windows stops at 8,191 characters). Entries stay artifact-relative and go through the existing source_paths validation; the native lane rejects the option like --source-path. Refs #4987 * fix(cli): cancel --source-paths-json file reads with the command Pass the command's abort signal through parseCliJsonInput to readJsonInputFile, so SIGINT/SIGTERM stop a selection-file read instead of waiting for it to finish. A cancelled read now propagates its abort rather than being reported as a file read failure. * test(cli): preserve ASAR identity for JSON file selections * docs(cli): record source-selection task costs and identity verification --------- Co-authored-by: KJLavender <KJLavender@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
@@ -18,7 +18,10 @@ import {
|
||||
snapshotSelectionOption,
|
||||
} from "./options.js";
|
||||
import type { CliInstance } from "./types.js";
|
||||
import { runCliJavaScriptApplicationAnalysis } from "./javascriptApplicationAnalysis.js";
|
||||
import {
|
||||
cliJavaScriptSourcePaths,
|
||||
runCliJavaScriptApplicationAnalysis,
|
||||
} from "./javascriptApplicationAnalysis.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import {
|
||||
analysisEvidenceDeliveryOptions,
|
||||
@@ -98,6 +101,7 @@ const registerOverviewCommands = (
|
||||
options: overviewOptions.extend({
|
||||
integrityPolicy: laneIntegrityPolicy,
|
||||
sourcePath: javascriptApplicationOptions.shape.sourcePath,
|
||||
sourcePathsJson: javascriptApplicationOptions.shape.sourcePathsJson,
|
||||
}),
|
||||
run: async ({ args, options }) => {
|
||||
// Route JavaScript targets exactly like analyze-javascript-application,
|
||||
@@ -105,23 +109,32 @@ const registerOverviewCommands = (
|
||||
// code is not replaced by the logged failure status.
|
||||
if (await routesToJavaScriptAnalysis(args.path, options))
|
||||
return withCommandCancellation((signal) =>
|
||||
logCliCommand(logger, "analyze", () =>
|
||||
runCliJavaScriptApplicationAnalysis(
|
||||
logCliCommand(logger, "analyze", async () => {
|
||||
const sourcePaths = await cliJavaScriptSourcePaths(
|
||||
options.sourcePath,
|
||||
options.sourcePathsJson,
|
||||
signal,
|
||||
);
|
||||
if (!sourcePaths.ok) return sourcePaths.error;
|
||||
return runCliJavaScriptApplicationAnalysis(
|
||||
{
|
||||
input_path: absoluteCallerPath(args.path),
|
||||
integrity_policy:
|
||||
options.integrityPolicy ?? defaultIntegrityPolicy,
|
||||
...(options.sourcePath === undefined
|
||||
...(sourcePaths.value === undefined
|
||||
? {}
|
||||
: { source_paths: options.sourcePath }),
|
||||
: { source_paths: sourcePaths.value }),
|
||||
},
|
||||
environment,
|
||||
signal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
return logCliCommand(logger, "analyze", () => {
|
||||
if (options.sourcePath !== undefined)
|
||||
if (
|
||||
options.sourcePath !== undefined ||
|
||||
options.sourcePathsJson !== undefined
|
||||
)
|
||||
throw new AnalysisInputError(
|
||||
"analyze_javascript_application",
|
||||
undefined,
|
||||
|
||||
@@ -22,7 +22,10 @@ import {
|
||||
javascriptApplicationOptions,
|
||||
selectedCommandTimeout,
|
||||
} from "../cliObservationOptions.js";
|
||||
import { runCliJavaScriptApplicationAnalysis } from "./javascriptApplicationAnalysis.js";
|
||||
import {
|
||||
cliJavaScriptSourcePaths,
|
||||
runCliJavaScriptApplicationAnalysis,
|
||||
} from "./javascriptApplicationAnalysis.js";
|
||||
import { withCommandCancellation } from "./commandCancellation.js";
|
||||
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
||||
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
||||
@@ -322,6 +325,12 @@ const registerJavaScriptApplicationCommand = (
|
||||
options,
|
||||
);
|
||||
if (!delivery.ok) return projectAnalysisError(delivery.error);
|
||||
const sourcePaths = await cliJavaScriptSourcePaths(
|
||||
options.sourcePath,
|
||||
options.sourcePathsJson,
|
||||
signal,
|
||||
);
|
||||
if (!sourcePaths.ok) return sourcePaths.error;
|
||||
return runCliJavaScriptApplicationAnalysis(
|
||||
{
|
||||
// An empty path names no file; leave it for input validation.
|
||||
@@ -347,9 +356,9 @@ const registerJavaScriptApplicationCommand = (
|
||||
max_semantic_unknowns_per_file:
|
||||
options.maxSemanticUnknownsPerFile,
|
||||
}),
|
||||
...(options.sourcePath === undefined
|
||||
...(sourcePaths.value === undefined
|
||||
? {}
|
||||
: { source_paths: options.sourcePath }),
|
||||
: { source_paths: sourcePaths.value }),
|
||||
...(options.maxHeapMb === undefined
|
||||
? {}
|
||||
: { max_heap_mb: options.maxHeapMb }),
|
||||
|
||||
@@ -8,6 +8,46 @@ import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
||||
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
||||
import { javaScriptSourcePathsSchema } from "../domain/javascript/javascriptSourceSelection.js";
|
||||
import { parseCliJsonInput } from "../cliJsonInput.js";
|
||||
|
||||
const OPERATION = "analyze_javascript_application";
|
||||
|
||||
/**
|
||||
* Combine repeated `--source-path` values with `--source-paths-json`, an inline
|
||||
* JSON array or a JSON file. A file keeps large selections below the host
|
||||
* command-line limit; entries remain exact application-relative paths and are
|
||||
* not resolved against the working directory.
|
||||
*/
|
||||
export const cliJavaScriptSourcePaths = async (
|
||||
sourcePath: readonly string[] | undefined,
|
||||
sourcePathsJson: string | undefined,
|
||||
signal?: AbortSignal,
|
||||
): Promise<
|
||||
| { readonly ok: true; readonly value: readonly string[] | undefined }
|
||||
| { readonly ok: false; readonly error: JsonValue }
|
||||
> => {
|
||||
if (sourcePathsJson === undefined) return { ok: true, value: sourcePath };
|
||||
const input = await parseCliJsonInput(sourcePathsJson, OPERATION, signal);
|
||||
if (!input.ok) return input;
|
||||
const parsed = javaScriptSourcePathsSchema.safeParse(input.value);
|
||||
if (!parsed.success)
|
||||
return {
|
||||
ok: false,
|
||||
error: cliError(
|
||||
analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
parsed.error.issues.map((issue) => ({
|
||||
...issue,
|
||||
path: ["source_paths_json", ...issue.path],
|
||||
})),
|
||||
{ source_paths_json: input.value },
|
||||
),
|
||||
),
|
||||
};
|
||||
return { ok: true, value: [...(sourcePath ?? []), ...parsed.data] };
|
||||
};
|
||||
|
||||
/**
|
||||
* One scope for every JavaScript analysis in this process. A healthy worker
|
||||
|
||||
Reference in New Issue
Block a user