rea-agents 2.0.0 → 2.2.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 (273) hide show
  1. package/README.md +103 -38
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/AndroidApplicationService.js +17 -0
  4. package/dist/application/AppleApplicationService.js +17 -0
  5. package/dist/application/ArtifactExtraction.js +1 -1
  6. package/dist/application/ArtifactInventory/classify.js +42 -0
  7. package/dist/application/ArtifactInventory/hash.js +43 -0
  8. package/dist/application/ArtifactInventory/reader.js +41 -0
  9. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  10. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  11. package/dist/application/ArtifactInventory/types.js +10 -0
  12. package/dist/application/ArtifactInventory.js +9 -351
  13. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  14. package/dist/application/BinarySession.js +47 -270
  15. package/dist/application/BinarySessionExecution.js +28 -0
  16. package/dist/application/BinarySessionOpen.js +39 -0
  17. package/dist/application/BinarySessionRecords.js +149 -0
  18. package/dist/application/BinarySessionStatus.js +75 -0
  19. package/dist/application/CapabilityInventory.js +24 -2
  20. package/dist/application/ClientRegistrationStatus.js +11 -4
  21. package/dist/application/CrossVersionInventory.js +8 -2
  22. package/dist/application/CrossVersionInvestigation.js +1 -23
  23. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  24. package/dist/application/DirectAnalysis.js +47 -44
  25. package/dist/application/Doctor.js +156 -238
  26. package/dist/application/DoctorDiagnostics.js +180 -0
  27. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  28. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  29. package/dist/application/EnhancedToolPagination.js +22 -0
  30. package/dist/application/EnhancedToolTypes.js +4 -0
  31. package/dist/application/EnhancedTools.js +20 -37
  32. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  33. package/dist/application/InventoryProjectionEvidence.js +41 -0
  34. package/dist/application/InvestigationProviders.js +12 -0
  35. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  36. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  37. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  38. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  39. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  40. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  41. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  42. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  43. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  44. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  45. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  46. package/dist/application/JavaScriptJsonModules.js +39 -0
  47. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  48. package/dist/application/JavaScriptReplayPermission.js +16 -0
  49. package/dist/application/JavaScriptReplayService.js +182 -156
  50. package/dist/application/LinuxHopper.js +10 -0
  51. package/dist/application/MacHopper.js +22 -11
  52. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  53. package/dist/application/PermissionAuthority.js +106 -63
  54. package/dist/application/ProcessCaptureCapability.js +1 -1
  55. package/dist/application/ProcessCapturePermission.js +13 -0
  56. package/dist/application/ProcessCli.js +2 -12
  57. package/dist/application/ProcessHarness.js +1 -1
  58. package/dist/application/ProcessPairedExperiment.js +72 -0
  59. package/dist/application/ProjectPermissionStore.js +48 -9
  60. package/dist/application/RealHopperAssertions.js +76 -0
  61. package/dist/application/ReconstructionCoverageService.js +69 -0
  62. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  63. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  64. package/dist/application/Setup.js +148 -260
  65. package/dist/application/SetupClientConfiguration.js +224 -0
  66. package/dist/application/SetupClients.js +24 -2
  67. package/dist/application/SetupPlan.js +124 -12
  68. package/dist/application/SetupSkill.js +54 -36
  69. package/dist/application/SupportedClients.js +1 -0
  70. package/dist/application/Uninstall.js +12 -3
  71. package/dist/application/Upgrade.js +16 -1
  72. package/dist/application/WindowsCapabilities.js +86 -0
  73. package/dist/artifacts/AsarArtifactReader.js +11 -0
  74. package/dist/browser/BrowserProcessStartup.js +88 -0
  75. package/dist/browser/CdpCaptureDocuments.js +12 -8
  76. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  77. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  78. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  79. package/dist/browser/CdpCaptureEventState.js +1 -0
  80. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  81. package/dist/browser/CdpCaptureEvents.js +29 -496
  82. package/dist/browser/CdpObservationSession.js +51 -8
  83. package/dist/browser/CdpPageCapture.js +12 -133
  84. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  85. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  86. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  87. package/dist/browser/PngVisualDiff.js +9 -2
  88. package/dist/catalogIdentity.js +4 -0
  89. package/dist/cli/artifactCommands.js +82 -0
  90. package/dist/cli/coreAnalysisCommands.js +232 -0
  91. package/dist/cli/managedCommands.js +397 -0
  92. package/dist/cli/managedProjectGraphCommand.js +24 -0
  93. package/dist/cli/options.js +9 -0
  94. package/dist/cli/setupCommands.js +113 -0
  95. package/dist/cli/types.js +1 -0
  96. package/dist/cli/utilityCommands.js +122 -0
  97. package/dist/cli.js +10 -768
  98. package/dist/cliApplicationCommands.js +118 -4
  99. package/dist/cliBrowserAdvancedCommands.js +73 -16
  100. package/dist/cliBrowserCommands.js +39 -115
  101. package/dist/cliCommandNames.js +4 -0
  102. package/dist/cliElectronCommands.js +33 -62
  103. package/dist/cliEvidenceCommands.js +13 -2
  104. package/dist/cliInvestigationCommands.js +53 -10
  105. package/dist/cliObservationOptions.js +137 -0
  106. package/dist/cliPolicyCommands.js +80 -46
  107. package/dist/cliSetup.js +306 -0
  108. package/dist/config/environment.js +107 -0
  109. package/dist/config/parseConfig.js +228 -0
  110. package/dist/config/parsers.js +68 -0
  111. package/dist/config/permissions.js +30 -0
  112. package/dist/config/types.js +1 -0
  113. package/dist/config.js +1 -375
  114. package/dist/contracts/applicationToolContracts.js +134 -0
  115. package/dist/contracts/electronToolContracts.js +51 -3
  116. package/dist/contracts/managedToolContracts.js +8 -3
  117. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  118. package/dist/contracts/providerSelection.js +2 -0
  119. package/dist/contracts/sessionStatusContract.js +26 -0
  120. package/dist/contracts/toolContractTypes.js +12 -1
  121. package/dist/contracts/toolContracts.js +5 -12
  122. package/dist/contracts/toolEffects.js +14 -0
  123. package/dist/contracts/toolOutputSchemaGroups.js +274 -0
  124. package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
  125. package/dist/contracts/toolOutputSchemas.js +2 -550
  126. package/dist/domain/analysisErrorPresentation.js +234 -0
  127. package/dist/domain/analysisErrorProjection.js +284 -0
  128. package/dist/domain/androidApplication.js +206 -0
  129. package/dist/domain/appleApplication.js +221 -0
  130. package/dist/domain/artifactComparison.js +34 -15
  131. package/dist/domain/binaryTarget.js +87 -37
  132. package/dist/domain/boundedCartesianProjection.js +15 -0
  133. package/dist/domain/browserObservation.js +1 -285
  134. package/dist/domain/browserObservationSchemas.js +286 -0
  135. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  136. package/dist/domain/errors.js +4 -486
  137. package/dist/domain/functionComparison.js +4 -165
  138. package/dist/domain/functionComparisonDimensions.js +164 -0
  139. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  140. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  141. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  142. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  143. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  145. package/dist/domain/javascriptSemanticIr.js +30 -0
  146. package/dist/domain/javascriptSemanticProjection.js +366 -0
  147. package/dist/domain/javascriptSemanticState.js +40 -0
  148. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  149. package/dist/domain/javascriptSemanticValues.js +352 -0
  150. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  151. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  152. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  153. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  154. package/dist/domain/knownPageCoverage.js +21 -0
  155. package/dist/domain/managedApplicationGraph.js +14 -364
  156. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  157. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  158. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  159. package/dist/domain/managedArtifact.js +16 -8
  160. package/dist/domain/managedMemberComparison.js +37 -391
  161. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  162. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  163. package/dist/domain/managedNativeVerification.js +27 -389
  164. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  165. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  166. package/dist/domain/managedReconstruction.js +82 -69
  167. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  168. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  169. package/dist/domain/permissionPolicy.js +2 -0
  170. package/dist/domain/processComparison.js +19 -0
  171. package/dist/domain/processPairedExperiment.js +128 -0
  172. package/dist/domain/reconstructionCoverage.js +387 -0
  173. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  174. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  175. package/dist/domain/replayMachine.js +316 -0
  176. package/dist/domain/replayMachineRuntime.js +215 -0
  177. package/dist/domain/runtimeCharacterization.js +85 -0
  178. package/dist/domain/runtimeIdentification.js +210 -0
  179. package/dist/domain/webBundleAnalyzer.js +3 -409
  180. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  181. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  182. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  183. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  184. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  185. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  186. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  187. package/dist/dotnet/ManagedMemberRows.js +120 -0
  188. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  189. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  190. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  191. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  192. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  193. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  194. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  195. package/dist/dotnet/ManagedPeReader.js +59 -28
  196. package/dist/evaluation/CodexAgentEval.js +132 -0
  197. package/dist/generatedPackageMetadata.js +2 -2
  198. package/dist/ghidra/GhidraClient.js +76 -91
  199. package/dist/ghidra/GhidraClientStartup.js +29 -0
  200. package/dist/ghidra/GhidraClientWire.js +34 -0
  201. package/dist/ghidra/GhidraDefaults.js +1 -1
  202. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  203. package/dist/ghidra/GhidraInstallation.js +20 -15
  204. package/dist/ghidra/GhidraLauncher.js +93 -29
  205. package/dist/ghidra/GhidraProvider.js +61 -161
  206. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  207. package/dist/ghidra/GhidraSessionValues.js +2 -0
  208. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  209. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  210. package/dist/ghidra/GhidraTransport.js +71 -0
  211. package/dist/identity.js +4 -2
  212. package/dist/main/messages.js +5 -0
  213. package/dist/main/reload.js +49 -0
  214. package/dist/main/shutdown.js +25 -0
  215. package/dist/main/startup.js +14 -0
  216. package/dist/main/state.js +26 -0
  217. package/dist/main/transport.js +61 -0
  218. package/dist/main/types.js +1 -0
  219. package/dist/main.js +35 -146
  220. package/dist/process/PrivateRuntimeRoot.js +3 -2
  221. package/dist/process/ProcessOwnership.js +35 -0
  222. package/dist/process/ProviderProcess.js +2 -0
  223. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  224. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  225. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  226. package/dist/replay/ReplayOutcome.js +95 -0
  227. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  228. package/dist/server/ProcessCaptureElicitation.js +157 -0
  229. package/dist/server/createServer.js +124 -56
  230. package/dist/server/javascriptApplicationResult.js +85 -0
  231. package/dist/server/registerApplicationTools/characterization.js +57 -0
  232. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  233. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  234. package/dist/server/registerApplicationTools/coverage.js +84 -0
  235. package/dist/server/registerApplicationTools/helpers.js +42 -0
  236. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  237. package/dist/server/registerApplicationTools/types.js +1 -0
  238. package/dist/server/registerApplicationTools.js +10 -112
  239. package/dist/server/registerBrowserTools.js +59 -83
  240. package/dist/server/registerElectronTools.js +48 -49
  241. package/dist/server/registerEnhancedTools.js +34 -42
  242. package/dist/server/registerEvidenceResources.js +56 -0
  243. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  244. package/dist/server/registerInvestigationTools/types.js +1 -0
  245. package/dist/server/registerInvestigationTools.js +83 -116
  246. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  247. package/dist/server/registerManagedTools.js +18 -1
  248. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  249. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  250. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  251. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  252. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  253. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  254. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  255. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  256. package/dist/server/registerManagedWorkflowTools.js +10 -274
  257. package/dist/server/registerOfficialTools.js +73 -59
  258. package/dist/server/registerSessionRecordTools.js +137 -0
  259. package/dist/server/registerSessionStatusTool.js +97 -26
  260. package/dist/server/registerSessionTools.js +60 -164
  261. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  262. package/dist/server/toolRegistrationOptions.js +101 -3
  263. package/dist/server/toolResult.js +15 -4
  264. package/package.json +11 -11
  265. package/skills/reverse-engineer-anything/SKILL.md +76 -0
  266. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  267. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  268. package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
  269. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  270. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
  271. package/scripts/prepare-node-pty.mjs +0 -35
  272. package/scripts/rebuild-native.mjs +0 -22
  273. package/skills/rea-analysis/SKILL.md +0 -222
@@ -1,85 +1,70 @@
1
- import { access, copyFile, mkdir, readFile, rm, writeFile, } from "node:fs/promises";
2
- import { constants as fsConstants } from "node:fs";
1
+ import { access } from "node:fs/promises";
3
2
  import { homedir } from "node:os";
4
- import { dirname, resolve } from "node:path";
5
- import { z } from "zod";
6
- import writeFileAtomic from "write-file-atomic";
7
- import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
3
+ import { resolve } from "node:path";
8
4
  import { PRODUCT_IDENTITY } from "../identity.js";
9
5
  import { supportsNodeVersion } from "../domain/runtimeVersion.js";
10
- import { resolveClientConfigTransactionPath } from "./ClientConfigPath.js";
11
6
  import { runDoctor, systemDoctorHost, } from "./Doctor.js";
12
7
  import { installLinuxHopper, readLinuxDistribution, } from "./LinuxHopper.js";
13
8
  import { installMacHopper } from "./MacHopper.js";
14
9
  import { configureDetectedClients } from "./SetupClients.js";
15
10
  import { supportedClients } from "./SupportedClients.js";
16
11
  import { canonicalSkillNeedsInstall, installCanonicalSkill, } from "./SetupSkill.js";
17
- import { setupPlan } from "./SetupPlan.js";
12
+ import { discoverSetupActions } from "./SetupPlan.js";
13
+ import { clientConfigurationAligned, configureJsonClient, configureTomlClient, inspectClientConfiguration, } from "./SetupClientConfiguration.js";
14
+ export { configureJsonClient, configureTomlClient };
18
15
  import { setupInstallFailure, } from "./SetupInstallFailure.js";
19
- export { canonicalSkillNeedsInstall, installCanonicalSkill, } from "./SetupSkill.js";
16
+ export { canonicalSkillNeedsInstall, installCanonicalSkill };
20
17
  const registrationCommand = () => process.env.npm_command === "exec"
21
- ? ["npx", "-y", PRODUCT_IDENTITY.packageName, "mcp"]
18
+ ? PRODUCT_IDENTITY.mcpCommand.split(" ")
22
19
  : [resolve(process.argv[1] ?? PRODUCT_IDENTITY.cliBinary), "mcp"];
23
- /**
24
- * Install prerequisites and configure detected clients idempotently.
25
- * Discovery always precedes mutation. Interactive confirmation or explicit
26
- * unattended options authorize only the actions represented in the result.
27
- */
20
+ /** Discover, approve, and apply setup actions idempotently. */
28
21
  export const runSetup = async (options, host = systemSetupHost(), confirm) => {
29
22
  const appliedActions = [];
30
23
  let plannedActions = [];
31
24
  const clients = {};
32
- const fail = async (remediation) => ({
33
- status: "needs_human",
34
- plannedActions,
35
- appliedActions,
36
- clients,
37
- doctor: await host.doctor(),
38
- remediation,
39
- });
25
+ const fail = (remediation) => setupFailure(remediation, [host, plannedActions, appliedActions, clients]);
40
26
  const unsupported = await hostRemediation(host, options.installHopper);
41
27
  if (unsupported !== undefined)
42
28
  return fail(unsupported);
43
29
  let hopperPath = await host.hopperPath();
44
30
  let providerEnvironment = await initialProviderEnvironment(host, hopperPath);
45
- const discovery = await discoverSetupActions(host, providerEnvironment, options.installHopper);
46
- const { installHopper, installSkill } = discovery;
47
- let { detectedClients } = discovery;
48
- plannedActions = discovery.plannedActions;
49
- let approved = options.approved || plannedActions.length === 0;
50
- let interactiveApproval = false;
51
- if (!approved && confirm !== undefined && !options.structured) {
52
- interactiveApproval = await confirm(plannedActions);
53
- approved = interactiveApproval;
54
- }
55
- if (!approved)
31
+ const discovery = await discoverSetupActions({
32
+ host,
33
+ providerEnvironment,
34
+ command: registrationCommand(),
35
+ forceHopperInstall: options.installHopper,
36
+ proposeHopper: options.proposeHopper ?? true,
37
+ selectedClientIds: options.clientIds,
38
+ installSkillSelection: options.installSkill,
39
+ });
40
+ if (discovery.blocker !== undefined)
41
+ return fail(discovery.blocker);
42
+ const selection = await resolveSetupSelection(options, discovery, confirm);
43
+ plannedActions = selection.plannedActions;
44
+ if (!selection.approved)
56
45
  return {
57
46
  status: confirm === undefined ? "needs_confirmation" : "planned",
58
47
  plannedActions,
59
48
  appliedActions,
60
49
  clients,
61
- doctor: await host.doctor(),
50
+ doctor: discovery.initialDoctor,
62
51
  remediation: "Review the setup plan, then rerun interactively or with --yes.",
63
52
  };
64
- if (installHopper && (interactiveApproval || options.installHopper)) {
65
- const installed = await host.installHopper(options.installHopper);
66
- if (installed.status === "failed")
67
- return {
68
- status: "needs_human",
69
- plannedActions,
70
- appliedActions,
71
- clients,
72
- doctor: await host.doctor(),
73
- code: installed.code,
74
- remediation: installed.remediation,
75
- };
76
- hopperPath = installed.launcherPath;
77
- providerEnvironment = {
78
- ...providerEnvironment,
79
- HOPPER_LAUNCHER_PATH: installed.launcherPath,
80
- };
81
- appliedActions.push("installed_hopper");
82
- detectedClients = await filterClientsNeedingConfigure(host, detectedClients, providerEnvironment, registrationCommand());
53
+ let detectedClients = selection.detectedClients;
54
+ if (selection.installHopper &&
55
+ (selection.interactiveApproval || options.installHopper)) {
56
+ const install = await installHopperAction({
57
+ host,
58
+ options,
59
+ plannedActions,
60
+ appliedActions,
61
+ clients,
62
+ detectedClients,
63
+ providerEnvironment,
64
+ });
65
+ if ("failure" in install)
66
+ return install.failure;
67
+ ({ hopperPath, providerEnvironment, detectedClients } = install);
83
68
  }
84
69
  const clientFailure = await configureDetectedClients({
85
70
  host,
@@ -88,16 +73,15 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
88
73
  command: registrationCommand(),
89
74
  clients,
90
75
  appliedActions,
76
+ ...(options.onProgress === undefined
77
+ ? {}
78
+ : { onProgress: options.onProgress }),
91
79
  });
92
80
  if (clientFailure !== undefined)
93
81
  return fail(clientFailure);
94
- if (installSkill) {
95
- const skill = await host.installSkill();
96
- if (skill === "failed")
97
- return fail("REA analysis skill could not be installed or verified. Check permissions for `~/.agents/skills`, then rerun setup.");
98
- if (skill === "installed")
99
- appliedActions.push("installed_skill");
100
- }
82
+ if (selection.installSkill &&
83
+ !(await installSkillAction(host, options, appliedActions)))
84
+ return fail("REA analysis skill could not be installed or verified. Check permissions for `~/.agents/skills`, then rerun setup.");
101
85
  const doctor = await host.doctor();
102
86
  const remediation = finalSetupRemediation(host.platform, appliedActions.includes("installed_hopper"), doctor.healthy, hopperPath);
103
87
  return {
@@ -109,30 +93,114 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
109
93
  ...(remediation === undefined ? {} : { remediation }),
110
94
  };
111
95
  };
112
- const discoverSetupActions = async (host, providerEnvironment, forceHopperInstall) => {
113
- const initialDoctor = await host.doctor();
114
- const linuxHopperRepairNeeded = initialDoctor.checks.some(({ name, ok, detail }) => !ok &&
115
- (name === "hopper-demo-runtime" ||
116
- (name === "hopper-version" && detail === "/opt/hopper/bin/Hopper")));
117
- const installHopper = forceHopperInstall ||
118
- Object.keys(providerEnvironment).length === 0 ||
119
- linuxHopperRepairNeeded;
120
- const [detectedClients, installSkill] = await Promise.all([
121
- host.detectedClients(),
122
- host.skillNeedsInstall(),
123
- ]);
124
- const command = registrationCommand();
125
- const clients = installHopper
126
- ? detectedClients
127
- : await filterClientsNeedingConfigure(host, detectedClients, providerEnvironment, command);
128
- const plannedActions = setupPlan(host.platform, installHopper, installSkill, clients, providerEnvironment);
96
+ const resolveSetupSelection = async (options, discovery, confirm) => {
97
+ let { installHopper, installSkill, detectedClients, plannedActions } = discovery;
98
+ let approved = options.approved || plannedActions.length === 0;
99
+ let interactiveApproval = false;
100
+ if (!approved && confirm !== undefined && !options.structured) {
101
+ const decision = await confirm(plannedActions);
102
+ if (typeof decision === "boolean") {
103
+ interactiveApproval = decision;
104
+ approved = decision;
105
+ }
106
+ else {
107
+ const selected = new Set(decision.selectedActionIds);
108
+ plannedActions = plannedActions.filter(({ id }) => selected.has(id));
109
+ installHopper = plannedActions.some(({ kind }) => kind === "install_hopper");
110
+ installSkill = plannedActions.some(({ kind }) => kind === "install_skill");
111
+ detectedClients = detectedClients.filter((client) => selected.has(`configure_client:${client.name}`));
112
+ interactiveApproval = decision.approved;
113
+ approved = decision.approved;
114
+ }
115
+ }
129
116
  return {
117
+ approved,
118
+ interactiveApproval,
130
119
  installHopper,
131
120
  installSkill,
132
- detectedClients: clients,
121
+ detectedClients,
133
122
  plannedActions,
134
123
  };
135
124
  };
125
+ const installHopperAction = async (input) => {
126
+ const label = "Hopper deep-analysis provider";
127
+ emitProgress(input.options, {
128
+ actionId: "install_hopper",
129
+ label,
130
+ state: "started",
131
+ });
132
+ const installed = await input.host.installHopper(input.options.installHopper);
133
+ if (installed.status === "failed") {
134
+ emitProgress(input.options, {
135
+ actionId: "install_hopper",
136
+ label,
137
+ state: "failed",
138
+ detail: installed.remediation,
139
+ });
140
+ return {
141
+ failure: {
142
+ status: "needs_human",
143
+ plannedActions: input.plannedActions,
144
+ appliedActions: input.appliedActions,
145
+ clients: input.clients,
146
+ doctor: await input.host.doctor(),
147
+ code: installed.code,
148
+ remediation: installed.remediation,
149
+ },
150
+ };
151
+ }
152
+ input.appliedActions.push("installed_hopper");
153
+ emitProgress(input.options, {
154
+ actionId: "install_hopper",
155
+ label,
156
+ state: "completed",
157
+ detail: installed.launcherPath,
158
+ });
159
+ const providerEnvironment = {
160
+ ...input.providerEnvironment,
161
+ HOPPER_LAUNCHER_PATH: installed.launcherPath,
162
+ };
163
+ return {
164
+ hopperPath: installed.launcherPath,
165
+ providerEnvironment,
166
+ detectedClients: await filterClientsNeedingConfigure(input.host, input.detectedClients, providerEnvironment, registrationCommand()),
167
+ };
168
+ };
169
+ const installSkillAction = async (host, options, appliedActions) => {
170
+ const label = "REA reverse-engineering skill";
171
+ emitProgress(options, {
172
+ actionId: "install_skill",
173
+ label,
174
+ state: "started",
175
+ });
176
+ const skill = await host.installSkill();
177
+ if (skill === "failed") {
178
+ emitProgress(options, {
179
+ actionId: "install_skill",
180
+ label,
181
+ state: "failed",
182
+ });
183
+ return false;
184
+ }
185
+ if (skill === "installed")
186
+ appliedActions.push("installed_skill");
187
+ emitProgress(options, {
188
+ actionId: "install_skill",
189
+ label,
190
+ state: skill === "installed" ? "completed" : "warning",
191
+ ...(skill === "unchanged" ? { detail: "Already current" } : {}),
192
+ });
193
+ return true;
194
+ };
195
+ const setupFailure = async (remediation, [host, plannedActions, appliedActions, clients]) => ({
196
+ status: "needs_human",
197
+ plannedActions,
198
+ appliedActions,
199
+ clients,
200
+ doctor: await host.doctor(),
201
+ remediation,
202
+ });
203
+ const emitProgress = (options, event) => options.onProgress?.(event);
136
204
  const filterClientsNeedingConfigure = async (host, detectedClients, providerEnvironment, command) => {
137
205
  const needs = await Promise.all(detectedClients.map((client) => host.clientNeedsConfigure(client, providerEnvironment, command)));
138
206
  return detectedClients.filter((_, index) => needs[index]);
@@ -202,6 +270,7 @@ export const systemSetupHost = (doctorHost = systemDoctorHost()) => {
202
270
  ? configureTomlClient(client, providerEnvironment, command)
203
271
  : configureJsonClient(client, providerEnvironment, command),
204
272
  clientNeedsConfigure: (client, providerEnvironment, command) => clientConfigurationAligned(client, providerEnvironment, command).then((aligned) => !aligned),
273
+ inspectClientConfiguration: inspectClientConfiguration,
205
274
  skillNeedsInstall: () => canonicalSkillNeedsInstall(homedir()),
206
275
  installSkill: () => installCanonicalSkill(homedir()),
207
276
  doctor: () => runDoctor(undefined, doctorHost),
@@ -215,133 +284,6 @@ export const detectClients = async (home) => {
215
284
  detected.push(candidate);
216
285
  return detected;
217
286
  };
218
- /** Back up, atomically update, and semantically read back one JSON MCP configuration. */
219
- export const configureJsonClient = async (client, environment = {}, command = [
220
- "npx",
221
- "-y",
222
- PRODUCT_IDENTITY.packageName,
223
- "mcp",
224
- ]) => {
225
- const transactionPath = await resolveClientConfigTransactionPath(client.configPath);
226
- if (transactionPath === undefined)
227
- return { status: "failed", reason: "path" };
228
- let document = {};
229
- let original;
230
- try {
231
- original = await readFile(transactionPath, "utf8");
232
- document = parseObject(original);
233
- }
234
- catch (cause) {
235
- if (!isMissing(cause))
236
- return { status: "failed", reason: "readback" };
237
- }
238
- let servers;
239
- try {
240
- servers = parseOptionalObject(document.mcpServers);
241
- }
242
- catch {
243
- return { status: "failed", reason: "readback" };
244
- }
245
- const desired = clientConfigurationDesired(normalizeProviderEnvironment(environment), command);
246
- if (JSON.stringify(servers[PRODUCT_IDENTITY.mcpServerKey]) ===
247
- JSON.stringify(desired))
248
- return { status: "unchanged" };
249
- let backupPath;
250
- if (original !== undefined) {
251
- backupPath = `${client.configPath}.rea.backup`;
252
- if (!(await preserveConfigBackup(transactionPath, backupPath)))
253
- return { status: "failed", reason: "backup" };
254
- }
255
- document.mcpServers = {
256
- ...servers,
257
- [PRODUCT_IDENTITY.mcpServerKey]: desired,
258
- };
259
- const encoded = `${JSON.stringify(document, null, 2)}\n`;
260
- try {
261
- await mkdir(dirname(client.configPath), { recursive: true });
262
- await writeFileAtomic(transactionPath, encoded, {
263
- encoding: "utf8",
264
- mode: 0o600,
265
- });
266
- }
267
- catch {
268
- return { status: "failed", reason: "write" };
269
- }
270
- try {
271
- const readback = parseObject(await readFile(transactionPath, "utf8"));
272
- const value = parseOptionalObject(readback.mcpServers)[PRODUCT_IDENTITY.mcpServerKey];
273
- if (JSON.stringify(value) !== JSON.stringify(desired)) {
274
- await restoreConfig(transactionPath, original);
275
- return { status: "failed", reason: "readback" };
276
- }
277
- }
278
- catch {
279
- await restoreConfig(transactionPath, original);
280
- return { status: "failed", reason: "readback" };
281
- }
282
- return {
283
- status: "configured",
284
- ...(backupPath === undefined ? {} : { backupPath }),
285
- };
286
- };
287
- /** Back up, atomically update, and semantically read back Codex TOML configuration. */
288
- export const configureTomlClient = async (client, environment = {}, command = [
289
- "npx",
290
- "-y",
291
- PRODUCT_IDENTITY.packageName,
292
- "mcp",
293
- ]) => {
294
- const transactionPath = await resolveClientConfigTransactionPath(client.configPath);
295
- if (transactionPath === undefined)
296
- return { status: "failed", reason: "path" };
297
- let document = {};
298
- let original;
299
- try {
300
- original = await readFile(transactionPath, "utf8");
301
- document = objectSchema.parse(parseToml(original));
302
- }
303
- catch (cause) {
304
- if (!isMissing(cause))
305
- return { status: "failed", reason: "readback" };
306
- }
307
- let servers;
308
- try {
309
- servers = parseOptionalObject(document.mcp_servers);
310
- }
311
- catch {
312
- return { status: "failed", reason: "readback" };
313
- }
314
- const desired = clientConfigurationDesired(normalizeProviderEnvironment(environment), command);
315
- if (JSON.stringify(servers[PRODUCT_IDENTITY.mcpServerKey]) ===
316
- JSON.stringify(desired))
317
- return { status: "unchanged" };
318
- const backupPath = original === undefined ? undefined : `${client.configPath}.rea.backup`;
319
- if (backupPath !== undefined &&
320
- !(await preserveConfigBackup(transactionPath, backupPath)))
321
- return { status: "failed", reason: "backup" };
322
- document.mcp_servers = {
323
- ...servers,
324
- [PRODUCT_IDENTITY.mcpServerKey]: desired,
325
- };
326
- try {
327
- await mkdir(dirname(client.configPath), { recursive: true });
328
- await writeFileAtomic(transactionPath, stringifyToml(document), {
329
- encoding: "utf8",
330
- mode: 0o600,
331
- });
332
- const readback = objectSchema.parse(parseToml(await readFile(transactionPath, "utf8")));
333
- if (JSON.stringify(parseOptionalObject(readback.mcp_servers)[PRODUCT_IDENTITY.mcpServerKey]) !== JSON.stringify(desired))
334
- throw new Error("TOML readback mismatch");
335
- }
336
- catch {
337
- await restoreConfig(transactionPath, original);
338
- return { status: "failed", reason: "readback" };
339
- }
340
- return {
341
- status: "configured",
342
- ...(backupPath === undefined ? {} : { backupPath }),
343
- };
344
- };
345
287
  const major = (version) => Number.parseInt(version.split(".")[0] ?? "0", 10);
346
288
  const exists = async (path) => {
347
289
  try {
@@ -352,60 +294,6 @@ const exists = async (path) => {
352
294
  return false;
353
295
  }
354
296
  };
355
- const preserveConfigBackup = async (source, destination) => {
356
- try {
357
- await copyFile(source, destination, fsConstants.COPYFILE_EXCL);
358
- return true;
359
- }
360
- catch (cause) {
361
- return cause instanceof Error && "code" in cause && cause.code === "EEXIST";
362
- }
363
- };
364
- const objectSchema = z.record(z.string(), z.unknown());
365
- const parseOptionalObject = (value) => value === undefined ? {} : objectSchema.parse(value);
366
- const parseObject = (text) => objectSchema.parse(JSON.parse(text));
367
- const isMissing = (cause) => cause instanceof Error && "code" in cause && cause.code === "ENOENT";
368
- const restoreConfig = async (path, original) => {
369
- try {
370
- if (original === undefined)
371
- await rm(path, { force: true });
372
- else
373
- await writeFile(path, original, { encoding: "utf8", mode: 0o600 });
374
- }
375
- catch {
376
- // The backup remains available for the remediation reported by setup.
377
- }
378
- };
379
- const clientConfigurationDesired = (providerEnvironment, command) => {
380
- const environment = Object.fromEntries(Object.entries(providerEnvironment).sort(([left], [right]) => left.localeCompare(right)));
381
- return {
382
- command: command[0] ?? PRODUCT_IDENTITY.cliBinary,
383
- args: command.slice(1),
384
- ...(Object.keys(environment).length === 0 ? {} : { env: environment }),
385
- };
386
- };
387
- const clientConfigurationAligned = async (client, providerEnvironment, command) => {
388
- const desired = clientConfigurationDesired(providerEnvironment, command);
389
- try {
390
- const original = await readFile(client.configPath, "utf8");
391
- if (client.format === "toml") {
392
- const document = objectSchema.parse(parseToml(original));
393
- const servers = parseOptionalObject(document.mcp_servers);
394
- return (JSON.stringify(servers[PRODUCT_IDENTITY.mcpServerKey]) ===
395
- JSON.stringify(desired));
396
- }
397
- const document = parseObject(original);
398
- const servers = parseOptionalObject(document.mcpServers);
399
- return (JSON.stringify(servers[PRODUCT_IDENTITY.mcpServerKey]) ===
400
- JSON.stringify(desired));
401
- }
402
- catch {
403
- return false;
404
- }
405
- };
406
- const normalizeProviderEnvironment = (environment) => typeof environment === "string"
407
- ? { HOPPER_LAUNCHER_PATH: environment }
408
- : environment;
409
297
  const providerRegistrationEnvironment = (inspections) => Object.fromEntries(inspections
410
298
  .filter(({ available }) => available)
411
299
  .flatMap(({ registrationEnvironment }) => Object.entries(registrationEnvironment))