mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 01:45:32 +00:00
* fix: order remaining outputs independently of host locale
String.prototype.localeCompare without a pinned collation follows the
host's ICU default locale, so the same input could be ordered differently
on different machines. 614fb768 moved canonical domain projections to
compareUnicodeCodePoints; provider, application, catalog, and setup
orderings still used locale collation.
Order every remaining source site by Unicode code point, including Mach-O
symbols, firmware extraction paths, ASAR entries (previously pinned to
"en" collation), evidence ID sets, browser and Electron target, script,
and resource inventories, reconstruction obligations, capability and
provider listings, and setup environment details.
The current tool and prompt catalog names order identically under both
comparators, so the catalog identity digest is unchanged.
* fix(artifacts): order ASAR entries by their portable path
@electron/asar lists host-spelled paths, so code-point order over the raw
spelling still differed by host: "a\b" sorts after "a0" on Windows while
"a/b" sorts before it on POSIX. Pair each listing with its portable
archive path once, order by that path, and keep the host spelling for
@electron/asar calls and adapter keys.
228 lines
7.3 KiB
TypeScript
228 lines
7.3 KiB
TypeScript
import type { BinaryTarget } from "../../domain/binaryTargetTypes.js";
|
|
import { binaryTargetSummary } from "./BinaryTargetSummary.js";
|
|
import type { JsonValue } from "../../domain/jsonValue.js";
|
|
import type { ProviderOperationHealth } from "../../domain/providerOperationHealth.js";
|
|
import type {
|
|
CapabilityDescriptor,
|
|
ProviderRequestActivitySnapshot,
|
|
ProviderRuntimeLineageSnapshot,
|
|
} from "../AnalysisProvider.js";
|
|
import type {
|
|
SessionProviderRoute,
|
|
SessionProviderRouter,
|
|
} from "./SessionProviderRouter.js";
|
|
import { compareUnicodeCodePoints } from "../../domain/unicodeCodePointOrder.js";
|
|
|
|
interface BinarySessionStatusInput {
|
|
readonly target: BinaryTarget | undefined;
|
|
readonly route: SessionProviderRoute;
|
|
readonly router: SessionProviderRouter;
|
|
readonly runtimeUnavailability: ReadonlyMap<
|
|
string,
|
|
{ readonly reason: string }
|
|
>;
|
|
readonly runId: string | undefined;
|
|
readonly runtimeLineageSnapshots: readonly ProviderRuntimeLineageSnapshot[];
|
|
readonly requestActivitySnapshots: readonly ProviderRequestActivitySnapshot[];
|
|
readonly providerOperationHealth?: ProviderOperationHealth | null;
|
|
}
|
|
|
|
/** Project internal provider routing state into the caller-visible session status. */
|
|
export const binarySessionStatus = ({
|
|
target,
|
|
route,
|
|
router,
|
|
runtimeUnavailability,
|
|
runId,
|
|
runtimeLineageSnapshots,
|
|
requestActivitySnapshots,
|
|
providerOperationHealth,
|
|
}: BinarySessionStatusInput): JsonValue => {
|
|
const capabilities = [...route.capabilities.values()]
|
|
.filter(({ operation }) => operation !== "inventory_artifact")
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(left.operation, right.operation),
|
|
)
|
|
.map((descriptor) => {
|
|
const unavailable = runtimeUnavailability.get(descriptor.operation);
|
|
return unavailable === undefined
|
|
? descriptor
|
|
: {
|
|
...descriptor,
|
|
available: false as const,
|
|
reason: unavailable.reason,
|
|
};
|
|
})
|
|
.map(capabilityStatus);
|
|
const analysisProviderBinding =
|
|
route.binding === null
|
|
? null
|
|
: {
|
|
provider: providerSummary(route.binding.identity),
|
|
selection_source: route.binding.selectionSource,
|
|
analysis_profile: structuredClone(route.binding.profile),
|
|
};
|
|
const analysisProviderCandidates = router
|
|
.candidateStatuses(route)
|
|
.map((candidate) => ({
|
|
provider: providerSummary(candidate.provider),
|
|
availability: structuredClone(candidate.availability),
|
|
target_support: structuredClone(candidate.targetSupport),
|
|
selected: candidate.selected,
|
|
capabilities: candidate.capabilities
|
|
.slice()
|
|
.filter(({ operation }) => operation !== "inventory_artifact")
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(left.operation, right.operation),
|
|
)
|
|
.map(capabilityStatus),
|
|
}));
|
|
const common = {
|
|
capabilities,
|
|
analysis_provider_binding: analysisProviderBinding,
|
|
analysis_provider_candidates: analysisProviderCandidates,
|
|
analysis_run:
|
|
runId === undefined
|
|
? null
|
|
: {
|
|
run_id: runId,
|
|
process_lineage: processLineageStatus(runtimeLineageSnapshots),
|
|
},
|
|
analysis_activity: requestActivityStatus(requestActivitySnapshots),
|
|
provider_operation_health: providerOperationHealthStatus(
|
|
providerOperationHealth,
|
|
),
|
|
};
|
|
return target === undefined
|
|
? { open: false, ...common }
|
|
: {
|
|
open: true,
|
|
...common,
|
|
...binaryTargetSummary(target),
|
|
};
|
|
};
|
|
|
|
const requestActivityStatus = (
|
|
snapshots: readonly ProviderRequestActivitySnapshot[],
|
|
) => {
|
|
const providers = snapshots
|
|
.slice()
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(left.provider.id, right.provider.id),
|
|
)
|
|
.map(({ provider, active, queuedRequests }) => ({
|
|
provider: providerSummary(provider),
|
|
active:
|
|
active === null
|
|
? null
|
|
: {
|
|
request_id: active.requestId,
|
|
operation: active.operation,
|
|
elapsed_ms: active.elapsedMs,
|
|
caller_state: active.callerState,
|
|
},
|
|
queued_requests: queuedRequests,
|
|
}));
|
|
const active = snapshots.flatMap((snapshot) =>
|
|
snapshot.active === null ? [] : [snapshot.active],
|
|
);
|
|
return {
|
|
status:
|
|
snapshots.length === 0
|
|
? "not_observed"
|
|
: active.length > 0
|
|
? "busy"
|
|
: "idle",
|
|
providers,
|
|
};
|
|
};
|
|
|
|
const providerSummary = ({
|
|
id,
|
|
name,
|
|
version,
|
|
}: {
|
|
readonly id: string;
|
|
readonly name: string;
|
|
readonly version: string | null;
|
|
}) => ({ id, name, version });
|
|
|
|
const processLineageStatus = (
|
|
snapshots: readonly ProviderRuntimeLineageSnapshot[],
|
|
) =>
|
|
snapshots.length === 0
|
|
? { status: "not_observed" }
|
|
: {
|
|
status: "snapshots",
|
|
snapshots: snapshots
|
|
.slice()
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(left.provider.id, right.provider.id),
|
|
)
|
|
.map(({ provider, observation }) => ({
|
|
provider: providerSummary(provider),
|
|
observation:
|
|
observation.status === "unavailable"
|
|
? {
|
|
status: observation.status,
|
|
observed_at: observation.observedAt,
|
|
launcher_pid: observation.launcherPid,
|
|
process_group_id: observation.processGroupId,
|
|
reason: observation.reason,
|
|
}
|
|
: {
|
|
status: observation.status,
|
|
observed_at: observation.observedAt,
|
|
launcher_pid: observation.lineage.launcherPid,
|
|
launcher_parent_pid: observation.lineage.launcherParentPid,
|
|
process_group_id: observation.lineage.processGroupId,
|
|
descendants: observation.lineage.descendants.map(
|
|
({ pid, parentPid, processGroupId }) => ({
|
|
pid,
|
|
parent_pid: parentPid,
|
|
process_group_id: processGroupId,
|
|
}),
|
|
),
|
|
},
|
|
})),
|
|
};
|
|
|
|
const capabilityStatus = (
|
|
descriptor: Omit<CapabilityDescriptor, "available" | "reason"> & {
|
|
readonly available: boolean;
|
|
readonly reason: string | null;
|
|
},
|
|
) => ({
|
|
operation: descriptor.operation,
|
|
available: descriptor.available,
|
|
reason: descriptor.reason,
|
|
availability_code: descriptor.availabilityCode ?? null,
|
|
effects: {
|
|
mutates_artifact: descriptor.effects.mutatesArtifact,
|
|
launches_process: descriptor.effects.launchesProcess,
|
|
may_show_ui: descriptor.effects.mayShowUi,
|
|
may_access_network: descriptor.effects.mayAccessNetwork,
|
|
may_write_filesystem: descriptor.effects.mayWriteFilesystem,
|
|
changes_permissions: descriptor.effects.changesPermissions,
|
|
requires_root: descriptor.effects.requiresRoot,
|
|
},
|
|
limitations: [...descriptor.limitations],
|
|
});
|
|
|
|
const providerOperationHealthStatus = (
|
|
health: ProviderOperationHealth | null | undefined,
|
|
): JsonValue => {
|
|
if (health === undefined || health === null) return null;
|
|
return {
|
|
state: health.state,
|
|
stage: health.stage,
|
|
retry_action: health.retryAction,
|
|
exit_code: health.exitCode,
|
|
requests: health.requests.map((request) => ({
|
|
request_id: request.requestId,
|
|
operation: request.operation,
|
|
stage: request.stage,
|
|
})),
|
|
};
|
|
};
|