rea-agents 1.7.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -59
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +42 -2
- package/dist/application/ClientRegistrationStatus.js +1 -1
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +11 -29
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +165 -197
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +112 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +223 -0
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +58 -50
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InvestigationProviders.js +30 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +52 -7
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +82 -17
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +4 -1
- package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +292 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessSampling.js +15 -11
- package/dist/application/ProjectPermissionStore.js +49 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +147 -259
- package/dist/application/SetupClientConfiguration.js +220 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +123 -12
- package/dist/application/SetupSkill.js +26 -12
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +6 -3
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/application/runtime.js +2 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +46 -464
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +5 -1
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -129
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +8 -1
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +108 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +13 -383
- package/dist/cliApplicationCommands.js +176 -0
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +17 -0
- package/dist/cliElectronCommands.js +100 -27
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +289 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -292
- package/dist/contracts/applicationToolContracts.js +225 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +49 -47
- package/dist/contracts/toolEffects.js +174 -0
- package/dist/contracts/toolOutputSchemaGroups.js +232 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
- package/dist/contracts/toolOutputSchemas.js +2 -535
- package/dist/doctorRuntime.js +42 -1
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/artifactComparison.js +37 -19
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/browserObservation.js +1 -282
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +206 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +32 -448
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
- package/dist/domain/javascriptApplicationGraph.js +2 -1
- package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +233 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +100 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +193 -0
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +26 -254
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +81 -2
- package/dist/domain/javascriptStaticAnalysisState.js +6 -0
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +206 -0
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +452 -0
- package/dist/domain/managedMemberComparison.js +211 -0
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +46 -0
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +196 -0
- package/dist/domain/managedRuntimeCorrelation.js +184 -0
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +9 -3
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +339 -0
- package/dist/dotnet/ManagedMemberInspector.js +153 -0
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +104 -0
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +259 -0
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +143 -0
- package/dist/dotnet/ManagedPeReader.js +188 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +67 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +3 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -132
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +300 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +217 -0
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +158 -41
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +13 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +61 -60
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +39 -12
- package/dist/server/registerEnhancedTools.js +52 -15
- package/dist/server/registerEvidenceResources.js +54 -0
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +103 -132
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +13 -0
- package/dist/server/registerOfficialTools.js +81 -67
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +9 -8
- package/dist/server/registerSessionTools.js +77 -216
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +177 -2
- package/dist/server/toolResult.js +30 -17
- package/package.json +21 -14
- package/scripts/rea.mjs +1 -1
- package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +75 -6
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
|
@@ -1,94 +1,27 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { parseBinaryTarget } from "../domain/binaryTarget.js";
|
|
8
|
-
import { supportsNodeVersion } from "../domain/runtimeVersion.js";
|
|
9
8
|
import { probeHomebrew } from "./homebrew.js";
|
|
10
|
-
import { linuxHopperBinarySupported,
|
|
9
|
+
import { linuxHopperBinarySupported, linuxSharedLibrariesAvailable, readLinuxDistribution, } from "./LinuxHopper.js";
|
|
11
10
|
import { CATALOG_IDENTITY } from "../catalogIdentity.js";
|
|
12
11
|
import { PRODUCT_IDENTITY, SDK_IDENTITY } from "../identity.js";
|
|
13
12
|
import { readClientRegistrationStatuses, } from "./ClientRegistrationStatus.js";
|
|
13
|
+
import { inspectRuntimeExecutables, } from "./RuntimeExecutableDiagnostics.js";
|
|
14
|
+
import { collectDoctorDiagnostics, doctorHealthy, } from "./DoctorDiagnostics.js";
|
|
14
15
|
const execFileAsync = promisify(execFile);
|
|
15
|
-
const DEFAULT_HOPPER = "/Applications/Hopper Disassembler.app/Contents/MacOS/hopper";
|
|
16
|
-
const SYSTEM_LINUX_HOPPER = "/opt/hopper/bin/Hopper";
|
|
17
16
|
/**
|
|
18
17
|
* Check requirements and an optional target without mutating the host.
|
|
19
18
|
* Every failed check includes remediation suitable for either the one-shot CLI
|
|
20
19
|
* or an agent consuming the structured result.
|
|
21
20
|
*/
|
|
22
21
|
export const runDoctor = async (target, host = systemDoctorHost()) => {
|
|
23
|
-
const checks =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
classification: "missing_dependency",
|
|
27
|
-
}));
|
|
28
|
-
const macosVersion = host.platform === "darwin" ? await host.macosVersion() : undefined;
|
|
29
|
-
const macosMajor = macosVersion === undefined ? 0 : parseMajor(macosVersion);
|
|
30
|
-
const linuxDistribution = host.platform === "linux" ? await host.linuxDistribution() : undefined;
|
|
31
|
-
const supportedHost = (host.platform === "darwin" && macosMajor >= 12) ||
|
|
32
|
-
(host.platform === "linux" && linuxDistribution?.supported === true);
|
|
33
|
-
checks.push(check("host", supportedHost, macosVersion ??
|
|
34
|
-
(linuxDistribution === undefined
|
|
35
|
-
? host.platform
|
|
36
|
-
: `${linuxDistribution.id} ${linuxDistribution.versionId ?? "unknown"}`), {
|
|
37
|
-
remediation: "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.",
|
|
38
|
-
classification: "unsupported_host",
|
|
39
|
-
}));
|
|
40
|
-
const candidates = host.configuredHopperPath === undefined
|
|
41
|
-
? [
|
|
42
|
-
DEFAULT_HOPPER,
|
|
43
|
-
SYSTEM_LINUX_HOPPER,
|
|
44
|
-
linuxHopperLauncherPath(homedir()),
|
|
45
|
-
...(await host.manualHopperPaths()),
|
|
46
|
-
await host.brewHopperPath(),
|
|
47
|
-
].filter((value) => value !== undefined)
|
|
48
|
-
: [host.configuredHopperPath];
|
|
49
|
-
let hopperPath;
|
|
50
|
-
for (const candidate of new Set(candidates))
|
|
51
|
-
if (await host.executable(candidate)) {
|
|
52
|
-
hopperPath = candidate;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
checks.push(check("hopper", hopperPath !== undefined, hopperPath ?? host.configuredHopperPath, {
|
|
56
|
-
remediation: host.configuredHopperPath === undefined
|
|
57
|
-
? "Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH."
|
|
58
|
-
: "Unset or update HOPPER_LAUNCHER_PATH to an executable Hopper launcher.",
|
|
59
|
-
classification: host.configuredHopperPath === undefined
|
|
60
|
-
? "missing_analysis_engine"
|
|
61
|
-
: "config_drift",
|
|
62
|
-
}));
|
|
63
|
-
if (host.platform === "linux" && hopperPath !== undefined)
|
|
64
|
-
checks.push(check("hopper-version", await host.supportedLinuxHopper(hopperPath), hopperPath, {
|
|
65
|
-
remediation: unsupportedHopperRemediation(host.configuredHopperPath === hopperPath),
|
|
66
|
-
classification: "config_drift",
|
|
67
|
-
}));
|
|
68
|
-
const providerInspections = await inspectDoctorProviders(host, checks);
|
|
69
|
-
if (host.platform === "linux" && hopperPath !== undefined)
|
|
70
|
-
checks.push(check("hopper-demo-runtime", await host.linuxDemoRuntimeReady(), undefined, {
|
|
71
|
-
remediation: "Rerun rea setup to install the Xvfb, xauth, Python, X11, and XTEST packages required for Linux demo sessions.",
|
|
72
|
-
classification: "missing_dependency",
|
|
73
|
-
}));
|
|
74
|
-
if (target !== undefined)
|
|
75
|
-
checks.push(check("target", await host.validTarget(target), target, {
|
|
76
|
-
remediation: "Supply a readable local app or program path.",
|
|
77
|
-
classification: "config_drift",
|
|
78
|
-
}));
|
|
79
|
-
const installationPaths = (await host.installationPaths?.()) ?? [];
|
|
80
|
-
const installedSkillVersion = await host.installedSkillVersion?.();
|
|
81
|
-
const registrations = (await host.clientRegistrations?.()) ?? [];
|
|
82
|
-
for (const registration of registrations)
|
|
83
|
-
if (registration.state !== "aligned")
|
|
84
|
-
checks.push({
|
|
85
|
-
name: `registration:${registration.client}`,
|
|
86
|
-
ok: false,
|
|
87
|
-
classification: "config_drift",
|
|
88
|
-
detail: registration.config_path,
|
|
89
|
-
remediation: registration.remediation ??
|
|
90
|
-
"Run rea setup, then restart the affected client.",
|
|
91
|
-
});
|
|
22
|
+
const { checks: diagnosticChecks, hopperPath, providerInspections, runtimeExecutables, } = await collectDoctorDiagnostics(target, host);
|
|
23
|
+
const identity = await collectDoctorIdentity(host, runtimeExecutables);
|
|
24
|
+
const checks = [...diagnosticChecks, ...identity.checks];
|
|
92
25
|
return {
|
|
93
26
|
healthy: doctorHealthy(checks, {
|
|
94
27
|
hopperAvailable: hopperPath !== undefined &&
|
|
@@ -101,7 +34,22 @@ export const runDoctor = async (target, host = systemDoctorHost()) => {
|
|
|
101
34
|
...(hopperPath === undefined ? {} : { hopperPath }),
|
|
102
35
|
...(providerInspections === undefined ? {} : { providerInspections }),
|
|
103
36
|
checks,
|
|
104
|
-
identity:
|
|
37
|
+
identity: identity.value,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const collectDoctorIdentity = async (host, runtimeExecutables) => {
|
|
41
|
+
const installationPaths = (await host.installationPaths?.()) ?? [];
|
|
42
|
+
const installedSkillIdentity = await readInstalledSkillIdentity(host);
|
|
43
|
+
const skillAligned = skillIdentityAligned(installedSkillIdentity);
|
|
44
|
+
const registrations = (await host.clientRegistrations?.()) ?? [];
|
|
45
|
+
return {
|
|
46
|
+
checks: [
|
|
47
|
+
...(skillAligned ? [] : [skillIdentityCheck(installedSkillIdentity)]),
|
|
48
|
+
...registrations
|
|
49
|
+
.filter(({ state }) => state !== "aligned")
|
|
50
|
+
.map(registrationCheck),
|
|
51
|
+
],
|
|
52
|
+
value: {
|
|
105
53
|
cli_package_version: PRODUCT_IDENTITY.packageVersion,
|
|
106
54
|
expected_skill_version: PRODUCT_IDENTITY.skillVersion,
|
|
107
55
|
sdk: SDK_IDENTITY,
|
|
@@ -112,79 +60,74 @@ export const runDoctor = async (target, host = systemDoctorHost()) => {
|
|
|
112
60
|
},
|
|
113
61
|
installations: {
|
|
114
62
|
paths: installationPaths,
|
|
115
|
-
state: installationPaths
|
|
116
|
-
? "unknown"
|
|
117
|
-
: installationPaths.length === 1
|
|
118
|
-
? "single"
|
|
119
|
-
: "multiple",
|
|
63
|
+
state: installationState(installationPaths),
|
|
120
64
|
},
|
|
121
65
|
skill: {
|
|
122
|
-
installed_version:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
66
|
+
installed_version: installedSkillIdentity?.version ?? null,
|
|
67
|
+
installed_tool_count: installedSkillIdentity?.toolCount ?? null,
|
|
68
|
+
installed_catalog_digest: installedSkillIdentity?.catalogDigest ?? null,
|
|
69
|
+
state: installedSkillIdentity === undefined
|
|
70
|
+
? "missing"
|
|
71
|
+
: skillAligned
|
|
126
72
|
? "aligned"
|
|
127
73
|
: "stale",
|
|
128
|
-
remediation:
|
|
129
|
-
installedSkillVersion === PRODUCT_IDENTITY.skillVersion
|
|
74
|
+
remediation: skillAligned
|
|
130
75
|
? null
|
|
131
76
|
: "Run rea setup to update the installed REA skill.",
|
|
132
77
|
},
|
|
133
78
|
registrations,
|
|
79
|
+
runtime_executables: runtimeExecutables ?? null,
|
|
134
80
|
},
|
|
135
81
|
};
|
|
136
82
|
};
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
83
|
+
const readInstalledSkillIdentity = async (host) => {
|
|
84
|
+
const observed = await host.installedSkillIdentity?.();
|
|
85
|
+
if (observed !== undefined)
|
|
86
|
+
return observed;
|
|
87
|
+
const legacyVersion = await host.installedSkillVersion?.();
|
|
88
|
+
return legacyVersion === undefined
|
|
89
|
+
? undefined
|
|
90
|
+
: { version: legacyVersion, toolCount: null, catalogDigest: null };
|
|
91
|
+
};
|
|
92
|
+
const skillIdentityAligned = (identity) => identity?.version === PRODUCT_IDENTITY.skillVersion &&
|
|
93
|
+
identity.toolCount === CATALOG_IDENTITY.counts.mcp_tools &&
|
|
94
|
+
identity.catalogDigest === CATALOG_IDENTITY.digests.combined_sha256;
|
|
95
|
+
const skillIdentityCheck = (identity) => ({
|
|
96
|
+
name: "skill:identity",
|
|
97
|
+
ok: false,
|
|
98
|
+
classification: "config_drift",
|
|
99
|
+
detail: identity === undefined
|
|
100
|
+
? "Installed REA skill identity is missing."
|
|
101
|
+
: "Installed REA skill identity is stale.",
|
|
102
|
+
remediation: "Run rea setup to update the installed REA skill.",
|
|
103
|
+
});
|
|
104
|
+
const registrationCheck = (registration) => ({
|
|
105
|
+
name: `registration:${registration.client}`,
|
|
106
|
+
ok: false,
|
|
107
|
+
classification: "config_drift",
|
|
108
|
+
detail: registration.config_path,
|
|
109
|
+
remediation: registration.remediation ??
|
|
110
|
+
"Run rea setup, then restart the affected client.",
|
|
111
|
+
});
|
|
112
|
+
const installationState = (paths) => paths.length === 0 ? "unknown" : paths.length === 1 ? "single" : "multiple";
|
|
140
113
|
/** Create diagnostics backed by the current process and host commands. */
|
|
141
114
|
export const systemDoctorHost = (options = {}) => ({
|
|
142
115
|
platform: process.platform,
|
|
116
|
+
architecture: process.arch,
|
|
143
117
|
nodeVersion: process.versions.node,
|
|
144
118
|
...(process.env.HOPPER_LAUNCHER_PATH === undefined
|
|
145
119
|
? {}
|
|
146
120
|
: { configuredHopperPath: process.env.HOPPER_LAUNCHER_PATH }),
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
catch {
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
},
|
|
121
|
+
...(process.env.REA_ILSPY_CMD_PATH === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: { configuredIlspyCmdPath: process.env.REA_ILSPY_CMD_PATH }),
|
|
124
|
+
macosVersion: readMacosVersion,
|
|
155
125
|
linuxDistribution: readLinuxDistribution,
|
|
156
126
|
async validTarget(path) {
|
|
157
127
|
return (await parseBinaryTarget(path)).ok;
|
|
158
128
|
},
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
await access(path, constants.X_OK);
|
|
162
|
-
if (process.platform === "linux") {
|
|
163
|
-
const linked = await execFileAsync("ldd", [path]);
|
|
164
|
-
if (!linuxSharedLibrariesAvailable(`${linked.stdout}\n${linked.stderr}`))
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
async linuxDemoRuntimeReady() {
|
|
174
|
-
try {
|
|
175
|
-
await access("/usr/bin/Xvfb", constants.X_OK);
|
|
176
|
-
await access("/usr/bin/xauth", constants.X_OK);
|
|
177
|
-
await access("/usr/bin/python3", constants.X_OK);
|
|
178
|
-
await execFileAsync("/usr/bin/python3", [
|
|
179
|
-
"-c",
|
|
180
|
-
"import ctypes; ctypes.CDLL('libX11.so.6'); ctypes.CDLL('libXtst.so.6')",
|
|
181
|
-
]);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
},
|
|
129
|
+
executable: executableAvailable,
|
|
130
|
+
linuxDemoRuntimeReady,
|
|
188
131
|
supportedLinuxHopper: linuxHopperBinarySupported,
|
|
189
132
|
async brewHopperPath() {
|
|
190
133
|
return probeHomebrew(async (command) => {
|
|
@@ -201,25 +144,30 @@ export const systemDoctorHost = (options = {}) => ({
|
|
|
201
144
|
}
|
|
202
145
|
});
|
|
203
146
|
},
|
|
204
|
-
|
|
205
|
-
const roots = ["/Applications", join(homedir(), "Applications")];
|
|
206
|
-
const paths = [];
|
|
207
|
-
for (const root of roots) {
|
|
208
|
-
try {
|
|
209
|
-
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
210
|
-
if (entry.isDirectory() && /^Hopper.*\.app$/i.test(entry.name))
|
|
211
|
-
paths.push(join(root, entry.name, "Contents/MacOS/hopper"));
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
catch {
|
|
215
|
-
// A missing or unreadable optional application directory is not fatal.
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return paths;
|
|
219
|
-
},
|
|
147
|
+
manualHopperPaths,
|
|
220
148
|
...(options.providerInspections === undefined
|
|
221
149
|
? {}
|
|
222
150
|
: { providerInspections: options.providerInspections }),
|
|
151
|
+
...(options.javascriptReplayCheck === undefined
|
|
152
|
+
? {}
|
|
153
|
+
: { javascriptReplayCheck: options.javascriptReplayCheck }),
|
|
154
|
+
async ilspyCmdVersion(path) {
|
|
155
|
+
try {
|
|
156
|
+
await access(path, constants.X_OK);
|
|
157
|
+
return firstIlspyVersionLine((await execFileAsync(path, ["--version"], { timeout: 10_000 })).stdout);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
runtimeExecutables: () => inspectRuntimeExecutables({
|
|
164
|
+
platform: process.platform,
|
|
165
|
+
path: process.env.PATH ?? "",
|
|
166
|
+
launcherNode: process.execPath,
|
|
167
|
+
...(process.env.PATHEXT === undefined
|
|
168
|
+
? {}
|
|
169
|
+
: { pathExtensions: process.env.PATHEXT.split(delimiter) }),
|
|
170
|
+
}),
|
|
223
171
|
async installationPaths() {
|
|
224
172
|
try {
|
|
225
173
|
const command = process.platform === "win32" ? "where" : "which";
|
|
@@ -230,68 +178,88 @@ export const systemDoctorHost = (options = {}) => ({
|
|
|
230
178
|
return [];
|
|
231
179
|
}
|
|
232
180
|
},
|
|
233
|
-
|
|
181
|
+
installedSkillVersion,
|
|
182
|
+
installedSkillIdentity,
|
|
183
|
+
clientRegistrations: () => readClientRegistrationStatuses(homedir()),
|
|
184
|
+
});
|
|
185
|
+
const readMacosVersion = async () => {
|
|
186
|
+
try {
|
|
187
|
+
return (await execFileAsync("sw_vers", ["-productVersion"])).stdout.trim();
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const executableAvailable = async (path) => {
|
|
194
|
+
try {
|
|
195
|
+
await access(path, constants.X_OK);
|
|
196
|
+
if (process.platform !== "linux")
|
|
197
|
+
return true;
|
|
198
|
+
const linked = await execFileAsync("ldd", [path]);
|
|
199
|
+
return linuxSharedLibrariesAvailable(`${linked.stdout}\n${linked.stderr}`);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const linuxDemoRuntimeReady = async () => {
|
|
206
|
+
try {
|
|
207
|
+
await access("/usr/bin/Xvfb", constants.X_OK);
|
|
208
|
+
await access("/usr/bin/xauth", constants.X_OK);
|
|
209
|
+
await access("/usr/bin/python3", constants.X_OK);
|
|
210
|
+
await execFileAsync("/usr/bin/python3", [
|
|
211
|
+
"-c",
|
|
212
|
+
"import ctypes; ctypes.CDLL('libX11.so.6'); ctypes.CDLL('libXtst.so.6')",
|
|
213
|
+
]);
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const manualHopperPaths = async () => {
|
|
221
|
+
const paths = [];
|
|
222
|
+
for (const root of ["/Applications", join(homedir(), "Applications")]) {
|
|
234
223
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
224
|
+
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
225
|
+
if (entry.isDirectory() && /^Hopper.*\.app$/i.test(entry.name))
|
|
226
|
+
paths.push(join(root, entry.name, "Contents/MacOS/hopper"));
|
|
227
|
+
}
|
|
237
228
|
}
|
|
238
229
|
catch {
|
|
239
|
-
|
|
230
|
+
// A missing or unreadable optional application directory is not fatal.
|
|
240
231
|
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
const
|
|
232
|
+
}
|
|
233
|
+
return paths;
|
|
234
|
+
};
|
|
235
|
+
const readInstalledSkill = () => readFile(join(homedir(), ".agents/skills", PRODUCT_IDENTITY.skillName, "SKILL.md"), "utf8");
|
|
236
|
+
const installedSkillVersion = async () => {
|
|
237
|
+
try {
|
|
238
|
+
return /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(await readInstalledSkill())?.[1];
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const installedSkillIdentity = async () => {
|
|
245
|
+
try {
|
|
246
|
+
const content = await readInstalledSkill();
|
|
247
|
+
const countText = /^\s{2}tool_count:\s*(\d+)\s*$/mu.exec(content)?.[1];
|
|
248
|
+
return {
|
|
249
|
+
version: /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(content)?.[1] ?? null,
|
|
250
|
+
toolCount: countText === undefined ? null : Number.parseInt(countText, 10),
|
|
251
|
+
catalogDigest: /^\s{2}catalog_digest:\s*"([a-f0-9]{64})"\s*$/mu.exec(content)?.[1] ??
|
|
252
|
+
null,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
245
259
|
const uniqueLines = (value) => [
|
|
246
260
|
...new Set(value
|
|
247
261
|
.split(/\r?\n/u)
|
|
248
262
|
.map((line) => line.trim())
|
|
249
263
|
.filter((line) => line.length > 0)),
|
|
250
264
|
];
|
|
251
|
-
const
|
|
252
|
-
name,
|
|
253
|
-
ok,
|
|
254
|
-
classification: ok ? "healthy" : failure.classification,
|
|
255
|
-
...(detail === undefined ? {} : { detail }),
|
|
256
|
-
...(ok ? {} : { remediation: failure.remediation }),
|
|
257
|
-
});
|
|
258
|
-
const providerDoctorChecks = (inspection) => inspection.checks.map((candidate) => ({
|
|
259
|
-
name: candidate.name === "configuration"
|
|
260
|
-
? inspection.id
|
|
261
|
-
: `${inspection.id}-${candidate.name}`,
|
|
262
|
-
ok: candidate.ok,
|
|
263
|
-
classification: candidate.ok ? "healthy" : candidate.classification,
|
|
264
|
-
detail: candidate.detail,
|
|
265
|
-
...(candidate.remediation === null
|
|
266
|
-
? {}
|
|
267
|
-
: { remediation: candidate.remediation }),
|
|
268
|
-
}));
|
|
269
|
-
const inspectDoctorProviders = async (host, checks) => {
|
|
270
|
-
const inspections = await host.providerInspections?.();
|
|
271
|
-
if (inspections === undefined)
|
|
272
|
-
return undefined;
|
|
273
|
-
for (const inspection of inspections)
|
|
274
|
-
checks.push(...providerDoctorChecks(inspection));
|
|
275
|
-
return inspections;
|
|
276
|
-
};
|
|
277
|
-
const doctorHealthy = (checks, providers) => {
|
|
278
|
-
if (providers.providerInspections === undefined)
|
|
279
|
-
return checks.every(({ ok }) => ok);
|
|
280
|
-
const providerChecks = checks.filter(({ name }) => name.startsWith("hopper") ||
|
|
281
|
-
providers.providerInspections?.some((inspection) => providerCheckName(inspection.id, name)) === true);
|
|
282
|
-
const coreHealthy = checks
|
|
283
|
-
.filter((candidate) => !providerChecks.includes(candidate))
|
|
284
|
-
.every(({ ok }) => ok);
|
|
285
|
-
const configuredProvidersHealthy = providerChecks
|
|
286
|
-
.filter(({ name }) => {
|
|
287
|
-
if (name.startsWith("hopper"))
|
|
288
|
-
return providers.hopperConfigured;
|
|
289
|
-
return providers.providerInspections?.some((inspection) => inspection.configured && providerCheckName(inspection.id, name));
|
|
290
|
-
})
|
|
291
|
-
.every(({ ok }) => ok);
|
|
292
|
-
return (coreHealthy &&
|
|
293
|
-
configuredProvidersHealthy &&
|
|
294
|
-
(providers.hopperAvailable ||
|
|
295
|
-
providers.providerInspections.some(({ available }) => available)));
|
|
296
|
-
};
|
|
297
|
-
const providerCheckName = (providerId, name) => name === providerId || name.startsWith(`${providerId}-`);
|
|
265
|
+
const firstIlspyVersionLine = (value) => uniqueLines(value).find((line) => line.startsWith("ilspycmd:"));
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { supportsNodeVersion } from "../domain/runtimeVersion.js";
|
|
3
|
+
import { linuxHopperLauncherPath } from "./LinuxHopper.js";
|
|
4
|
+
const DEFAULT_HOPPER = "/Applications/Hopper Disassembler.app/Contents/MacOS/hopper";
|
|
5
|
+
const SYSTEM_LINUX_HOPPER = "/opt/hopper/bin/Hopper";
|
|
6
|
+
/** Collect host, provider, and optional-target diagnostics without mutation. */
|
|
7
|
+
export const collectDoctorDiagnostics = async (target, host) => {
|
|
8
|
+
const runtimeExecutables = await host.runtimeExecutables?.();
|
|
9
|
+
const checks = [
|
|
10
|
+
nodeCheck(host),
|
|
11
|
+
...runtimeChecks(runtimeExecutables),
|
|
12
|
+
];
|
|
13
|
+
checks.push(await hostCheck(host));
|
|
14
|
+
const hopperPath = await findHopper(host);
|
|
15
|
+
checks.push(hopperCheck(host, hopperPath));
|
|
16
|
+
const hopperVersionCheck = await optionalHopperVersionCheck(host, hopperPath);
|
|
17
|
+
if (hopperVersionCheck !== undefined)
|
|
18
|
+
checks.push(hopperVersionCheck);
|
|
19
|
+
const providerInspections = await host.providerInspections?.();
|
|
20
|
+
if (providerInspections !== undefined)
|
|
21
|
+
checks.push(...providerInspections.flatMap(providerDoctorChecks));
|
|
22
|
+
const javascriptReplayCheck = await host.javascriptReplayCheck?.();
|
|
23
|
+
if (javascriptReplayCheck !== undefined)
|
|
24
|
+
checks.push(javascriptReplayCheck);
|
|
25
|
+
const ilspyCheck = await optionalIlspyCheck(host);
|
|
26
|
+
if (ilspyCheck !== undefined)
|
|
27
|
+
checks.push(ilspyCheck);
|
|
28
|
+
const hopperDemoCheck = await optionalHopperDemoCheck(host, hopperPath);
|
|
29
|
+
if (hopperDemoCheck !== undefined)
|
|
30
|
+
checks.push(hopperDemoCheck);
|
|
31
|
+
const targetCheck = await optionalTargetCheck(target, host);
|
|
32
|
+
if (targetCheck !== undefined)
|
|
33
|
+
checks.push(targetCheck);
|
|
34
|
+
return {
|
|
35
|
+
checks,
|
|
36
|
+
...(hopperPath === undefined ? {} : { hopperPath }),
|
|
37
|
+
...(providerInspections === undefined ? {} : { providerInspections }),
|
|
38
|
+
...(runtimeExecutables === undefined ? {} : { runtimeExecutables }),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/** Evaluate core health while treating unconfigured providers as optional. */
|
|
42
|
+
export const doctorHealthy = (checks, providers) => {
|
|
43
|
+
if (providers.providerInspections === undefined)
|
|
44
|
+
return checks.every(({ ok }) => ok);
|
|
45
|
+
const providerChecks = checks.filter(({ name }) => name.startsWith("hopper") ||
|
|
46
|
+
providers.providerInspections?.some((inspection) => providerCheckName(inspection.id, name)) === true);
|
|
47
|
+
const coreHealthy = checks
|
|
48
|
+
.filter((candidate) => !providerChecks.includes(candidate))
|
|
49
|
+
.every(({ ok }) => ok);
|
|
50
|
+
const configuredProvidersHealthy = providerChecks
|
|
51
|
+
.filter(({ name }) => {
|
|
52
|
+
if (name.startsWith("hopper"))
|
|
53
|
+
return providers.hopperConfigured;
|
|
54
|
+
return providers.providerInspections?.some((inspection) => inspection.configured && providerCheckName(inspection.id, name));
|
|
55
|
+
})
|
|
56
|
+
.every(({ ok }) => ok);
|
|
57
|
+
return (coreHealthy &&
|
|
58
|
+
configuredProvidersHealthy &&
|
|
59
|
+
(providers.hopperAvailable ||
|
|
60
|
+
providers.providerInspections.some(({ available }) => available)));
|
|
61
|
+
};
|
|
62
|
+
const nodeCheck = (host) => check("node", supportsNodeVersion(host.nodeVersion), host.nodeVersion, {
|
|
63
|
+
remediation: "Install Node.js 22.19+ or 24.11+.",
|
|
64
|
+
classification: "missing_dependency",
|
|
65
|
+
});
|
|
66
|
+
const runtimeChecks = (inventory) => {
|
|
67
|
+
if (inventory === undefined)
|
|
68
|
+
return [];
|
|
69
|
+
const broken = inventory.candidates.filter(({ healthy }) => !healthy);
|
|
70
|
+
const detail = broken.length === 0
|
|
71
|
+
? `${String(inventory.candidates.length)} runtime executable candidates passed bounded version probes.`
|
|
72
|
+
: `${String(broken.length)} of ${String(inventory.candidates.length)} runtime executable candidates failed bounded version probes: ${broken
|
|
73
|
+
.slice(0, 5)
|
|
74
|
+
.map(({ lexical_path: path }) => path)
|
|
75
|
+
.join(", ")}${broken.length > 5 ? ", …" : ""}`;
|
|
76
|
+
return [
|
|
77
|
+
check("node-toolchains", broken.length === 0, detail, {
|
|
78
|
+
remediation: "Select a verified healthy Node toolchain or repair the failing installation, then rerun rea doctor. Do not create compatibility-library symlinks.",
|
|
79
|
+
classification: "missing_dependency",
|
|
80
|
+
}),
|
|
81
|
+
];
|
|
82
|
+
};
|
|
83
|
+
const hostCheck = async (host) => {
|
|
84
|
+
const macosVersion = host.platform === "darwin" ? await host.macosVersion() : undefined;
|
|
85
|
+
const linuxDistribution = host.platform === "linux" ? await host.linuxDistribution() : undefined;
|
|
86
|
+
const supported = (host.platform === "darwin" && parseMajor(macosVersion) >= 12) ||
|
|
87
|
+
(host.platform === "linux" && linuxDistribution?.supported === true) ||
|
|
88
|
+
(host.platform === "win32" && host.architecture === "x64");
|
|
89
|
+
const detail = macosVersion ??
|
|
90
|
+
(linuxDistribution === undefined
|
|
91
|
+
? `${host.platform} ${host.architecture}`
|
|
92
|
+
: `${linuxDistribution.id} ${linuxDistribution.versionId ?? "unknown"}`);
|
|
93
|
+
return check("host", supported, detail, {
|
|
94
|
+
remediation: "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and the experimental Windows x64 Ghidra P0 boundary.",
|
|
95
|
+
classification: "unsupported_host",
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const findHopper = async (host) => {
|
|
99
|
+
const candidates = host.configuredHopperPath === undefined
|
|
100
|
+
? [
|
|
101
|
+
DEFAULT_HOPPER,
|
|
102
|
+
SYSTEM_LINUX_HOPPER,
|
|
103
|
+
linuxHopperLauncherPath(homedir()),
|
|
104
|
+
...(await host.manualHopperPaths()),
|
|
105
|
+
]
|
|
106
|
+
: [host.configuredHopperPath];
|
|
107
|
+
const hopperPath = await firstExecutable(host, candidates);
|
|
108
|
+
if (hopperPath !== undefined || host.configuredHopperPath !== undefined)
|
|
109
|
+
return hopperPath;
|
|
110
|
+
const brewCandidate = await host.brewHopperPath();
|
|
111
|
+
return firstExecutable(host, brewCandidate === undefined ? [] : [brewCandidate]);
|
|
112
|
+
};
|
|
113
|
+
const firstExecutable = async (host, candidates) => {
|
|
114
|
+
for (const candidate of new Set(candidates))
|
|
115
|
+
if (await host.executable(candidate))
|
|
116
|
+
return candidate;
|
|
117
|
+
return undefined;
|
|
118
|
+
};
|
|
119
|
+
const hopperCheck = (host, hopperPath) => check("hopper", hopperPath !== undefined, hopperPath ?? host.configuredHopperPath, {
|
|
120
|
+
remediation: host.configuredHopperPath === undefined
|
|
121
|
+
? "Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH."
|
|
122
|
+
: "Unset or update HOPPER_LAUNCHER_PATH to an executable Hopper launcher.",
|
|
123
|
+
classification: host.configuredHopperPath === undefined
|
|
124
|
+
? "missing_analysis_engine"
|
|
125
|
+
: "config_drift",
|
|
126
|
+
});
|
|
127
|
+
const optionalHopperVersionCheck = async (host, hopperPath) => {
|
|
128
|
+
if (host.platform !== "linux" || hopperPath === undefined)
|
|
129
|
+
return undefined;
|
|
130
|
+
return check("hopper-version", await host.supportedLinuxHopper(hopperPath), hopperPath, {
|
|
131
|
+
remediation: unsupportedHopperRemediation(host.configuredHopperPath === hopperPath),
|
|
132
|
+
classification: "config_drift",
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const optionalHopperDemoCheck = async (host, hopperPath) => host.platform !== "linux" || hopperPath === undefined
|
|
136
|
+
? undefined
|
|
137
|
+
: check("hopper-demo-runtime", await host.linuxDemoRuntimeReady(), undefined, {
|
|
138
|
+
remediation: "Rerun rea setup to install the Xvfb, xauth, Python, X11, and XTEST packages required for Linux demo sessions.",
|
|
139
|
+
classification: "missing_dependency",
|
|
140
|
+
});
|
|
141
|
+
const optionalIlspyCheck = async (host) => {
|
|
142
|
+
if (host.configuredIlspyCmdPath === undefined)
|
|
143
|
+
return undefined;
|
|
144
|
+
const version = await host.ilspyCmdVersion?.(host.configuredIlspyCmdPath);
|
|
145
|
+
return check("ilspycmd", version !== undefined, version === undefined
|
|
146
|
+
? host.configuredIlspyCmdPath
|
|
147
|
+
: `${host.configuredIlspyCmdPath} (${version})`, {
|
|
148
|
+
remediation: "Unset REA_ILSPY_CMD_PATH or point it at a runnable ilspycmd executable.",
|
|
149
|
+
classification: "config_drift",
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
const optionalTargetCheck = async (target, host) => target === undefined
|
|
153
|
+
? undefined
|
|
154
|
+
: check("target", await host.validTarget(target), target, {
|
|
155
|
+
remediation: "Supply a readable local app or program path.",
|
|
156
|
+
classification: "config_drift",
|
|
157
|
+
});
|
|
158
|
+
const check = (name, ok, detail, failure) => ({
|
|
159
|
+
name,
|
|
160
|
+
ok,
|
|
161
|
+
classification: ok ? "healthy" : failure.classification,
|
|
162
|
+
...(detail === undefined ? {} : { detail }),
|
|
163
|
+
...(ok ? {} : { remediation: failure.remediation }),
|
|
164
|
+
});
|
|
165
|
+
const providerDoctorChecks = (inspection) => inspection.checks.map((candidate) => ({
|
|
166
|
+
name: candidate.name === "configuration"
|
|
167
|
+
? inspection.id
|
|
168
|
+
: `${inspection.id}-${candidate.name}`,
|
|
169
|
+
ok: candidate.ok,
|
|
170
|
+
classification: candidate.ok ? "healthy" : candidate.classification,
|
|
171
|
+
detail: candidate.detail,
|
|
172
|
+
...(candidate.remediation === null
|
|
173
|
+
? {}
|
|
174
|
+
: { remediation: candidate.remediation }),
|
|
175
|
+
}));
|
|
176
|
+
const unsupportedHopperRemediation = (configured) => configured
|
|
177
|
+
? "Unset or update HOPPER_LAUNCHER_PATH, install the Hopper build supported by this REA release, or update REA."
|
|
178
|
+
: "Install the Hopper build supported by this REA release, or update REA for a newer Hopper build.";
|
|
179
|
+
const parseMajor = (version) => Number.parseInt(version?.split(".")[0] ?? "0", 10);
|
|
180
|
+
const providerCheckName = (providerId, name) => name === providerId || name.startsWith(`${providerId}-`);
|