rea-agents 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -1,58 +1,47 @@
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
+ ? ["npx", "-y", PRODUCT_IDENTITY.packageSpecifier, "mcp"]
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,
@@ -61,25 +50,21 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
61
50
  doctor: await host.doctor(),
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 || plannedActions.length === 0;
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))