rea-agents 1.6.0 → 2.0.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.
Files changed (238) hide show
  1. package/README.md +157 -66
  2. package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
  3. package/bridge/hopper_bridge.py +13 -3
  4. package/dist/application/AbortablePromise.js +25 -0
  5. package/dist/application/AnalysisProvider.js +3 -0
  6. package/dist/application/AnalysisProviderEvaluation.js +60 -0
  7. package/dist/application/AnalysisProviderRegistry.js +228 -0
  8. package/dist/application/AnalysisSnapshotCache.js +41 -21
  9. package/dist/application/AnalysisSnapshotFiles.js +5 -6
  10. package/dist/application/BinarySession.js +218 -101
  11. package/dist/application/CapabilityInventory.js +18 -0
  12. package/dist/application/CompositeProvider.js +20 -8
  13. package/dist/application/CrossVersionInvestigation.js +10 -6
  14. package/dist/application/DirectAnalysis.js +49 -35
  15. package/dist/application/Doctor.js +110 -8
  16. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  17. package/dist/application/ElectronBoundaryGraph.js +9 -0
  18. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  19. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  20. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  21. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  22. package/dist/application/EnhancedTools.js +39 -14
  23. package/dist/application/InvestigationProviders.js +41 -0
  24. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  25. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  26. package/dist/application/JavaScriptApplicationService.js +74 -0
  27. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  28. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  29. package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
  30. package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
  31. package/dist/application/JavaScriptArtifactFiles.js +231 -0
  32. package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
  33. package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
  34. package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
  35. package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
  36. package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
  37. package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
  38. package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
  39. package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
  40. package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
  41. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  42. package/dist/application/JavaScriptReplayService.js +266 -0
  43. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  44. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  45. package/dist/application/MacHopper.js +20 -4
  46. package/dist/application/ManagedApplicationGraphService.js +55 -0
  47. package/dist/application/ManagedMemberComparisonService.js +122 -0
  48. package/dist/application/ManagedNativeVerificationService.js +39 -0
  49. package/dist/application/ManagedReconstructionService.js +58 -0
  50. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  51. package/dist/application/PermissionAuthority.js +1 -1
  52. package/dist/application/ProcessCaptureLifecycle.js +1 -1
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProcessSampling.js +16 -12
  55. package/dist/application/ProjectPermissionStore.js +1 -0
  56. package/dist/application/SessionProviderRouter.js +219 -0
  57. package/dist/application/Setup.js +84 -60
  58. package/dist/application/SetupClients.js +1 -1
  59. package/dist/application/SetupPlan.js +9 -2
  60. package/dist/application/SupportedClients.js +40 -16
  61. package/dist/application/Upgrade.js +1 -1
  62. package/dist/application/runtime.js +11 -6
  63. package/dist/artifacts/AsarArtifactReader.js +23 -3
  64. package/dist/browser/CdpBrowserProvider.js +4 -2
  65. package/dist/browser/CdpCaptureEvents.js +49 -0
  66. package/dist/browser/CdpElectronInspection.js +106 -162
  67. package/dist/browser/CdpElectronProvider.js +9 -3
  68. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  69. package/dist/browser/CdpElectronScripts.js +94 -0
  70. package/dist/browser/CdpElectronWorkers.js +56 -0
  71. package/dist/browser/CdpEndpoint.js +14 -1
  72. package/dist/browser/CdpPageCapture.js +8 -4
  73. package/dist/catalogIdentity.js +6 -42
  74. package/dist/cli.js +446 -34
  75. package/dist/cliApplicationCommands.js +62 -0
  76. package/dist/cliBrowserAdvancedCommands.js +5 -4
  77. package/dist/cliBrowserCommands.js +5 -4
  78. package/dist/cliCommandNames.js +56 -0
  79. package/dist/cliElectronCommands.js +106 -3
  80. package/dist/cliEvidenceCommands.js +4 -3
  81. package/dist/cliInvestigationCommands.js +2 -1
  82. package/dist/cliJsonInput.js +54 -0
  83. package/dist/cliPolicyCommands.js +2 -1
  84. package/dist/cliProcessCommands.js +3 -2
  85. package/dist/config.js +101 -2
  86. package/dist/contracts/applicationToolContracts.js +91 -0
  87. package/dist/contracts/artifactToolContracts.js +5 -9
  88. package/dist/contracts/browserToolContracts.js +14 -62
  89. package/dist/contracts/electronToolContracts.js +45 -19
  90. package/dist/contracts/enhancedInputs.js +2 -2
  91. package/dist/contracts/errorSchemas.js +1 -0
  92. package/dist/contracts/investigationExamples.js +0 -6
  93. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  94. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  95. package/dist/contracts/managedToolContracts.js +153 -0
  96. package/dist/contracts/managedWorkflowExamples.js +346 -0
  97. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  98. package/dist/contracts/nativeToolContracts.js +2 -6
  99. package/dist/contracts/promptContracts.js +18 -2
  100. package/dist/contracts/providerSelection.js +28 -0
  101. package/dist/contracts/sessionLifecycleInputs.js +2 -0
  102. package/dist/contracts/toolContractExamples.js +10 -25
  103. package/dist/contracts/toolContracts.js +69 -65
  104. package/dist/contracts/toolEffects.js +160 -0
  105. package/dist/contracts/toolOutputSchemas.js +152 -42
  106. package/dist/doctorRuntime.js +47 -0
  107. package/dist/domain/analysisProfile.js +78 -0
  108. package/dist/domain/analysisSnapshot.js +92 -46
  109. package/dist/domain/artifactComparison.js +3 -4
  110. package/dist/domain/artifactGraph.js +1 -0
  111. package/dist/domain/binaryTarget.js +4 -29
  112. package/dist/domain/browserObservation.js +3 -0
  113. package/dist/domain/bundleComparison.js +4 -4
  114. package/dist/domain/electronObservation.js +13 -0
  115. package/dist/domain/electronStaticAnalysis.js +9 -0
  116. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  117. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  118. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  119. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  120. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  121. package/dist/domain/errors.js +148 -7
  122. package/dist/domain/evidence.js +44 -5
  123. package/dist/domain/evidenceBundle.js +9 -7
  124. package/dist/domain/functionComparison.js +9 -3
  125. package/dist/domain/functionComparisonNormalization.js +37 -0
  126. package/dist/domain/functionComparisonSchemas.js +3 -8
  127. package/dist/domain/functionDossierEvidence.js +4 -1
  128. package/dist/domain/hopperValues.js +66 -9
  129. package/dist/domain/inputIssueProjection.js +52 -0
  130. package/dist/domain/investigationWorkspace.js +2 -1
  131. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  132. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  133. package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
  134. package/dist/domain/javascriptApplicationGraph.js +282 -0
  135. package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
  136. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  137. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  138. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  139. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  140. package/dist/domain/javascriptAstFingerprint.js +143 -0
  141. package/dist/domain/javascriptFeatureSeed.js +140 -0
  142. package/dist/domain/javascriptFeatureTrace.js +232 -0
  143. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  144. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  145. package/dist/domain/javascriptNativeHandoff.js +85 -0
  146. package/dist/domain/javascriptReplay.js +361 -0
  147. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  148. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  149. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  150. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  151. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  152. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  153. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  154. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  155. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +346 -0
  157. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  158. package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
  159. package/dist/domain/javascriptStaticAnalysisState.js +23 -0
  160. package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
  161. package/dist/domain/managedApplicationGraph.js +556 -0
  162. package/dist/domain/managedArtifact.js +444 -0
  163. package/dist/domain/managedMemberComparison.js +565 -0
  164. package/dist/domain/managedNativeVerification.js +408 -0
  165. package/dist/domain/managedReconstruction.js +183 -0
  166. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  167. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  168. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  169. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  170. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  172. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  173. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  174. package/dist/dotnet/ManagedPeReader.js +157 -0
  175. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  176. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  177. package/dist/generatedPackageMetadata.js +2 -2
  178. package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
  179. package/dist/ghidra/GhidraClient.js +400 -0
  180. package/dist/ghidra/GhidraClientTypes.js +1 -0
  181. package/dist/ghidra/GhidraDefaults.js +20 -0
  182. package/dist/ghidra/GhidraDiagnostics.js +43 -0
  183. package/dist/ghidra/GhidraDoctor.js +59 -0
  184. package/dist/ghidra/GhidraFunctionValues.js +264 -0
  185. package/dist/ghidra/GhidraInstallation.js +239 -0
  186. package/dist/ghidra/GhidraInventoryValues.js +217 -0
  187. package/dist/ghidra/GhidraLauncher.js +139 -0
  188. package/dist/ghidra/GhidraProvider.js +366 -0
  189. package/dist/ghidra/GhidraRequestQueue.js +98 -0
  190. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  191. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  192. package/dist/ghidra/GhidraSessionError.js +39 -0
  193. package/dist/ghidra/GhidraSessionValues.js +62 -0
  194. package/dist/ghidra/GhidraSocketConnection.js +46 -0
  195. package/dist/ghidra/protocol.js +69 -0
  196. package/dist/hopper/BridgeLauncher.js +19 -54
  197. package/dist/hopper/HopperAnalysisProfile.js +106 -0
  198. package/dist/hopper/HopperClient.js +154 -162
  199. package/dist/hopper/HopperProvider.js +99 -7
  200. package/dist/hopper/HopperSessionValues.js +27 -0
  201. package/dist/hopper/HopperSocketConnection.js +31 -0
  202. package/dist/main.js +14 -0
  203. package/dist/native/NativeMacOSProvider.js +3 -1
  204. package/dist/process/PendingOperations.js +59 -0
  205. package/dist/process/PrivateRuntimeRoot.js +33 -0
  206. package/dist/process/ProviderDeadline.js +95 -0
  207. package/dist/process/ProviderProcess.js +275 -0
  208. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  209. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  210. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  211. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  212. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  213. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  214. package/dist/server/createServer.js +53 -0
  215. package/dist/server/promptCompletion.js +9 -3
  216. package/dist/server/registerApplicationTools.js +115 -0
  217. package/dist/server/registerArtifactComparisonTool.js +16 -9
  218. package/dist/server/registerBrowserTools.js +33 -8
  219. package/dist/server/registerBundleComparisonTool.js +17 -20
  220. package/dist/server/registerElectronTools.js +45 -3
  221. package/dist/server/registerEnhancedTools.js +67 -19
  222. package/dist/server/registerEvidenceTools.js +5 -1
  223. package/dist/server/registerFunctionComparisonTool.js +14 -7
  224. package/dist/server/registerInvestigationTools.js +21 -17
  225. package/dist/server/registerManagedTools.js +6 -0
  226. package/dist/server/registerManagedWorkflowTools.js +277 -0
  227. package/dist/server/registerOfficialTools.js +11 -8
  228. package/dist/server/registerProcessComparisonTool.js +25 -47
  229. package/dist/server/registerSessionStatusTool.js +7 -7
  230. package/dist/server/registerSessionTools.js +57 -87
  231. package/dist/server/sessionEvidence.js +14 -19
  232. package/dist/server/toolInputValidation.js +77 -0
  233. package/dist/server/toolRegistrationOptions.js +79 -2
  234. package/dist/server/toolResult.js +25 -17
  235. package/package.json +17 -7
  236. package/scripts/rea.mjs +36 -2
  237. package/skills/rea-analysis/SKILL.md +59 -3
  238. /package/dist/{application → process}/ProcessOwnership.js +0 -0
@@ -0,0 +1,69 @@
1
+ import { z } from "zod";
2
+ import { jsonValueSchema } from "../domain/jsonValue.js";
3
+ import { err, ok } from "../domain/result.js";
4
+ const responseSchema = z
5
+ .object({
6
+ id: z.number().int().positive(),
7
+ ok: z.boolean(),
8
+ result: jsonValueSchema.optional(),
9
+ error: z
10
+ .object({
11
+ code: z.string().min(1).max(128),
12
+ message: z.string().min(1).max(4_096),
13
+ })
14
+ .strict()
15
+ .optional(),
16
+ })
17
+ .strict()
18
+ .superRefine((value, context) => {
19
+ if (value.ok && value.result === undefined)
20
+ context.addIssue({
21
+ code: "custom",
22
+ path: ["result"],
23
+ message: "Successful Ghidra response requires a result",
24
+ });
25
+ if (!value.ok && value.error === undefined)
26
+ context.addIssue({
27
+ code: "custom",
28
+ path: ["error"],
29
+ message: "Failed Ghidra response requires an error",
30
+ });
31
+ if (!value.ok && value.result !== undefined)
32
+ context.addIssue({
33
+ code: "custom",
34
+ path: ["result"],
35
+ message: "Failed Ghidra response cannot include a result",
36
+ });
37
+ if (value.ok && value.error !== undefined)
38
+ context.addIssue({
39
+ code: "custom",
40
+ path: ["error"],
41
+ message: "Successful Ghidra response cannot include an error",
42
+ });
43
+ });
44
+ /** Expected operation failure returned by the authenticated Java bridge. */
45
+ export class GhidraRemoteError extends Error {
46
+ code;
47
+ constructor(code, message) {
48
+ super(message);
49
+ this.code = code;
50
+ this.name = "GhidraRemoteError";
51
+ }
52
+ }
53
+ /** Parse one complete Java-bridge response without accepting extra fields. */
54
+ export const parseGhidraResponseLine = (line) => {
55
+ try {
56
+ const parsed = JSON.parse(line);
57
+ const response = responseSchema.safeParse(parsed);
58
+ return response.success
59
+ ? ok(response.data)
60
+ : err(new Error("Ghidra bridge response has an invalid shape"));
61
+ }
62
+ catch (cause) {
63
+ return err(new Error("Ghidra bridge response is not valid JSON", { cause }));
64
+ }
65
+ };
66
+ /** Project one validated response into its result or remote failure. */
67
+ export const ghidraResponseResult = (response) => response.ok
68
+ ? ok(response.result ?? null)
69
+ : err(new GhidraRemoteError(response.error?.code ?? "bridge_error", response.error?.message ?? "Ghidra bridge request failed"));
@@ -1,11 +1,12 @@
1
- import { spawn } from "node:child_process";
2
1
  import { execFile } from "node:child_process";
3
2
  import { chmod, writeFile } from "node:fs/promises";
4
3
  import { basename, dirname, extname, join } from "node:path";
5
4
  import { promisify } from "node:util";
6
5
  import { HopperCancelledError, HopperStartError } from "../domain/errors.js";
7
6
  import { err, ok } from "../domain/result.js";
8
- import { cleanupOwnedProcessGroup, } from "../application/ProcessOwnership.js";
7
+ import { cleanupOwnedProcessGroup } from "../process/ProcessOwnership.js";
8
+ import { spawnOwnedProviderProcess, } from "../process/ProviderProcess.js";
9
+ import { waitForAbortableDelay } from "../process/ProviderDeadline.js";
9
10
  import writeFileAtomic from "write-file-atomic";
10
11
  const execFileAsync = promisify(execFile);
11
12
  const HOPPER_BACKGROUND_STARTUP_MS = 5_000;
@@ -64,13 +65,14 @@ export class HopperApplicationLauncher {
64
65
  return err(new HopperCancelledError());
65
66
  const linuxDemo = linuxDemoLaunch(this.options, session, args);
66
67
  const ownershipCommand = linuxDemo?.ownershipCommand ?? this.options.launcherPath;
67
- const started = await spawnLauncher(linuxDemo?.command ?? this.options.launcherPath, linuxDemo?.args ?? args, session.runId);
68
- if (!started.ok)
69
- return started;
70
- const child = started.value;
71
- const pid = child.pid;
72
- if (pid === undefined)
73
- return err(new HopperStartError({ cause: new Error("missing launcher PID") }));
68
+ const started = await spawnOwnedProviderProcess({
69
+ command: linuxDemo?.command ?? this.options.launcherPath,
70
+ arguments: linuxDemo?.args ?? args,
71
+ runId: session.runId,
72
+ expectedCommand: ownershipCommand,
73
+ });
74
+ const child = started.process;
75
+ const pid = started.ownership.leaderPid;
74
76
  const ownership = {
75
77
  schema_version: 1,
76
78
  run_id: session.runId,
@@ -84,18 +86,18 @@ export class HopperApplicationLauncher {
84
86
  await writeFileAtomic(join(session.directory, "ownership.json"), `${JSON.stringify(ownership)}\n`, { encoding: "utf8", mode: 0o600 });
85
87
  }
86
88
  catch (cause) {
87
- await cleanupOwnedProcessGroup(ownedProcessGroup(session, pid, ownershipCommand));
89
+ await cleanupOwnedProcessGroup(started.ownership);
88
90
  return err(new HopperStartError({ cause }));
89
91
  }
90
92
  return ok({
91
93
  process: child,
92
94
  ownsProcessLifetime: true,
93
95
  shutdownByCleanup: ownsProcessLifetime,
94
- cleanup: () => cleanupOwnedProcessGroup(ownedProcessGroup(session, pid, ownershipCommand)),
96
+ cleanup: () => cleanupOwnedProcessGroup(started.ownership),
95
97
  });
96
98
  }
97
99
  catch (cause) {
98
- return err(new HopperStartError({ cause }));
100
+ return err(hopperLaunchFailure(cause, options.signal));
99
101
  }
100
102
  }
101
103
  }
@@ -117,34 +119,11 @@ const linuxDemoLaunch = (options, session, hopperArgs) => {
117
119
  ],
118
120
  };
119
121
  };
122
+ const hopperLaunchFailure = (cause, signal) => signal?.aborted === true
123
+ ? new HopperCancelledError()
124
+ : new HopperStartError({ cause });
120
125
  /** Select the version-pinned adapter from the caller's explicit launch mode. */
121
126
  export const usesLinuxDemo = (options) => options.launchMode === "verified_linux_demo";
122
- const spawnLauncher = async (command, args, runId) => {
123
- const child = spawn(command, args, {
124
- stdio: ["ignore", "ignore", "pipe"],
125
- detached: process.platform !== "win32",
126
- env: { ...process.env, REA_PROCESS_RUN_ID: runId },
127
- });
128
- return new Promise((resolve) => {
129
- const onSpawn = () => {
130
- child.off("error", onError);
131
- resolve(ok(child));
132
- };
133
- const onError = (cause) => {
134
- child.off("spawn", onSpawn);
135
- resolve(err(new HopperStartError({ cause })));
136
- };
137
- child.once("spawn", onSpawn);
138
- child.once("error", onError);
139
- });
140
- };
141
- const ownedProcessGroup = (session, pid, launcherPath) => ({
142
- runId: session.runId,
143
- leaderPid: pid,
144
- processGroupId: pid,
145
- expectedCommand: launcherPath,
146
- expectedParentPid: process.pid,
147
- });
148
127
  const prepareHopperApplication = async (launcherPath, signal) => {
149
128
  const appBundle = hopperApplicationBundle(launcherPath);
150
129
  if (appBundle === undefined)
@@ -160,22 +139,8 @@ const prepareHopperApplication = async (launcherPath, signal) => {
160
139
  "-a",
161
140
  appBundle,
162
141
  ]);
163
- return waitForDelay(HOPPER_BACKGROUND_STARTUP_MS, signal);
164
- };
165
- const waitForDelay = (milliseconds, signal) => {
166
- if (signal?.aborted === true)
167
- return Promise.resolve(false);
168
- return new Promise((resolve) => {
169
- const timer = setTimeout(() => {
170
- signal?.removeEventListener("abort", onAbort);
171
- resolve(true);
172
- }, milliseconds);
173
- const onAbort = () => {
174
- clearTimeout(timer);
175
- resolve(false);
176
- };
177
- signal?.addEventListener("abort", onAbort, { once: true });
178
- });
142
+ return ((await waitForAbortableDelay(HOPPER_BACKGROUND_STARTUP_MS, signal)) ===
143
+ "elapsed");
179
144
  };
180
145
  const hopperApplicationBundle = (launcherPath) => {
181
146
  if (basename(launcherPath) !== "hopper")
@@ -0,0 +1,106 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { createAnalysisProfile } from "../domain/analysisProfile.js";
4
+ import { AnalysisCancelledError, ProviderAdapterError, } from "../domain/errors.js";
5
+ import { err, ok } from "../domain/result.js";
6
+ /** Resolve Hopper open semantics without placing its CLI flags in BinaryTarget. */
7
+ export const resolveHopperAnalysisProfile = async (target, options) => {
8
+ if (target.kind !== "executable" && target.kind !== "database")
9
+ return ok({ profile: null, compatibility: {} });
10
+ const derived = hopperLoaderArgsForTarget(target);
11
+ if (!derived.ok)
12
+ return derived;
13
+ const loaderArgs = options.loaderArgsOverride.length === 0
14
+ ? derived.value
15
+ : [...options.loaderArgsOverride];
16
+ const compatibility = { loaderArgs: [...loaderArgs] };
17
+ const launcherDigest = await sha256File(options.launcherPath, options.signal);
18
+ if (!launcherDigest.ok)
19
+ return launcherDigest;
20
+ if (launcherDigest.value === undefined)
21
+ return ok({ profile: null, compatibility });
22
+ const provider = {
23
+ id: options.provider.id,
24
+ name: options.provider.name,
25
+ version: `launcher-sha256:${launcherDigest.value}`,
26
+ };
27
+ return ok({
28
+ profile: createAnalysisProfile(provider, 1, {
29
+ target_kind: target.kind,
30
+ target_format: target.format,
31
+ architecture: target.architecture ?? null,
32
+ available_architectures: [
33
+ ...(target.availableArchitectures ?? []),
34
+ ].sort(),
35
+ loader: {
36
+ source: options.loaderArgsOverride.length === 0
37
+ ? "derived"
38
+ : "configured_override",
39
+ argument_count: loaderArgs.length,
40
+ arguments_sha256: createHash("sha256")
41
+ .update(JSON.stringify(loaderArgs))
42
+ .digest("hex"),
43
+ },
44
+ }),
45
+ compatibility,
46
+ });
47
+ };
48
+ /** Derive Hopper's complete non-interactive CLI loader selection. */
49
+ export const hopperLoaderArgsForTarget = (target) => {
50
+ if (target.kind === "database")
51
+ return ok([]);
52
+ const architecture = target.architecture;
53
+ if (architecture === undefined)
54
+ return err(new ProviderAdapterError("hopper", "resolve_analysis_profile"));
55
+ const flag = hopperArchitectureFlag(architecture);
56
+ switch (target.format) {
57
+ case "mach-o":
58
+ return ok((target.availableArchitectures?.length ?? 0) > 1
59
+ ? ["-l", "FAT", flag, "-l", "Mach-O"]
60
+ : ["-l", "Mach-O", flag]);
61
+ case "elf":
62
+ return ok(["-l", "ELF", flag]);
63
+ case "pe":
64
+ return ok(["-l", "WinPE", flag]);
65
+ default:
66
+ return err(new ProviderAdapterError("hopper", "resolve_analysis_profile"));
67
+ }
68
+ };
69
+ const hopperArchitectureFlag = (architecture) => {
70
+ switch (architecture) {
71
+ case "x86":
72
+ return "--intel-32";
73
+ case "x86_64":
74
+ return "--intel-64";
75
+ case "arm":
76
+ return "--armv7";
77
+ case "arm64":
78
+ return "--aarch64";
79
+ }
80
+ };
81
+ const sha256File = async (path, signal) => {
82
+ if (signalIsAborted(signal))
83
+ return err(new AnalysisCancelledError("open_binary"));
84
+ const stream = createReadStream(path);
85
+ const onAbort = () => {
86
+ stream.destroy();
87
+ };
88
+ signal?.addEventListener("abort", onAbort, { once: true });
89
+ try {
90
+ const hash = createHash("sha256");
91
+ for await (const chunk of stream)
92
+ hash.update(chunk);
93
+ return signalIsAborted(signal)
94
+ ? err(new AnalysisCancelledError("open_binary"))
95
+ : ok(hash.digest("hex"));
96
+ }
97
+ catch {
98
+ return signalIsAborted(signal)
99
+ ? err(new AnalysisCancelledError("open_binary"))
100
+ : ok(undefined);
101
+ }
102
+ finally {
103
+ signal?.removeEventListener("abort", onAbort);
104
+ }
105
+ };
106
+ const signalIsAborted = (signal) => signal?.aborted === true;