rea-agents 6.3.0 → 6.4.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 +7 -4
- package/bridge/ghidra/ReaGhidraApplySeeds.java +151 -0
- package/bridge/ghidra/ReaGhidraBridge.java +597 -209
- package/bridge/ghidra/ReaGhidraNoReturnFix.java +123 -0
- package/dist/adb/AdbConfiguration.js +88 -0
- package/dist/adb/AdbDeviceOutput.js +186 -0
- package/dist/adb/AdbProvider.js +1190 -0
- package/dist/adb/AdbShellOutput.js +309 -0
- package/dist/android/AndroidTargetSnapshot.js +104 -22
- package/dist/android/JadxConfiguration.js +44 -24
- package/dist/android/JadxProvider.js +51 -19
- package/dist/apktool/ApktoolConfiguration.js +84 -0
- package/dist/apktool/ApktoolDecodeOutput.js +59 -0
- package/dist/apktool/ApktoolProvider.js +506 -0
- package/dist/application/AnalysisProvider.js +8 -2
- package/dist/application/AppBundleExecutable.js +27 -22
- package/dist/application/BinaryTargetResolver.js +22 -2
- package/dist/application/CapabilityInventory.js +11 -1
- package/dist/application/ClientConfigurationDocument.js +70 -0
- package/dist/application/ClientConfigurationFiles.js +91 -0
- package/dist/application/ClientRegistrationEnvironment.js +35 -0
- package/dist/application/ClientRegistrationStatus.js +66 -31
- package/dist/application/DirectAnalysis.js +2 -2
- package/dist/application/DirectAnalysisCleanup.js +1 -1
- package/dist/application/DirectAnalysisStatus.js +1 -1
- package/dist/application/Doctor.js +5 -3
- package/dist/application/DoctorDiagnostics.js +1 -1
- package/dist/application/EvidenceBundleCommands.js +6 -12
- package/dist/application/GeminiClientSettings.js +139 -0
- package/dist/application/HermesClientHome.js +115 -0
- package/dist/application/HopperDownload.js +39 -0
- package/dist/application/InvestigationProviders.js +6 -0
- package/dist/application/JsonFiles.js +6 -5
- package/dist/{cliJsonFile.js → application/JsonInputFile.js} +108 -35
- package/dist/application/LinuxHopper.js +4 -12
- package/dist/application/MacHopper.js +172 -149
- package/dist/application/MipsElfAbiFlags.js +140 -0
- package/dist/application/ProviderOperationExecution.js +31 -0
- package/dist/application/ReconstructionObligationCandidates.js +9 -3
- package/dist/application/ReferenceSourceImport.js +76 -29
- package/dist/application/ReferenceSourceImportEntries.js +18 -9
- package/dist/application/ReferenceSourceImportPolicy.js +99 -23
- package/dist/application/ReferenceSourceImportTypes.js +30 -22
- package/dist/application/ReferenceSourceVcsAdapter.js +145 -30
- package/dist/application/RegularFileRead.js +93 -7
- package/dist/application/Setup.js +54 -6
- package/dist/application/SetupClientConfiguration.js +116 -16
- package/dist/application/SetupHost.js +102 -24
- package/dist/application/SetupPlan.js +5 -8
- package/dist/application/SetupSkill.js +58 -39
- package/dist/application/SupportedClients.js +290 -65
- package/dist/application/Uninstall.js +158 -10
- package/dist/application/UpdateMaintenance.js +17 -21
- package/dist/application/Utf8JsonInput.js +7 -0
- package/dist/application/WebScriptExportService.js +131 -65
- package/dist/application/WebSourceLocationService.js +3 -0
- package/dist/application/adb/AdbDeviceAnalysisPort.js +1 -0
- package/dist/application/adb/AdbDeviceAnalysisService.js +13 -0
- package/dist/application/analysisView/AnalysisViewService.js +40 -2
- package/dist/application/apktool/ApktoolResourceAnalysisPort.js +1 -0
- package/dist/application/apktool/ApktoolResourceAnalysisService.js +13 -0
- package/dist/application/binary/AnalysisClientCleanup.js +0 -24
- package/dist/application/binary/BinarySession.js +2 -1
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +2 -1
- package/dist/application/binaryDiagnostics/PeResourcesPort.js +1 -0
- package/dist/application/binaryDiagnostics/PeResourcesService.js +111 -0
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +2 -1
- package/dist/application/evm/EvmInterfaceService.js +3 -0
- package/dist/application/firmware/FirmwareAnalysisService.js +3 -0
- package/dist/application/investigation/InspectEvidenceBundle.js +18 -0
- package/dist/application/javascript/ElectronBoundaryGraphContext.js +3 -2
- package/dist/application/javascript/ElectronBoundaryGraphIpc.js +2 -2
- package/dist/application/javascript/ElectronBoundaryGraphNative.js +19 -6
- package/dist/application/javascript/ElectronBoundaryGraphWindows.js +3 -3
- package/dist/application/javascript/JavaScriptApplicationService.js +22 -11
- package/dist/application/javascript/JavaScriptApplicationWorkflowEvidence.js +5 -6
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +171 -30
- package/dist/application/javascript/JavaScriptArtifactGraphAccumulator.js +14 -16
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +67 -19
- package/dist/application/javascript/JavaScriptArtifactGraphBundlers.js +4 -3
- package/dist/application/javascript/JavaScriptArtifactGraphContext.js +11 -5
- package/dist/application/javascript/JavaScriptArtifactGraphFindings.js +19 -16
- package/dist/application/javascript/JavaScriptArtifactGraphStructure.js +92 -10
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +258 -235
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +115 -26
- package/dist/application/javascript/JavaScriptArtifactReconstructionInput.js +4 -1
- package/dist/application/javascript/JavaScriptModuleRelationships.js +6 -4
- package/dist/application/javascript/JavaScriptRecoveryService.js +3 -0
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +9 -3
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +199 -61
- package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +12 -19
- package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +4 -1
- package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -3
- package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +5 -3
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +19 -8
- package/dist/application/jeb/JebAnalysisPort.js +1 -0
- package/dist/application/jeb/JebAnalysisService.js +13 -0
- package/dist/application/managed/ManagedApplicationGraphService.js +1 -1
- package/dist/application/native/NativeUiActionTrace.js +16 -8
- package/dist/application/native/NativeValueTrace.js +23 -6
- package/dist/application/process/ProcessCli.js +25 -20
- package/dist/artifacts/ArtifactPaths.js +129 -8
- package/dist/artifacts/ArtifactProvider.js +20 -15
- package/dist/artifacts/ArtifactReader.js +23 -0
- package/dist/artifacts/ArtifactResourceScope.js +100 -0
- package/dist/artifacts/AsarArtifactReader.js +176 -102
- package/dist/artifacts/AsarHeader.js +24 -0
- package/dist/artifacts/DirectoryArtifactReader.js +168 -65
- package/dist/artifacts/MachOSliceArtifactReader.js +116 -39
- package/dist/artifacts/NativeDmgArtifactReader.js +29 -22
- package/dist/artifacts/SafeOutputTree.js +245 -70
- package/dist/artifacts/SafeOutputTreeCleanup.js +45 -0
- package/dist/artifacts/SafeOutputTreeCreationFailure.js +20 -0
- package/dist/artifacts/SafeOutputTreeIdentity.js +42 -0
- package/dist/artifacts/SafeOutputTreeReadback.js +33 -0
- package/dist/artifacts/ZipArtifactReader.js +77 -27
- package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +58 -18
- package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +62 -24
- package/dist/artifacts/apple/KeyedArchiveReader.js +32 -13
- package/dist/artifacts/extraction/ArtifactExtraction.js +110 -86
- package/dist/artifacts/inventory/ArtifactGraphConstruction.js +110 -43
- package/dist/artifacts/inventory/ArtifactInventory.js +5 -4
- package/dist/artifacts/inventory/classify.js +75 -32
- package/dist/artifacts/inventory/hashStableRootArtifact.js +48 -15
- package/dist/artifacts/inventory/reader.js +13 -5
- package/dist/artifacts/inventory/scanCanonical.js +62 -25
- package/dist/artifacts/inventory/scanReader.js +38 -11
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +15 -9
- package/dist/artifacts/javascript/JavaScriptSourceAnalysis.js +3 -0
- package/dist/browser/BrowserScenarioPartialObservation.js +8 -2
- package/dist/browser/BrowserScenarioSecrets.js +52 -37
- package/dist/browser/CdpBrowserProvider.js +5 -0
- package/dist/browser/CdpCaptureDocuments.js +40 -6
- package/dist/browser/CdpCaptureDomSvg.js +210 -0
- package/dist/browser/CdpCaptureEventBodyShapes.js +4 -8
- package/dist/browser/CdpCaptureEventHandlers.js +26 -9
- package/dist/browser/CdpCaptureEvents.js +4 -9
- package/dist/browser/CdpConnection.js +22 -5
- package/dist/browser/CdpPageCapture.js +1 -1
- package/dist/browser/CdpTargetSession.js +30 -34
- package/dist/browser/CdpWebMcpDiscovery.js +32 -10
- package/dist/browser/ElectronFileScope.js +99 -9
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +60 -4
- package/dist/browser/PlaywrightElectronActiveProvider.js +11 -6
- package/dist/browser/PlaywrightScenarioArtifacts.js +5 -5
- package/dist/browser/PlaywrightScenarioBrowser.js +69 -7
- package/dist/browser/PlaywrightScenarioSession.js +127 -54
- package/dist/browser/PlaywrightScenarioStorage.js +507 -0
- package/dist/browser/PngVisualDiff.js +198 -29
- package/dist/browser/WebSourceMapFetcher.js +3 -3
- package/dist/browser/assets/LocalWebScriptArtifacts.js +3 -1
- package/dist/browser/assets/PublishWebScripts.js +75 -20
- package/dist/browser/assets/WebArtifactReads.js +23 -1
- package/dist/browser/assets/WebSourceLocationArtifacts.js +1 -0
- package/dist/browser/execution/CdpExecutionCapture.js +3 -3
- package/dist/browser/execution/CdpPreciseCoverage.js +5 -8
- package/dist/browser/history/HarCapture.js +14 -0
- package/dist/browser/history/HarCaptureProcess.js +2 -10
- package/dist/browser/history/MitmproxyCaptureAdapter.js +3 -1
- package/dist/browser/modules/WebModuleArtifacts.js +1 -0
- package/dist/cli/adbCommands.js +337 -0
- package/dist/cli/analysisViewCommands.js +39 -1
- package/dist/cli/apktoolCommands.js +53 -0
- package/dist/cli/artifactCommands.js +8 -11
- package/dist/cli/binaryDiagnosticsCommands.js +19 -1
- package/dist/cli/browserAdvancedCommands.js +65 -36
- package/dist/cli/coreBinaryCommands.js +0 -5
- package/dist/cli/coreNativeCommands.js +1 -14
- package/dist/cli/electronCommands.js +6 -1
- package/dist/cli/evmCommands.js +13 -4
- package/dist/cli/firmwareCommands.js +7 -2
- package/dist/cli/javascriptApplicationAnalysis.js +17 -1
- package/dist/cli/javascriptRecoveryCommands.js +22 -15
- package/dist/cli/jebCommands.js +99 -0
- package/dist/cli/jsonOutput.js +11 -4
- package/dist/cli/referenceSourceImportStatus.js +7 -0
- package/dist/cli/setupCommands.js +15 -3
- package/dist/cli/utilityCommands.js +66 -18
- package/dist/cli/webScriptCommands.js +20 -1
- package/dist/cli/webSourceLocationCommands.js +21 -9
- package/dist/cli.js +6 -0
- package/dist/cliBrowserScenarioCommands.js +2 -1
- package/dist/cliCommandNames.js +32 -0
- package/dist/cliJsonInput.js +2 -2
- package/dist/cliObservationOptions.js +3 -0
- package/dist/composition/adb.js +3 -0
- package/dist/composition/apktool.js +3 -0
- package/dist/composition/binaryDiagnostics.js +4 -0
- package/dist/composition/directAnalysis.js +12 -4
- package/dist/composition/javascriptAnalysisWorkerProcess.js +48 -0
- package/dist/composition/jeb.js +6 -0
- package/dist/config/environment.js +10 -0
- package/dist/config/ghidraLanguageOverride.js +9 -0
- package/dist/config/mcpInputSchemaProfile.js +27 -0
- package/dist/config/parseConfig.js +28 -1
- package/dist/contracts/adb/adbToolContracts.js +353 -0
- package/dist/contracts/advertisedInputExamples.js +48 -0
- package/dist/contracts/analysisDetail.js +10 -0
- package/dist/contracts/analysisViewToolContracts.js +17 -2
- package/dist/contracts/apktool/apktoolToolContracts.js +33 -0
- package/dist/contracts/artifactToolContracts.js +8 -5
- package/dist/contracts/browserToolContracts.js +1 -1
- package/dist/contracts/compactInputSchema.js +256 -0
- package/dist/contracts/functionWorkflowToolContracts.js +1 -1
- package/dist/contracts/inputSchemaPresentation.js +10 -10
- package/dist/contracts/javascript/electronToolContracts.js +15 -3
- package/dist/contracts/javascript/javascriptRecoveryToolContracts.js +1 -1
- package/dist/contracts/jeb/jebToolContracts.js +64 -0
- package/dist/contracts/native/binaryDiagnosticsToolContracts.js +25 -3
- package/dist/contracts/officialToolContracts.js +5 -5
- package/dist/contracts/sessionToolContracts.js +2 -2
- package/dist/contracts/sessionToolSchemas.js +6 -2
- package/dist/contracts/toolContractExamples.js +6 -0
- package/dist/contracts/toolContractTypes.js +3 -0
- package/dist/contracts/toolContracts.js +6 -0
- package/dist/contracts/toolEffects.js +167 -0
- package/dist/contracts/toolOutputSchemaGroups.js +5 -3
- package/dist/contracts/toolSchemaMetadata.js +82 -7
- package/dist/domain/adb/adbDeviceAnalysis.js +583 -0
- package/dist/domain/analysisErrorCleanup.js +29 -0
- package/dist/domain/analysisErrorPresentation.js +18 -6
- package/dist/domain/analysisErrorProjection.js +4 -1
- package/dist/domain/analysisSnapshot.js +2 -1
- package/dist/domain/analysisView/analysisView.js +52 -2
- package/dist/domain/analysisView/binaryLayoutView.js +2 -0
- package/dist/domain/analysisView/javascriptApplicationView.js +2 -0
- package/dist/domain/analysisView/nativeFunctionView.js +155 -0
- package/dist/domain/android/androidApplication.js +25 -20
- package/dist/domain/apktool/apktoolResourceAnalysis.js +77 -0
- package/dist/domain/apple/appleApplication.js +40 -23
- package/dist/domain/apple/appleBundleAnatomy.js +95 -28
- package/dist/domain/apple/dyldPaths.js +9 -1
- package/dist/domain/apple/dylibResolution.js +2 -2
- package/dist/domain/apple/interfaceBuilderGraph.js +22 -19
- package/dist/domain/apple/interfaceBuilderKeyedArchive.js +10 -1
- package/dist/domain/artifactComparison.js +35 -7
- package/dist/domain/artifactGraph.js +5 -3
- package/dist/domain/artifactIdentity.js +30 -2
- package/dist/domain/artifactInspection.js +3 -43
- package/dist/domain/artifactIntegrityPolicy.js +10 -0
- package/dist/domain/artifactInventoryEvidence.js +11 -12
- package/dist/domain/artifactPathSyntax.js +1 -1
- package/dist/domain/binaryTarget.js +16 -0
- package/dist/domain/binaryTargetTypes.js +8 -1
- package/dist/domain/browserObservationSchemas.js +8 -4
- package/dist/domain/canonicalJsonDigestSteps.js +34 -12
- package/dist/domain/compactStringIdentity.js +9 -0
- package/dist/domain/evidence.js +2 -1
- package/dist/domain/evidenceRecordSummary.js +144 -0
- package/dist/domain/inputIssueProjection.js +16 -1
- package/dist/domain/javascript/electronStaticAnalysisBrowser.js +12 -13
- package/dist/domain/javascript/electronStaticAnalysisIpc.js +11 -20
- package/dist/domain/javascript/electronStaticAnalysisNative.js +70 -40
- package/dist/domain/javascript/electronStaticAnalysisValues.js +16 -0
- package/dist/domain/javascript/javascriptAnalysisResourceControls.js +45 -0
- package/dist/domain/javascript/javascriptApplicationAnalysis.js +4 -1
- package/dist/domain/javascript/javascriptApplicationChangeGraph.js +6 -6
- package/dist/domain/javascript/javascriptApplicationGraph.js +79 -18
- package/dist/domain/javascript/javascriptApplicationProjectionResources.js +35 -0
- package/dist/domain/javascript/javascriptApplicationVersionItems.js +28 -12
- package/dist/domain/javascript/javascriptApplicationVersionKeys.js +9 -1
- package/dist/domain/javascript/javascriptAstFingerprint.js +58 -31
- package/dist/domain/javascript/javascriptAstValues.js +32 -0
- package/dist/domain/javascript/javascriptElectronMemberWrites.js +50 -0
- package/dist/domain/javascript/javascriptModuleSemanticIr.js +15 -0
- package/dist/domain/javascript/javascriptNativeHandoff.js +6 -1
- package/dist/domain/javascript/javascriptRpcEndpoints.js +253 -0
- package/dist/domain/javascript/javascriptRuntimeLoadState.js +1 -3
- package/dist/domain/javascript/javascriptRuntimeReconciliationGraph.js +7 -2
- package/dist/domain/javascript/javascriptRuntimeStaticCandidates.js +8 -11
- package/dist/domain/javascript/javascriptSemanticAnalysis.js +190 -33
- package/dist/domain/javascript/javascriptSemanticCallableResults.js +42 -0
- package/dist/domain/javascript/javascriptSemanticCalls.js +8 -1
- package/dist/domain/javascript/javascriptSemanticDerivedAnalysis.js +15 -4
- package/dist/domain/javascript/javascriptSemanticFileProjection.js +1 -0
- package/dist/domain/javascript/javascriptSemanticFingerprints.js +42 -41
- package/dist/domain/javascript/javascriptSemanticGraph.js +69 -23
- package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +2 -10
- package/dist/domain/javascript/javascriptSemanticIterationSources.js +40 -7
- package/dist/domain/javascript/javascriptSemanticMemberMutations.js +341 -87
- package/dist/domain/javascript/javascriptSemanticMutationValues.js +78 -35
- package/dist/domain/javascript/javascriptSemanticObjectReferences.js +381 -0
- package/dist/domain/javascript/javascriptSemanticObjects.js +7 -1
- package/dist/domain/javascript/javascriptSemanticProjection.js +52 -14
- package/dist/domain/javascript/javascriptSemanticPropertyPaths.js +77 -0
- package/dist/domain/javascript/javascriptSemanticQuery.js +27 -29
- package/dist/domain/javascript/javascriptSemanticQueryIdentity.js +1 -3
- package/dist/domain/javascript/javascriptSemanticReturnedReferences.js +219 -0
- package/dist/domain/javascript/javascriptSemanticReturns.js +13 -23
- package/dist/domain/javascript/javascriptSemanticState.js +22 -2
- package/dist/domain/javascript/javascriptSemanticTraversal.js +19 -1
- package/dist/domain/javascript/javascriptSemanticValues.js +47 -19
- package/dist/domain/javascript/javascriptSourceAnalysis.js +1 -0
- package/dist/domain/javascript/javascriptStaticAnalysis.js +29 -17
- package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +8 -6
- package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +49 -5
- package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +8 -4
- package/dist/domain/javascript/javascriptStaticAnalysisState.js +1 -0
- package/dist/domain/jeb/jebAnalysis.js +114 -0
- package/dist/domain/jsonShape.js +63 -52
- package/dist/domain/jsonValue.js +3 -0
- package/dist/domain/managed/managedApplicationGraph.js +37 -0
- package/dist/domain/managed/managedApplicationGraphCoverage.js +8 -2
- package/dist/domain/managed/managedApplicationGraphNodes.js +32 -22
- package/dist/domain/managed/managedArtifact.js +1 -0
- package/dist/domain/managed/managedInspectionEvidence.js +13 -0
- package/dist/domain/native/nativeAotPe.js +1408 -0
- package/dist/domain/native/nativeInstruction.js +21 -0
- package/dist/domain/native/nativeMetadataRecovery.js +79 -6
- package/dist/domain/native/peResources.js +94 -0
- package/dist/domain/nativeConvention.js +33 -0
- package/dist/domain/process/processCapture.js +91 -1
- package/dist/domain/process/processCaptureExample.js +24 -0
- package/dist/domain/process/processCaptureValidation.js +79 -0
- package/dist/domain/process/processScenario.js +59 -7
- package/dist/domain/propertyListKeys.js +3 -1
- package/dist/domain/referenceSourceGraph.js +25 -16
- package/dist/domain/referenceSourceImportParsing.js +6 -3
- package/dist/domain/referenceSourcePathIdentity.js +22 -0
- package/dist/domain/referenceSourceRequireBindings.js +137 -0
- package/dist/domain/webBundleAnalyzerInspection.js +14 -2
- package/dist/domain/webCaptureDiff.js +102 -4
- package/dist/domain/webScriptExportError.js +10 -4
- package/dist/domain/webScriptExportPlan.js +7 -13
- package/dist/domain/zipPackageFormat.js +4 -1
- package/dist/domain/zodCollectionSteps.js +24 -0
- package/dist/dotnet/ManagedArtifactInspector.js +55 -43
- package/dist/dotnet/ManagedDecodeBudget.js +80 -0
- package/dist/dotnet/ManagedMemberInspector.js +13 -6
- package/dist/dotnet/ManagedMemberInspectorCore.js +2 -2
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +4 -2
- package/dist/dotnet/ManagedMemberRows.js +9 -4
- package/dist/dotnet/ManagedMetadataHeaps.js +15 -5
- package/dist/dotnet/ManagedMetadataInventoryRows.js +20 -11
- package/dist/dotnet/ManagedMetadataLayout.js +10 -4
- package/dist/dotnet/ManagedMethodBodyReader.js +8 -3
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +3 -2
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +49 -34
- package/dist/dotnet/ManagedPeReader.js +41 -25
- package/dist/dotnet/ManagedReaderFailure.js +2 -1
- package/dist/dotnet/ManagedStaticProvider.js +55 -24
- package/dist/evaluation/AgentEvaluationReport.js +9 -5
- package/dist/evaluation/CodexAgentEval.js +43 -13
- package/dist/evm/EvmoleFailures.js +0 -2
- package/dist/evm/EvmoleInterfaceProvider.js +67 -2
- package/dist/filesystem/OwnedDirectoryHandle.js +29 -0
- package/dist/filesystem/OwnedFileHandle.js +36 -0
- package/dist/filesystem/RegularFile.js +33 -1
- package/dist/filesystem/StableFileHash.js +46 -0
- package/dist/filesystem/readFileHandleChunks.js +26 -0
- package/dist/firmware/FirmwareCommand.js +25 -3
- package/dist/firmware/FirmwareFiles.js +23 -8
- package/dist/firmware/FirmwareProvider.js +156 -32
- package/dist/firmware/FirmwarePublication.js +24 -3
- package/dist/firmware/FirmwareRelease.js +1 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +55 -4
- package/dist/ghidra/GhidraAnalysisSeeds.js +211 -0
- package/dist/ghidra/GhidraClient.js +76 -4
- package/dist/ghidra/GhidraDiagnostics.js +11 -8
- package/dist/ghidra/GhidraFunctionValues.js +26 -2
- package/dist/ghidra/GhidraInstallation.js +1 -1
- package/dist/ghidra/GhidraInventoryValues.js +23 -1
- package/dist/ghidra/GhidraLanguageCatalog.js +99 -0
- package/dist/ghidra/GhidraLauncher.js +80 -39
- package/dist/ghidra/GhidraLoadImageAttest.js +49 -2
- package/dist/ghidra/GhidraMipsProfile.js +79 -0
- package/dist/ghidra/GhidraProvider.js +57 -9
- package/dist/ghidra/GhidraProviderCapabilities.js +18 -8
- package/dist/ghidra/GhidraProviderClient.js +282 -21
- package/dist/ghidra/GhidraSessionValues.js +9 -2
- package/dist/hopper/HopperAnalysisProfile.js +2 -0
- package/dist/hopper/HopperClient.js +15 -6
- package/dist/hopper/HopperMachOImage.js +2 -0
- package/dist/hopper/HopperProvider.js +13 -2
- package/dist/ida/IdaOperationRunner.js +8 -2
- package/dist/ida/IdaProvider.js +7 -0
- package/dist/ida/IdaSessionClient.js +3 -1
- package/dist/ida/IdaWorkspace.js +4 -2
- package/dist/javascript/analysis/JavaScriptAnalysisTransfer.js +326 -0
- package/dist/javascript/analysis/JavaScriptAnalysisWorker.js +520 -0
- package/dist/javascript/analysis/JavaScriptAnalysisWorkerProtocol.js +63 -0
- package/dist/javascript/analysis/JavaScriptAnalysisWorkerSchemas.js +521 -0
- package/dist/javascript/analysis/JavaScriptAnalysisWorkerServer.js +116 -0
- package/dist/javascript/recovery/RecoveryFiles.js +18 -7
- package/dist/javascript/recovery/WakaruCommand.js +22 -9
- package/dist/javascript/recovery/WakaruExecution.js +11 -7
- package/dist/javascript/recovery/WakaruProvider.js +139 -16
- package/dist/javascript/recovery/WakaruPublication.js +14 -4
- package/dist/javascript/recovery/WakaruRelease.js +10 -3
- package/dist/javascript/recovery/WakaruReport.js +12 -7
- package/dist/javascript/recovery/WakaruVersion.js +24 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +16 -3
- package/dist/javascript/sourceMaps/SourceMapDecoder.js +63 -7
- package/dist/javascript/sourceMaps/SourceMapFormat.js +15 -4
- package/dist/jeb/JebMcpConnection.js +179 -0
- package/dist/jeb/JebMcpEndpoint.js +40 -0
- package/dist/jeb/JebProvider.js +299 -0
- package/dist/mcpDoctor.js +15 -6
- package/dist/native/AppleDispatchCoverage.js +25 -12
- package/dist/native/AppleDispatchMetadata.js +14 -7
- package/dist/native/AppleMachoFixups.js +71 -15
- package/dist/native/AppleMachoSelection.js +23 -3
- package/dist/native/NativeMacOSProvider.js +91 -16
- package/dist/native/NativeMachoInspection.js +11 -7
- package/dist/native/NativeUiHelperRuntime.js +6 -1
- package/dist/native/NativeUiObservation.js +4 -3
- package/dist/native/pe/PeResourceLayout.js +120 -0
- package/dist/native/pe/PeResourceParser.js +286 -0
- package/dist/native/pe/PeResourcesProvider.js +63 -0
- package/dist/process/BoundedFileBytes.js +30 -7
- package/dist/process/DarwinProcessRunTokenReader.js +8 -3
- package/dist/process/OwnedCommand.js +4 -3
- package/dist/process/ProcessOwnership.js +104 -112
- package/dist/process/ProcessOwnershipObservation.js +96 -39
- package/dist/process/ProcessOwnershipProcessTree.js +4 -4
- package/dist/process/ProcessRunTokenObservations.js +48 -0
- package/dist/process/ProviderDiagnosticRedaction.js +17 -0
- package/dist/process/ProviderProcess.js +4 -3
- package/dist/process/capture/FilesystemSnapshot.js +28 -4
- package/dist/process/capture/ProcessCaptureLifecycle.js +350 -59
- package/dist/process/capture/ProcessCaptureProgress.js +62 -0
- package/dist/process/capture/ProcessHarness.js +194 -26
- package/dist/process/capture/ProcessNormalization.js +3 -1
- package/dist/process/capture/ProcessScenarioRuntimeValidation.js +5 -2
- package/dist/process/capture/TerminalRenderer.js +24 -7
- package/dist/reference/ReferenceSourceReader.js +22 -5
- package/dist/reference/ReferenceSourceReaderEntries.js +114 -51
- package/dist/reference/ReferenceSourceReaderErrors.js +12 -0
- package/dist/reference/ReferenceSourceReaderFile.js +50 -10
- package/dist/reference/ReferenceSourceReaderValidate.js +4 -4
- package/dist/server/EvidenceMcpServer.js +36 -1
- package/dist/server/admittedToolOperation.js +9 -0
- package/dist/server/contractToolHandler.js +21 -0
- package/dist/server/createServer.js +97 -25
- package/dist/server/registerAdbTools.js +36 -0
- package/dist/server/registerAnalysisViewTool.js +4 -9
- package/dist/server/registerAndroidTools.js +3 -12
- package/dist/server/registerApktoolTools.js +14 -0
- package/dist/server/registerApplicationTools/compareExportShapes.js +4 -6
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +4 -5
- package/dist/server/registerApplicationTools/compareVersions.js +3 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -2
- package/dist/server/registerApplicationTools/obligationLedger.js +3 -2
- package/dist/server/registerApplicationTools/projectMobileApplicationGraph.js +3 -2
- package/dist/server/registerApplicationTools/traceFeature.js +3 -2
- package/dist/server/registerApplicationTools/traceSemantics.js +4 -5
- package/dist/server/registerArtifactComparisonTool.js +5 -2
- package/dist/server/registerBinaryDiagnosticsTools.js +16 -4
- package/dist/server/registerBrowserScenarioTool.js +10 -7
- package/dist/server/registerBrowserTools.js +12 -9
- package/dist/server/registerBundleComparisonTool.js +16 -2
- package/dist/server/registerCloseLifecycleTool.js +32 -3
- package/dist/server/registerElectronTools.js +28 -10
- package/dist/server/registerEvmTools.js +4 -9
- package/dist/server/registerFirmwareTools.js +3 -11
- package/dist/server/registerFunctionComparisonTool.js +5 -2
- package/dist/server/registerJavaScriptRecoveryTool.js +4 -9
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +9 -6
- package/dist/server/registerJebTools.js +16 -0
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -6
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -2
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +3 -2
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +3 -2
- package/dist/server/registerPeResourcesTool.js +9 -0
- package/dist/server/registerProcessComparisonTool.js +5 -2
- package/dist/server/registerRecordedCrashTools.js +4 -9
- package/dist/server/registerSessionRecordTools.js +2 -1
- package/dist/server/registerSessionTools.js +49 -18
- package/dist/server/registerWebModuleTool.js +4 -9
- package/dist/server/registerWebNetworkCaptureTool.js +4 -9
- package/dist/server/registerWebRuntimeTools.js +6 -5
- package/dist/server/registerWebScriptTool.js +6 -3
- package/dist/server/registerWebSourceLocationTool.js +4 -9
- package/dist/server/toolResult.js +21 -2
- package/dist/windows/WindowsPrivateRuntime.js +12 -5
- package/install.sh +7 -4
- package/licenses/nativeaot-parser-MIT.txt +28 -0
- package/native/windows/build/manifest.json +2 -2
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +33 -5
- package/scripts/rea.mjs +16 -1
- package/skills/reverse-engineer-anything/SKILL.md +52 -51
- package/skills/reverse-engineer-anything/references/connection-and-recovery.md +127 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +8 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +92 -13
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +22 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { isOwnedClientRegistrationCommand } from "./ClientRegistrationIdentity.js";
|
|
3
3
|
import { linuxHopperInstallDisclosure, } from "./LinuxHopper.js";
|
|
4
|
-
import { skillDestinations } from "./SetupSkill.js";
|
|
5
4
|
import { macHopperInstallDisclosure } from "./MacHopper.js";
|
|
6
5
|
/** Discover supported clients and current registrations without mutation. */
|
|
7
6
|
export const discoverSetupState = async (input) => {
|
|
@@ -118,7 +117,7 @@ export const planSetupActions = async (input) => {
|
|
|
118
117
|
installHopper: input.discovery.installHopper,
|
|
119
118
|
installSkill: input.installSkill,
|
|
120
119
|
skillClientIds: input.skillClientIds,
|
|
121
|
-
|
|
120
|
+
skillDestinations: input.host.skillDestinations(input.skillClientIds),
|
|
122
121
|
clients: clientPlans,
|
|
123
122
|
providerEnvironment: input.providerEnvironment,
|
|
124
123
|
...(input.discovery.linuxPackageFamily === undefined
|
|
@@ -170,10 +169,8 @@ const setupPlan = (input) => [
|
|
|
170
169
|
...(backupPath === undefined ? {} : { backupPath }),
|
|
171
170
|
})),
|
|
172
171
|
...(input.installSkill
|
|
173
|
-
?
|
|
174
|
-
id: client === "
|
|
175
|
-
? "install_skill:claude_code"
|
|
176
|
-
: "install_skill",
|
|
172
|
+
? input.skillDestinations.map(({ client, path }) => ({
|
|
173
|
+
id: client === "shared" ? "install_skill" : `install_skill:${client}`,
|
|
177
174
|
kind: "install_skill",
|
|
178
175
|
label: client === "claude_code"
|
|
179
176
|
? "REA skill for Claude Code"
|
|
@@ -201,8 +198,8 @@ const clientConfigurationDetail = (client, command, environment, installHopper)
|
|
|
201
198
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
202
199
|
.map(([name, value]) => `${name}=${value}`);
|
|
203
200
|
const environmentDetail = entries.length === 0
|
|
204
|
-
? " No additional environment variables."
|
|
205
|
-
: `
|
|
201
|
+
? " No additional provider environment variables. Existing server environment overrides are retained."
|
|
202
|
+
: ` Detected provider environment: ${entries.join(", ")}. Existing server environment overrides are retained; detected values replace matching keys.`;
|
|
206
203
|
const hopperDetail = installHopper
|
|
207
204
|
? environment.HOPPER_LAUNCHER_PATH === undefined
|
|
208
205
|
? " If this plan installs Hopper, REA will add HOPPER_LAUNCHER_PATH using the launcher path reported by that installation."
|
|
@@ -1,45 +1,54 @@
|
|
|
1
|
-
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
1
|
+
import { copyFile, lstat, mkdir, readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import writeFileAtomic from "write-file-atomic";
|
|
4
5
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
5
|
-
|
|
6
|
+
import { NonRegularFileReadError } from "../filesystem/RegularFile.js";
|
|
7
|
+
import { readRegularFileText } from "./RegularFileRead.js";
|
|
8
|
+
import { clientSkillDirectories } from "./SupportedClients.js";
|
|
9
|
+
export const MANAGED_SKILL_FILES = [
|
|
6
10
|
"SKILL.md",
|
|
11
|
+
"references/connection-and-recovery.md",
|
|
7
12
|
"references/native-and-artifacts.md",
|
|
8
13
|
"references/javascript-applications.md",
|
|
9
14
|
"references/android-applications.md",
|
|
10
15
|
"references/runtime-observation.md",
|
|
11
16
|
"references/evidence-workflows.md",
|
|
12
17
|
];
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
export const isManagedSkillManifest = (content) => content.startsWith(`---\nname: ${PRODUCT_IDENTITY.skillName}\n`) ||
|
|
19
|
+
content.startsWith(`---\r\nname: ${PRODUCT_IDENTITY.skillName}\r\n`);
|
|
20
|
+
export const readSkillFile = (path) => readRegularFileText(path, { symlinks: "reject" });
|
|
15
21
|
/** Resolve the skill roots required by a client selection, or all owned roots. */
|
|
16
|
-
export const skillDestinations = (home, clientIds,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
export const skillDestinations = (home, clientIds, environment = {}, platform = process.platform) => clientSkillDirectories(home, clientIds, environment, platform).map(({ client, directory }) => ({
|
|
23
|
+
client,
|
|
24
|
+
path: join(directory, PRODUCT_IDENTITY.skillName),
|
|
25
|
+
}));
|
|
26
|
+
/** Resolve existing REA-owned skill roots using setup's canonical destinations. */
|
|
27
|
+
export const existingSkillDestinations = async (home, environment = {}, platform = process.platform) => {
|
|
28
|
+
const existing = [];
|
|
29
|
+
for (const destination of skillDestinations(home, undefined, environment, platform)) {
|
|
30
|
+
try {
|
|
31
|
+
const root = await lstat(destination.path);
|
|
32
|
+
if (root.isSymbolicLink() || !root.isDirectory())
|
|
33
|
+
continue;
|
|
34
|
+
const manifest = join(destination.path, "SKILL.md");
|
|
35
|
+
const content = await readSkillFile(manifest);
|
|
36
|
+
if (isManagedSkillManifest(content))
|
|
37
|
+
existing.push(destination);
|
|
38
|
+
}
|
|
39
|
+
catch (cause) {
|
|
40
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
|
|
41
|
+
continue;
|
|
42
|
+
if (cause instanceof NonRegularFileReadError)
|
|
43
|
+
continue;
|
|
44
|
+
throw cause;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return existing;
|
|
39
48
|
};
|
|
40
49
|
const readOptionalText = async (path) => {
|
|
41
50
|
try {
|
|
42
|
-
return await
|
|
51
|
+
return await readSkillFile(path);
|
|
43
52
|
}
|
|
44
53
|
catch (cause) {
|
|
45
54
|
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
|
|
@@ -47,7 +56,7 @@ const readOptionalText = async (path) => {
|
|
|
47
56
|
throw cause;
|
|
48
57
|
}
|
|
49
58
|
};
|
|
50
|
-
const canonicalSkillFiles = async (home, clientIds,
|
|
59
|
+
const canonicalSkillFiles = async (home, clientIds, environment = {}, platform = process.platform) => Promise.all(skillDestinations(home, clientIds, environment, platform).flatMap(({ path: root }) => MANAGED_SKILL_FILES.map(async (relativePath) => {
|
|
51
60
|
const destination = join(root, relativePath);
|
|
52
61
|
return {
|
|
53
62
|
destination,
|
|
@@ -56,9 +65,9 @@ const canonicalSkillFiles = async (home, clientIds, claudeSkillsDirectory) => Pr
|
|
|
56
65
|
};
|
|
57
66
|
})));
|
|
58
67
|
/** Report whether setup would change any file in the managed REA skill bundle. */
|
|
59
|
-
export const canonicalSkillNeedsInstall = async (home, clientIds = [],
|
|
68
|
+
export const canonicalSkillNeedsInstall = async (home, clientIds = [], environment = {}, platform = process.platform) => {
|
|
60
69
|
try {
|
|
61
|
-
return (await canonicalSkillFiles(home, clientIds,
|
|
70
|
+
return (await canonicalSkillFiles(home, clientIds, environment, platform)).some(({ content, original }) => original !== content);
|
|
62
71
|
}
|
|
63
72
|
catch (cause) {
|
|
64
73
|
// Unreadable skill state fails open to install so setup can repair it.
|
|
@@ -67,6 +76,16 @@ export const canonicalSkillNeedsInstall = async (home, clientIds = [], claudeSki
|
|
|
67
76
|
}
|
|
68
77
|
};
|
|
69
78
|
const writeText = (path, content) => writeFileAtomic(path, content, { encoding: "utf8", mode: 0o600 });
|
|
79
|
+
const preserveSkillBackup = async (source, destination) => {
|
|
80
|
+
try {
|
|
81
|
+
await copyFile(source, destination, fsConstants.COPYFILE_EXCL);
|
|
82
|
+
}
|
|
83
|
+
catch (cause) {
|
|
84
|
+
if (cause instanceof Error && "code" in cause && cause.code === "EEXIST")
|
|
85
|
+
return;
|
|
86
|
+
throw cause;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
70
89
|
const restoreSkillFiles = async (changed) => {
|
|
71
90
|
for (const { destination, original } of [...changed].reverse()) {
|
|
72
91
|
if (original === undefined)
|
|
@@ -76,22 +95,22 @@ const restoreSkillFiles = async (changed) => {
|
|
|
76
95
|
}
|
|
77
96
|
};
|
|
78
97
|
/** Transactionally install or upgrade the canonical REA skill and references. */
|
|
79
|
-
export const installCanonicalSkill = async (home, clientIds = [],
|
|
98
|
+
export const installCanonicalSkill = async (home, clientIds = [], environment = {}, platform = process.platform) => {
|
|
80
99
|
let changed = [];
|
|
81
100
|
try {
|
|
82
|
-
const canonical = await canonicalSkillFiles(home, clientIds,
|
|
101
|
+
const canonical = await canonicalSkillFiles(home, clientIds, environment, platform);
|
|
83
102
|
changed = canonical.filter(({ content, original }) => original !== content);
|
|
84
103
|
if (changed.length === 0)
|
|
85
104
|
return "unchanged";
|
|
86
105
|
for (const { destination, original } of changed) {
|
|
87
106
|
await mkdir(dirname(destination), { recursive: true });
|
|
88
107
|
if (original !== undefined)
|
|
89
|
-
await
|
|
108
|
+
await preserveSkillBackup(destination, `${destination}.rea.backup`);
|
|
90
109
|
}
|
|
91
110
|
for (const { destination, content } of changed)
|
|
92
111
|
await writeText(destination, content);
|
|
93
112
|
for (const { destination, content } of changed)
|
|
94
|
-
if ((await
|
|
113
|
+
if ((await readSkillFile(destination)) !== content)
|
|
95
114
|
throw new Error(`skill readback mismatch: ${destination}`);
|
|
96
115
|
return "installed";
|
|
97
116
|
}
|
|
@@ -109,12 +128,12 @@ export const installCanonicalSkill = async (home, clientIds = [], claudeSkillsDi
|
|
|
109
128
|
}
|
|
110
129
|
};
|
|
111
130
|
/** Read installed skill metadata and compare every managed location. */
|
|
112
|
-
export const readInstalledSkillIdentity = async (home, clientIds,
|
|
113
|
-
const destinations = skillDestinations(home, clientIds,
|
|
131
|
+
export const readInstalledSkillIdentity = async (home, clientIds, environment = {}, platform = process.platform) => {
|
|
132
|
+
const destinations = skillDestinations(home, clientIds, environment, platform);
|
|
114
133
|
let content;
|
|
115
134
|
for (const destination of destinations) {
|
|
116
135
|
try {
|
|
117
|
-
content = await
|
|
136
|
+
content = await readSkillFile(join(destination.path, "SKILL.md"));
|
|
118
137
|
break;
|
|
119
138
|
}
|
|
120
139
|
catch {
|
|
@@ -126,7 +145,7 @@ export const readInstalledSkillIdentity = async (home, clientIds, claudeSkillsDi
|
|
|
126
145
|
return undefined;
|
|
127
146
|
const toolCount = /^\s{2}tool_count:\s*(\d+)\s*$/mu.exec(content)?.[1];
|
|
128
147
|
return {
|
|
129
|
-
canonical: !(await canonicalSkillNeedsInstall(home, clientIds,
|
|
148
|
+
canonical: !(await canonicalSkillNeedsInstall(home, clientIds, environment, platform)),
|
|
130
149
|
version: /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(content)?.[1] ?? null,
|
|
131
150
|
toolCount: toolCount === undefined ? null : Number.parseInt(toolCount, 10),
|
|
132
151
|
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { isAbsolute, join } from "node:path";
|
|
1
|
+
import { dirname, isAbsolute, join, resolve, win32 } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { lstatSync } from "node:fs";
|
|
4
|
+
import { err, ok } from "../domain/result.js";
|
|
5
|
+
import { HermesClientPathError, resolveHermesClientHome, } from "./HermesClientHome.js";
|
|
3
6
|
const vscodeUserDirectory = ({ home, platform, env, }) => {
|
|
4
7
|
if (platform === "win32")
|
|
5
8
|
return join(env.APPDATA ?? join(home, "AppData", "Roaming"), "Code", "User");
|
|
@@ -16,8 +19,24 @@ const claudeDesktopDirectory = ({ home, platform, env, }) => {
|
|
|
16
19
|
};
|
|
17
20
|
const claudeCodeConfigDirectory = ({ home, env }) => env.CLAUDE_CONFIG_DIR ?? home;
|
|
18
21
|
const claudeCodeMarkerDirectory = ({ home, env }) => env.CLAUDE_CONFIG_DIR ?? join(home, ".claude");
|
|
19
|
-
const codexDirectory = ({ home, env }) => env.CODEX_HOME
|
|
20
|
-
|
|
22
|
+
const codexDirectory = ({ home, env }) => env.CODEX_HOME || join(home, ".codex");
|
|
23
|
+
/** Match Qwen Code's home override, including tilde and cwd-relative paths. */
|
|
24
|
+
const qwenCodeDirectory = ({ home, env }) => {
|
|
25
|
+
const override = env.QWEN_HOME;
|
|
26
|
+
if (!override)
|
|
27
|
+
return join(home, ".qwen");
|
|
28
|
+
const expanded = override === "~"
|
|
29
|
+
? home
|
|
30
|
+
: override.startsWith("~/") || override.startsWith("~\\")
|
|
31
|
+
? join(home, ...override
|
|
32
|
+
.slice(2)
|
|
33
|
+
.split(/[/\\]+/)
|
|
34
|
+
.filter(Boolean))
|
|
35
|
+
: override;
|
|
36
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
37
|
+
};
|
|
38
|
+
const grokDirectory = ({ home, env }) => env.GROK_HOME || join(home, ".grok");
|
|
39
|
+
const hermesDirectory = ({ home, platform, env }) => resolveHermesClientHome(home, platform, env);
|
|
21
40
|
/** Grok Bot uses an absolute SAND_DATA_ROOT; anything else stays ~/.grokbot. */
|
|
22
41
|
const grokBotDirectory = ({ home, env }) => {
|
|
23
42
|
const root = env.SAND_DATA_ROOT;
|
|
@@ -35,51 +54,101 @@ export const manualRegistrationRemediation = (clientName) => clientName === "gro
|
|
|
35
54
|
? GROK_BOT_MANUAL_REGISTRATION_REMEDIATION
|
|
36
55
|
: undefined;
|
|
37
56
|
const OMP_PROFILE_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
|
|
38
|
-
|
|
39
|
-
* OMP's active named profile. OMP_PROFILE wins over the legacy PI_PROFILE even
|
|
40
|
-
* when empty; empty and "default" select the default profile. OMP refuses to
|
|
41
|
-
* start with an invalid name, so one leaves the default location in place.
|
|
42
|
-
*/
|
|
43
|
-
const ompProfile = ({ env }) => {
|
|
44
|
-
const name = (env.OMP_PROFILE ?? env.PI_PROFILE)?.trim();
|
|
45
|
-
return name === undefined ||
|
|
46
|
-
name === "" ||
|
|
47
|
-
name === "default" ||
|
|
48
|
-
name.endsWith(".") ||
|
|
49
|
-
!OMP_PROFILE_NAME.test(name)
|
|
50
|
-
? undefined
|
|
51
|
-
: name;
|
|
52
|
-
};
|
|
57
|
+
const OMP_RESERVED_PROFILE = /^(?:CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(?:\..*)?$/iu;
|
|
53
58
|
/**
|
|
54
59
|
* The OMP agent directory whose `mcp.json` holds user-scope MCP servers. OMP
|
|
55
60
|
* joins PI_CONFIG_DIR to the home directory, and a named profile ignores
|
|
56
|
-
* PI_CODING_AGENT_DIR.
|
|
57
|
-
* process's working directory, so only an absolute override names one file.
|
|
61
|
+
* PI_CODING_AGENT_DIR. Relative overrides use the setup process working directory.
|
|
58
62
|
*/
|
|
59
63
|
const ompAgentDirectory = (context) => {
|
|
60
64
|
const root = join(context.home, context.env.PI_CONFIG_DIR || ".omp");
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
// OMP_PROFILE wins even when empty. Match OMP's validation on every host.
|
|
66
|
+
const profile = (context.env.OMP_PROFILE ?? context.env.PI_PROFILE)?.trim();
|
|
67
|
+
if (profile !== undefined && profile !== "" && profile !== "default") {
|
|
68
|
+
if (!OMP_PROFILE_NAME.test(profile) ||
|
|
69
|
+
profile.endsWith(".") ||
|
|
70
|
+
OMP_RESERVED_PROFILE.test(profile))
|
|
71
|
+
return err({
|
|
72
|
+
path: root,
|
|
73
|
+
message: `Invalid OMP profile ${JSON.stringify(profile)}: names must match ^[a-z0-9][a-z0-9._-]{0,63}$, cannot end with ".", and cannot be a Windows reserved device name (CON, PRN, AUX, NUL, COM0-9, LPT0-9, including extensions). Repair OMP_PROFILE or PI_PROFILE before configuring OMP.`,
|
|
74
|
+
});
|
|
75
|
+
return ok(join(root, "profiles", profile, "agent"));
|
|
76
|
+
}
|
|
64
77
|
const override = context.env.PI_CODING_AGENT_DIR;
|
|
65
|
-
return override
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
return ok(override ? resolve(override) : join(root, "agent"));
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Pi's public getAgentDir()/normalizePath semantics, not OMP's profiles.
|
|
82
|
+
* Keep relative overrides relative: filesystem consumers resolve them in cwd.
|
|
83
|
+
*/
|
|
84
|
+
const piAgentDirectory = ({ home, platform, env, }) => {
|
|
85
|
+
const paths = platform === "win32" ? win32 : { join };
|
|
86
|
+
let directory = env.PI_CODING_AGENT_DIR;
|
|
87
|
+
if (!directory)
|
|
88
|
+
return paths.join(home, ".pi", "agent");
|
|
89
|
+
if (platform === "win32" &&
|
|
90
|
+
directory.startsWith("/") &&
|
|
91
|
+
!directory.startsWith("//") &&
|
|
92
|
+
!directory.includes("\\")) {
|
|
93
|
+
const drive = /^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/iu.exec(directory);
|
|
94
|
+
if (drive !== null)
|
|
95
|
+
directory = `${drive[1]?.toUpperCase()}:\\${drive[2]?.replaceAll("/", "\\") ?? ""}`;
|
|
96
|
+
}
|
|
97
|
+
if (directory === "~")
|
|
98
|
+
return home;
|
|
99
|
+
if (directory.startsWith("~/") ||
|
|
100
|
+
(platform === "win32" && directory.startsWith("~\\")))
|
|
101
|
+
return paths.join(home, directory.slice(2));
|
|
102
|
+
if (directory.startsWith("file://"))
|
|
103
|
+
return fileURLToPath(directory, { windows: platform === "win32" });
|
|
104
|
+
return directory;
|
|
68
105
|
};
|
|
69
106
|
const copilotDirectory = ({ home, env }) => env.COPILOT_HOME ?? join(home, ".copilot");
|
|
70
107
|
const commandCodeDirectory = ({ home }) => join(home, ".commandcode");
|
|
71
|
-
|
|
108
|
+
/**
|
|
109
|
+
* OpenCode appends `OPENCODE_CONFIG_DIR` only when the value is non-empty.
|
|
110
|
+
* An empty variable is the same as unset and leaves the XDG directory in place.
|
|
111
|
+
*/
|
|
112
|
+
const openCodeOverrideDirectory = (env) => {
|
|
113
|
+
const directory = env.OPENCODE_CONFIG_DIR;
|
|
114
|
+
return directory === undefined || directory === "" ? undefined : directory;
|
|
115
|
+
};
|
|
116
|
+
const opencodeDirectory = (context) => openCodeOverrideDirectory(context.env) ??
|
|
117
|
+
join(context.env.XDG_CONFIG_HOME ?? join(context.home, ".config"), "opencode");
|
|
118
|
+
/** OpenCode loads global JSON before JSONC, then explicit file and directory overrides. */
|
|
119
|
+
const openCodeConfigurationPaths = (context) => {
|
|
120
|
+
const directory = join(context.env.XDG_CONFIG_HOME ?? join(context.home, ".config"), "opencode");
|
|
121
|
+
const overrideDirectory = openCodeOverrideDirectory(context.env);
|
|
122
|
+
const paths = [
|
|
123
|
+
join(directory, "config.json"),
|
|
124
|
+
join(directory, "opencode.json"),
|
|
125
|
+
join(directory, "opencode.jsonc"),
|
|
126
|
+
...(context.env.OPENCODE_CONFIG ? [context.env.OPENCODE_CONFIG] : []),
|
|
127
|
+
...(overrideDirectory !== undefined
|
|
128
|
+
? [
|
|
129
|
+
join(overrideDirectory, "opencode.json"),
|
|
130
|
+
join(overrideDirectory, "opencode.jsonc"),
|
|
131
|
+
]
|
|
132
|
+
: []),
|
|
133
|
+
];
|
|
134
|
+
const resolved = paths.map((path) => resolve(path));
|
|
135
|
+
return resolved.filter((path, index) => resolved.lastIndexOf(path) === index);
|
|
136
|
+
};
|
|
72
137
|
const existingOpenCodeConfigPath = (context) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
138
|
+
// The selected directory is loaded after an explicit file by OpenCode V1.
|
|
139
|
+
const overrideDirectory = openCodeOverrideDirectory(context.env);
|
|
140
|
+
const candidates = overrideDirectory !== undefined
|
|
141
|
+
? [
|
|
142
|
+
join(overrideDirectory, "opencode.json"),
|
|
143
|
+
join(overrideDirectory, "opencode.jsonc"),
|
|
144
|
+
].map((path) => resolve(path))
|
|
145
|
+
: context.env.OPENCODE_CONFIG
|
|
146
|
+
? [resolve(context.env.OPENCODE_CONFIG)]
|
|
147
|
+
: [
|
|
148
|
+
join(opencodeDirectory(context), "opencode.json"),
|
|
149
|
+
join(opencodeDirectory(context), "opencode.jsonc"),
|
|
150
|
+
].map((path) => resolve(path));
|
|
151
|
+
return ([...candidates].reverse().find((path) => {
|
|
83
152
|
try {
|
|
84
153
|
lstatSync(path);
|
|
85
154
|
return true;
|
|
@@ -90,17 +159,44 @@ const existingOpenCodeConfigPath = (context) => {
|
|
|
90
159
|
cause.code === "ENOENT");
|
|
91
160
|
}
|
|
92
161
|
}) ??
|
|
93
|
-
|
|
94
|
-
|
|
162
|
+
resolve(overrideDirectory !== undefined
|
|
163
|
+
? join(overrideDirectory, "opencode.json")
|
|
164
|
+
: context.env.OPENCODE_CONFIG ||
|
|
165
|
+
join(opencodeDirectory(context), "opencode.json")));
|
|
166
|
+
};
|
|
167
|
+
const geminiSettings = ({ home, platform, env, }) => {
|
|
168
|
+
const system = env.GEMINI_CLI_SYSTEM_SETTINGS_PATH ||
|
|
169
|
+
(platform === "darwin"
|
|
170
|
+
? "/Library/Application Support/GeminiCli/settings.json"
|
|
171
|
+
: platform === "win32"
|
|
172
|
+
? "C:\\ProgramData\\gemini-cli\\settings.json"
|
|
173
|
+
: "/etc/gemini-cli/settings.json");
|
|
174
|
+
const trustOverride = env.GEMINI_RESTRICTED_MODE === "true" ||
|
|
175
|
+
env.GEMINI_CLI_TRUST_WORKSPACE === "false"
|
|
176
|
+
? false
|
|
177
|
+
: env.GEMINI_CLI_TRUST_WORKSPACE === "true"
|
|
178
|
+
? true
|
|
179
|
+
: undefined;
|
|
180
|
+
return {
|
|
181
|
+
systemPath: system,
|
|
182
|
+
defaultsPath: env.GEMINI_CLI_SYSTEM_DEFAULTS_PATH ||
|
|
183
|
+
join(dirname(system), "system-defaults.json"),
|
|
184
|
+
workspaceDirectory: process.cwd(),
|
|
185
|
+
trustedFoldersPath: env.GEMINI_CLI_TRUSTED_FOLDERS_PATH ||
|
|
186
|
+
join(env.GEMINI_CLI_HOME || home, ".gemini", "trustedFolders.json"),
|
|
187
|
+
...(trustOverride === undefined ? {} : { trustOverride }),
|
|
188
|
+
platform,
|
|
189
|
+
};
|
|
95
190
|
};
|
|
96
191
|
const devinDirectory = ({ home, platform, env }) => platform === "win32"
|
|
97
192
|
? join(env.APPDATA ?? join(home, "AppData", "Roaming"), "devin")
|
|
98
|
-
: join(home, ".config", "devin");
|
|
193
|
+
: join(env.XDG_CONFIG_HOME || join(home, ".config"), "devin");
|
|
99
194
|
/** Stable product metadata used to derive setup discovery and documentation. */
|
|
100
195
|
export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
101
196
|
{
|
|
102
197
|
name: "claude_code",
|
|
103
198
|
displayName: "Claude Code",
|
|
199
|
+
skillPath: ({ home, env }) => join(env.CLAUDE_CONFIG_DIR ?? join(home, ".claude"), "skills"),
|
|
104
200
|
configPath: (context) => join(claudeCodeConfigDirectory(context), ".claude.json"),
|
|
105
201
|
markerPath: claudeCodeMarkerDirectory,
|
|
106
202
|
format: "json",
|
|
@@ -129,8 +225,10 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
|
129
225
|
{
|
|
130
226
|
name: "gemini_cli",
|
|
131
227
|
displayName: "Gemini CLI",
|
|
132
|
-
|
|
133
|
-
|
|
228
|
+
geminiSettings,
|
|
229
|
+
configPath: ({ home, env }) => join(env.GEMINI_CLI_HOME || home, ".gemini", "settings.json"),
|
|
230
|
+
markerPath: ({ home, env }) => join(env.GEMINI_CLI_HOME || home, ".gemini"),
|
|
231
|
+
skillPath: ({ home, env }) => join(env.GEMINI_CLI_HOME || home, ".agents", "skills"),
|
|
134
232
|
format: "json",
|
|
135
233
|
},
|
|
136
234
|
{
|
|
@@ -151,12 +249,14 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
|
151
249
|
name: "opencode",
|
|
152
250
|
displayName: "OpenCode",
|
|
153
251
|
configPath: existingOpenCodeConfigPath,
|
|
252
|
+
configPaths: openCodeConfigurationPaths,
|
|
154
253
|
markerPath: opencodeDirectory,
|
|
155
254
|
format: "opencode",
|
|
156
255
|
},
|
|
157
256
|
{
|
|
158
257
|
name: "antigravity",
|
|
159
258
|
displayName: "Antigravity",
|
|
259
|
+
skillPath: [".gemini", "config", "skills"],
|
|
160
260
|
configPath: [".gemini", "config", "mcp_config.json"],
|
|
161
261
|
markerPath: [".gemini", "config"],
|
|
162
262
|
format: "json",
|
|
@@ -175,6 +275,13 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
|
175
275
|
markerPath: commandCodeDirectory,
|
|
176
276
|
format: "commandcode",
|
|
177
277
|
},
|
|
278
|
+
{
|
|
279
|
+
name: "qwen_code",
|
|
280
|
+
displayName: "Qwen Code",
|
|
281
|
+
configPath: (context) => join(qwenCodeDirectory(context), "settings.json"),
|
|
282
|
+
markerPath: qwenCodeDirectory,
|
|
283
|
+
format: "json",
|
|
284
|
+
},
|
|
178
285
|
{
|
|
179
286
|
name: "vscode",
|
|
180
287
|
displayName: "VS Code",
|
|
@@ -192,10 +299,28 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
|
192
299
|
{
|
|
193
300
|
name: "omp",
|
|
194
301
|
displayName: "OMP",
|
|
195
|
-
configPath: (context) =>
|
|
302
|
+
configPath: (context) => {
|
|
303
|
+
const directory = ompAgentDirectory(context);
|
|
304
|
+
return directory.ok ? ok(join(directory.value, "mcp.json")) : directory;
|
|
305
|
+
},
|
|
196
306
|
markerPath: ompAgentDirectory,
|
|
197
307
|
format: "omp",
|
|
198
308
|
},
|
|
309
|
+
{
|
|
310
|
+
name: "pi",
|
|
311
|
+
displayName: "Pi",
|
|
312
|
+
configPath: (context) => (context.platform === "win32" ? win32 : { join }).join(piAgentDirectory(context), "mcp.json"),
|
|
313
|
+
markerPath: piAgentDirectory,
|
|
314
|
+
format: "pi",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: "hermes",
|
|
318
|
+
displayName: "Hermes",
|
|
319
|
+
skillPath: (context) => join(hermesDirectory(context), "skills"),
|
|
320
|
+
configPath: (context) => join(hermesDirectory(context), "config.yaml"),
|
|
321
|
+
markerPath: hermesDirectory,
|
|
322
|
+
format: "hermes",
|
|
323
|
+
},
|
|
199
324
|
{
|
|
200
325
|
name: "grok_bot",
|
|
201
326
|
displayName: "Grok Bot",
|
|
@@ -204,28 +329,128 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
|
|
204
329
|
format: "unsupported",
|
|
205
330
|
},
|
|
206
331
|
];
|
|
207
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Paths whose presence means doctor and setup should inspect the client.
|
|
334
|
+
* Layered sources count: OpenCode still loads its XDG files when a custom
|
|
335
|
+
* directory override does not exist.
|
|
336
|
+
*/
|
|
337
|
+
export const clientEvidencePaths = (client) => [
|
|
338
|
+
...new Set([
|
|
339
|
+
client.configPath,
|
|
340
|
+
...(client.configPaths ?? []),
|
|
341
|
+
client.markerPath,
|
|
342
|
+
].filter((path) => path !== undefined && path.length > 0)),
|
|
343
|
+
];
|
|
344
|
+
const resolvePath = (path, context) => {
|
|
345
|
+
const resolved = typeof path === "function" ? path(context) : join(context.home, ...path);
|
|
346
|
+
return typeof resolved === "string" ? ok(resolved) : resolved;
|
|
347
|
+
};
|
|
208
348
|
/** Describe every client location that setup, doctor, or uninstall may inspect. */
|
|
209
|
-
export const supportedClients = (home, platform = process.platform, env = {
|
|
210
|
-
APPDATA: process.env.APPDATA,
|
|
211
|
-
CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR,
|
|
212
|
-
CODEX_HOME: process.env.CODEX_HOME,
|
|
213
|
-
COPILOT_HOME: process.env.COPILOT_HOME,
|
|
214
|
-
GROK_HOME: process.env.GROK_HOME,
|
|
215
|
-
OMP_PROFILE: process.env.OMP_PROFILE,
|
|
216
|
-
OPENCODE_CONFIG: process.env.OPENCODE_CONFIG,
|
|
217
|
-
PI_CODING_AGENT_DIR: process.env.PI_CODING_AGENT_DIR,
|
|
218
|
-
PI_CONFIG_DIR: process.env.PI_CONFIG_DIR,
|
|
219
|
-
PI_PROFILE: process.env.PI_PROFILE,
|
|
220
|
-
SAND_DATA_ROOT: process.env.SAND_DATA_ROOT,
|
|
221
|
-
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
|
|
222
|
-
}) => {
|
|
349
|
+
export const supportedClients = (home, platform = process.platform, env = process.env) => {
|
|
223
350
|
const context = { home, platform, env };
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
351
|
+
const definitions = SUPPORTED_CLIENT_DEFINITIONS;
|
|
352
|
+
return definitions.map((definition) => {
|
|
353
|
+
try {
|
|
354
|
+
const config = resolvePath(definition.configPath, context);
|
|
355
|
+
const marker = config.ok
|
|
356
|
+
? resolvePath(definition.markerPath, context)
|
|
357
|
+
: config;
|
|
358
|
+
if (!config.ok)
|
|
359
|
+
return {
|
|
360
|
+
name: definition.name,
|
|
361
|
+
displayName: definition.displayName,
|
|
362
|
+
format: definition.format,
|
|
363
|
+
configPath: config.error.path,
|
|
364
|
+
configPathError: config.error.message,
|
|
365
|
+
};
|
|
366
|
+
if (!marker.ok)
|
|
367
|
+
return {
|
|
368
|
+
name: definition.name,
|
|
369
|
+
displayName: definition.displayName,
|
|
370
|
+
format: definition.format,
|
|
371
|
+
configPath: marker.error.path,
|
|
372
|
+
configPathError: marker.error.message,
|
|
373
|
+
};
|
|
374
|
+
const configPath = config.value;
|
|
375
|
+
return {
|
|
376
|
+
name: definition.name,
|
|
377
|
+
displayName: definition.displayName,
|
|
378
|
+
configPath,
|
|
379
|
+
...(definition.geminiSettings === undefined
|
|
380
|
+
? {}
|
|
381
|
+
: { geminiSettings: definition.geminiSettings(context) }),
|
|
382
|
+
...(definition.configPaths === undefined
|
|
383
|
+
? {}
|
|
384
|
+
: {
|
|
385
|
+
configPaths: [
|
|
386
|
+
...new Set([...definition.configPaths(context), configPath]),
|
|
387
|
+
],
|
|
388
|
+
}),
|
|
389
|
+
markerPath: marker.value,
|
|
390
|
+
format: definition.format,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
catch (cause) {
|
|
394
|
+
if (cause instanceof HermesClientPathError)
|
|
395
|
+
return {
|
|
396
|
+
name: definition.name,
|
|
397
|
+
displayName: definition.displayName,
|
|
398
|
+
format: definition.format,
|
|
399
|
+
configPath: cause.path,
|
|
400
|
+
configPathError: cause.message,
|
|
401
|
+
};
|
|
402
|
+
if (definition.name !== "pi")
|
|
403
|
+
throw cause;
|
|
404
|
+
return {
|
|
405
|
+
name: definition.name,
|
|
406
|
+
displayName: definition.displayName,
|
|
407
|
+
format: definition.format,
|
|
408
|
+
configPath: env.PI_CODING_AGENT_DIR ?? "",
|
|
409
|
+
configPathError: `Invalid PI_CODING_AGENT_DIR: ${cause instanceof Error ? cause.message : String(cause)}. Repair the override before configuring Pi.`,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
/** Resolve personal skill roots using the same client environment as setup. */
|
|
415
|
+
export const clientSkillDirectories = (home, clientIds, environment = {}, platform = process.platform) => {
|
|
416
|
+
const definitions = SUPPORTED_CLIENT_DEFINITIONS;
|
|
417
|
+
const selected = clientIds === undefined
|
|
418
|
+
? [
|
|
419
|
+
"shared",
|
|
420
|
+
...definitions
|
|
421
|
+
.filter(({ skillPath }) => skillPath !== undefined)
|
|
422
|
+
.map(({ name }) => name),
|
|
423
|
+
]
|
|
424
|
+
: clientIds.length === 0
|
|
425
|
+
? ["shared"]
|
|
426
|
+
: clientIds;
|
|
427
|
+
const destinations = new Map();
|
|
428
|
+
for (const client of selected) {
|
|
429
|
+
const definition = definitions.find(({ name }) => name === client);
|
|
430
|
+
let directory;
|
|
431
|
+
try {
|
|
432
|
+
const resolved = definition?.skillPath === undefined
|
|
433
|
+
? ok(join(home, ".agents", "skills"))
|
|
434
|
+
: resolvePath(definition.skillPath, {
|
|
435
|
+
home,
|
|
436
|
+
platform,
|
|
437
|
+
env: environment,
|
|
438
|
+
});
|
|
439
|
+
if (!resolved.ok)
|
|
440
|
+
continue;
|
|
441
|
+
directory = resolved.value;
|
|
442
|
+
}
|
|
443
|
+
catch (cause) {
|
|
444
|
+
// Registration diagnostics own an unresolved profile; never fall back to another skill root.
|
|
445
|
+
if (cause instanceof HermesClientPathError)
|
|
446
|
+
continue;
|
|
447
|
+
throw cause;
|
|
448
|
+
}
|
|
449
|
+
if (!destinations.has(directory))
|
|
450
|
+
destinations.set(directory, {
|
|
451
|
+
client: directory === join(home, ".agents", "skills") ? "shared" : client,
|
|
452
|
+
directory,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
return [...destinations.values()].sort((left, right) => left.directory.localeCompare(right.directory));
|
|
231
456
|
};
|