* feat(artifacts): read Godot PCK packs Add a Godot Engine PCK artifact reader for standalone packs and packs embedded behind an executable trailer (formats 2 and 3). Members are uncompressed byte ranges verified against their declared MD5 on read. Encrypted directories, sparse bundles, patch removal entries, unknown flags and format 1 fail closed as unavailable; encrypted members are reported without being read. inspect-artifact and extract-artifact recognize standalone packs by their GDPC magic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(artifacts): bound Godot PCK metadata and own member streams Cap member paths at 4096 bytes and the retained directory at 64 MiB before allocation. Read members with positional chunk reads through pipeline so cancellation is typed, abandoned consumers release their source, and the shared descriptor stays reader-owned. Retain the root descriptor for godot-pck inventory, as for ZIP and universal Mach-O. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(artifacts): complete Godot PCK facts, integrity and selected views * fix(artifacts): preserve atomic-root inventory Evidence * fix(artifacts): bound aggregate PCK member expansion --------- Co-authored-by: Samucahub <151155051+Samucahub@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>
146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
import { z } from "incur";
|
|
import { parseEvidence, type Evidence } from "../domain/evidence.js";
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import type { Result } from "../domain/result.js";
|
|
import { AnalysisInputError } from "../domain/analysisErrorCore.js";
|
|
import { createEvidenceBundle } from "../domain/evidenceBundle.js";
|
|
import { writeEvidenceBundle } from "../application/EvidenceBundleFiles.js";
|
|
import { inspectAnalysisView } from "../application/analysisView/AnalysisViewService.js";
|
|
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import { absoluteCallerPath } from "../filesystem/callerPath.js";
|
|
import { finishAnalysisCommand } from "./finishAnalysisCommand.js";
|
|
|
|
export const analysisEvidenceDeliveryOptions = {
|
|
detail: z
|
|
.enum(["complete", "summary"])
|
|
.default("complete")
|
|
.describe("Deliver complete Evidence, or a summary with --evidence_output"),
|
|
evidence_output: z
|
|
.string()
|
|
.min(1)
|
|
.optional()
|
|
.describe(
|
|
"Save complete Evidence as a bundle; required for summary delivery. Never overwrites.",
|
|
),
|
|
};
|
|
|
|
const isEvidence = (value: JsonValue): value is Evidence =>
|
|
typeof value === "object" &&
|
|
value !== null &&
|
|
!Array.isArray(value) &&
|
|
"evidence_id" in value &&
|
|
"normalized_result" in value;
|
|
|
|
interface CliEvidenceDelivery {
|
|
readonly providerId: string;
|
|
readonly operation: string;
|
|
readonly signal: AbortSignal;
|
|
readonly detail: "complete" | "summary";
|
|
readonly evidenceOutput: string | undefined;
|
|
}
|
|
|
|
export const cliEvidenceDeliveryInputError = (
|
|
input: Pick<CliEvidenceDelivery, "detail" | "evidenceOutput" | "operation">,
|
|
): AnalysisInputError | undefined =>
|
|
input.detail === "summary" && input.evidenceOutput === undefined
|
|
? new AnalysisInputError(input.operation, undefined, [
|
|
{
|
|
path: ["evidence_output"],
|
|
reason: "invalid_value",
|
|
message:
|
|
"Summary delivery requires --evidence_output so complete source Evidence remains available.",
|
|
},
|
|
])
|
|
: undefined;
|
|
|
|
/** One-shot delivery owns report publication; analysis keeps its own cleanup owner. */
|
|
export const deliverCliAnalysisEvidence = async (
|
|
input: CliEvidenceDelivery & {
|
|
readonly run: () => Promise<Result<Evidence, AnalysisError>>;
|
|
readonly service: { close(): Promise<void> };
|
|
},
|
|
) => {
|
|
const {
|
|
run,
|
|
service,
|
|
providerId,
|
|
operation,
|
|
signal,
|
|
detail,
|
|
evidenceOutput,
|
|
} = input;
|
|
const inputError = cliEvidenceDeliveryInputError(input);
|
|
if (inputError !== undefined)
|
|
return finishAnalysisCommand(
|
|
async () => ({ ok: false, error: inputError }),
|
|
service,
|
|
providerId,
|
|
operation,
|
|
signal,
|
|
);
|
|
const complete = await finishAnalysisCommand(
|
|
run,
|
|
service,
|
|
providerId,
|
|
operation,
|
|
signal,
|
|
);
|
|
return publishCliAnalysisEvidence(complete, {
|
|
providerId,
|
|
operation,
|
|
signal,
|
|
detail,
|
|
evidenceOutput,
|
|
});
|
|
};
|
|
|
|
/** Publish already finalized CLI analysis without opening a second provider/session. */
|
|
export const publishCliAnalysisEvidence = async (
|
|
value: JsonValue,
|
|
input: CliEvidenceDelivery,
|
|
) => {
|
|
const { providerId, operation, signal, detail, evidenceOutput } = input;
|
|
if (!isEvidence(value)) return value;
|
|
const complete = parseEvidence(value);
|
|
const inputError = cliEvidenceDeliveryInputError(input);
|
|
if (inputError !== undefined) {
|
|
inputError.retainPartialObservation(complete);
|
|
return projectAnalysisError(inputError);
|
|
}
|
|
if (evidenceOutput === undefined) return complete;
|
|
const resources = new ArtifactResourceScope();
|
|
let receipt: { readonly path: string; readonly bytes: number } | undefined;
|
|
const delivered = await finishAnalysisCommand(
|
|
async () => {
|
|
const written = await writeEvidenceBundle(
|
|
createEvidenceBundle([complete]),
|
|
absoluteCallerPath(evidenceOutput),
|
|
false,
|
|
resources,
|
|
signal,
|
|
);
|
|
if (!written.ok) {
|
|
if (written.error.partialObservation === undefined)
|
|
written.error.retainPartialObservation(complete);
|
|
return written;
|
|
}
|
|
receipt = written.value;
|
|
return detail === "summary"
|
|
? inspectAnalysisView({
|
|
source: { kind: "inline", evidence: complete },
|
|
view: { kind: "summary" },
|
|
})
|
|
: { ok: true as const, value: complete };
|
|
},
|
|
{ close: () => resources.close() },
|
|
providerId,
|
|
operation,
|
|
signal,
|
|
);
|
|
return isEvidence(delivered) && detail === "summary"
|
|
? { summary: delivered, evidence_bundle: receipt }
|
|
: delivered;
|
|
};
|