rea-agents 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -18
- package/dist/application/AndroidApplicationService.js +17 -0
- package/dist/application/AppleApplicationService.js +17 -0
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
- package/dist/application/BinarySession.js +9 -2
- package/dist/application/BinarySessionStatus.js +39 -1
- package/dist/application/CapabilityInventory.js +15 -0
- package/dist/application/ClientRegistrationStatus.js +11 -4
- package/dist/application/CommandShimReplay.js +6 -3
- package/dist/application/CompositeProvider.js +5 -2
- package/dist/application/DirectAnalysis.js +1 -20
- package/dist/application/DirectAnalysisStatus.js +83 -0
- package/dist/application/Doctor.js +34 -25
- package/dist/application/DoctorDiagnostics.js +1 -4
- package/dist/application/DoctorProjection.js +43 -0
- package/dist/application/DoctorScope.js +102 -0
- package/dist/application/EvidenceReferenceResolver.js +26 -0
- package/dist/application/InventoryProjectionEvidence.js +41 -0
- package/dist/application/InvestigationProviders.js +12 -0
- package/dist/application/JavaScriptApplicationEvidence.js +1 -1
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
- package/dist/application/JavaScriptApplicationService.js +3 -2
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
- package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
- package/dist/application/JavaScriptModuleRelationships.js +25 -1
- package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
- package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
- package/dist/application/JavaScriptSemanticTraceService.js +45 -0
- package/dist/application/LoopbackReplay.js +277 -133
- package/dist/application/LoopbackReplayRecorder.js +101 -0
- package/dist/application/PermissionAuthority.js +7 -3
- package/dist/application/ProcessCaptureJournal.js +95 -0
- package/dist/application/ProcessCaptureLifecycle.js +24 -13
- package/dist/application/ProcessCheckpoints.js +30 -4
- package/dist/application/ProcessCli.js +26 -4
- package/dist/application/ProcessHarness.js +20 -50
- package/dist/application/ProcessNormalization.js +3 -2
- package/dist/application/ProcessPairedExperiment.js +72 -0
- package/dist/application/ProcessReactiveCoordinator.js +218 -0
- package/dist/application/ProcessReactiveEffects.js +133 -0
- package/dist/application/ProcessReactiveObservations.js +20 -0
- package/dist/application/ProcessSampling.js +2 -0
- package/dist/application/SessionProviderRouter.js +7 -6
- package/dist/application/Setup.js +14 -13
- package/dist/application/SetupClientConfiguration.js +10 -6
- package/dist/application/SetupPlan.js +4 -3
- package/dist/application/SetupRegistrationEnvironment.js +12 -0
- package/dist/application/SetupSkill.js +44 -40
- package/dist/application/TerminalRenderer.js +3 -1
- package/dist/application/Uninstall.js +7 -1
- package/dist/application/Upgrade.js +16 -1
- package/dist/browser/CdpEndpoint.js +37 -4
- package/dist/catalogIdentity.js +16 -7
- package/dist/cli/coreAnalysisCommands.js +34 -2
- package/dist/cli/javascriptApplicationAnalysis.js +19 -0
- package/dist/cli/setupCommands.js +84 -30
- package/dist/cli/utilityCommands.js +10 -2
- package/dist/cliApplicationCommands.js +79 -16
- package/dist/cliCommandNames.js +3 -0
- package/dist/cliElectronCommands.js +23 -33
- package/dist/cliObservationOptions.js +1 -1
- package/dist/cliOutput.js +56 -0
- package/dist/cliProcessCommands.js +32 -1
- package/dist/cliSetup.js +55 -29
- package/dist/contracts/applicationToolContracts.js +54 -7
- package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
- package/dist/contracts/electronToolContracts.js +73 -4
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
- package/dist/contracts/managedToolContracts.js +7 -2
- package/dist/contracts/processCaptureExample.js +1 -0
- package/dist/contracts/replayMachineExample.js +37 -0
- package/dist/contracts/sessionStatusContract.js +26 -0
- package/dist/contracts/toolContractExamples.js +2 -0
- package/dist/contracts/toolContractTypes.js +12 -1
- package/dist/contracts/toolContracts.js +8 -11
- package/dist/contracts/toolEffects.js +3 -0
- package/dist/contracts/toolOutputSchemaGroups.js +53 -9
- package/dist/contracts/toolOutputSchemaPrimitives.js +73 -32
- package/dist/doctorRuntime.js +41 -0
- package/dist/domain/analysisErrorPresentation.js +5 -3
- package/dist/domain/analysisErrorProjection.js +7 -1
- package/dist/domain/androidApplication.js +206 -0
- package/dist/domain/appleApplication.js +221 -0
- package/dist/domain/boundedCartesianProjection.js +15 -0
- package/dist/domain/changedBehavior.js +5 -4
- package/dist/domain/completionLedgerGeneration.js +190 -0
- package/dist/domain/errors.js +4 -2
- package/dist/domain/evidenceCompletionLedger.js +113 -0
- package/dist/domain/hopperStartupFailure.js +9 -0
- package/dist/domain/javascriptApplicationAnalysis.js +44 -2
- package/dist/domain/javascriptExportShapeComparison.js +145 -0
- package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
- package/dist/domain/javascriptExportShapeSelection.js +132 -0
- package/dist/domain/javascriptExportShapeVariants.js +272 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
- package/dist/domain/javascriptSemanticAnalysis.js +17 -36
- package/dist/domain/javascriptSemanticCallResolution.js +129 -0
- package/dist/domain/javascriptSemanticCalls.js +269 -0
- package/dist/domain/javascriptSemanticGraph.js +356 -0
- package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
- package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
- package/dist/domain/javascriptSemanticIr.js +14 -1
- package/dist/domain/javascriptSemanticPrimitives.js +36 -0
- package/dist/domain/javascriptSemanticProjection.js +36 -4
- package/dist/domain/javascriptSemanticProvenance.js +29 -0
- package/dist/domain/javascriptSemanticQuery.js +294 -0
- package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
- package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
- package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
- package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
- package/dist/domain/javascriptSemanticReturns.js +187 -0
- package/dist/domain/javascriptSemanticState.js +32 -0
- package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
- package/dist/domain/javascriptSemanticValues.js +54 -70
- package/dist/domain/processCapture.js +36 -0
- package/dist/domain/processCaptureValidation.js +101 -12
- package/dist/domain/processComparison.js +96 -24
- package/dist/domain/processObservation.js +153 -0
- package/dist/domain/processPairedExperiment.js +128 -0
- package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
- package/dist/domain/processReactiveMatching.js +210 -0
- package/dist/domain/processReactiveRuntime.js +109 -0
- package/dist/domain/processReactiveScenario.js +361 -0
- package/dist/domain/processReactiveScenarioPreflight.js +120 -0
- package/dist/domain/processReactiveTransition.js +159 -0
- package/dist/domain/processScenario.js +12 -0
- package/dist/domain/processTraceComparison.js +43 -0
- package/dist/domain/processTraceDimensionProjection.js +50 -0
- package/dist/domain/processTraceEvaluation.js +312 -0
- package/dist/domain/processTraceSpecification.js +373 -0
- package/dist/domain/reconstructionVerification.js +5 -4
- package/dist/domain/replayMachine.js +332 -0
- package/dist/domain/replayMachineRun.js +216 -0
- package/dist/domain/replayMachineRuntime.js +294 -0
- package/dist/domain/runtimeIdentification.js +210 -0
- package/dist/domain/staticRuntimeCorrelation.js +4 -2
- package/dist/evaluation/CodexAgentEval.js +156 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +13 -2
- package/dist/ghidra/GhidraDiagnostics.js +1 -0
- package/dist/ghidra/GhidraLauncher.js +7 -3
- package/dist/ghidra/GhidraProvider.js +8 -1
- package/dist/hopper/BridgeLauncher.js +56 -14
- package/dist/hopper/HopperClient.js +23 -10
- package/dist/hopper/HopperDiagnostics.js +14 -0
- package/dist/hopper/HopperProcessDiagnostic.js +14 -0
- package/dist/hopper/HopperProvider.js +8 -1
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
- package/dist/identity.js +2 -1
- package/dist/main/transport.js +2 -0
- package/dist/mcpDoctor.js +310 -0
- package/dist/process/ProcessOwnership.js +200 -53
- package/dist/process/ProviderRunLineage.js +21 -0
- package/dist/server/createServer.js +8 -2
- package/dist/server/javascriptApplicationResult.js +106 -0
- package/dist/server/registerApplicationTools/characterization.js +3 -3
- package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
- package/dist/server/registerApplicationTools/compareVersions.js +12 -6
- package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -3
- package/dist/server/registerApplicationTools/helpers.js +37 -25
- package/dist/server/registerApplicationTools/traceFeature.js +9 -5
- package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
- package/dist/server/registerApplicationTools.js +4 -0
- package/dist/server/registerElectronTools.js +19 -5
- package/dist/server/registerEvidenceResources.js +7 -1
- package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
- package/dist/server/registerManagedTools.js +18 -1
- package/dist/server/registerProcessComparisonTool.js +17 -7
- package/dist/server/registerReplayMachineTool.js +20 -0
- package/dist/server/registerSessionStatusTool.js +95 -25
- package/dist/server/registerSessionTools.js +12 -7
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/server/toolResult.js +10 -4
- package/dist/serverIdentity.js +19 -0
- package/package.json +62 -29
- package/scripts/hopper-demo-x11.py +459 -61
- package/scripts/prepack.mjs +55 -0
- package/scripts/prepare.mjs +28 -0
- package/scripts/rea.mjs +26 -5
- package/skills/reverse-engineer-anything/SKILL.md +69 -228
- package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +35 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { commitProcessReactiveProposal, createProcessReactiveSnapshot, reduceProcessReactiveScenario, } from "../domain/processReactiveRuntime.js";
|
|
2
|
+
const systemTimerHost = {
|
|
3
|
+
schedule: (callback, delayMs) => {
|
|
4
|
+
const timer = setTimeout(callback, delayMs);
|
|
5
|
+
return { cancel: () => clearTimeout(timer) };
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Serializes live observations, deadlines, and effect commits for one run.
|
|
10
|
+
* Producer callbacks enqueue work without awaiting it, preventing reentrancy.
|
|
11
|
+
*/
|
|
12
|
+
export class ProcessReactiveCoordinator {
|
|
13
|
+
#scenario;
|
|
14
|
+
#executor;
|
|
15
|
+
#timerHost;
|
|
16
|
+
#now;
|
|
17
|
+
#scenarioDeadlineAt;
|
|
18
|
+
#onDecision;
|
|
19
|
+
#snapshot;
|
|
20
|
+
#pendingInputs = [];
|
|
21
|
+
#tail = Promise.resolve();
|
|
22
|
+
#running = false;
|
|
23
|
+
#failure;
|
|
24
|
+
#stateTimer;
|
|
25
|
+
#scenarioTimer;
|
|
26
|
+
#stateDeadlineAt = 0;
|
|
27
|
+
#effectAbort;
|
|
28
|
+
#accepting = true;
|
|
29
|
+
#closed = false;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.#scenario = options.scenario;
|
|
32
|
+
this.#executor = options.executor;
|
|
33
|
+
this.#timerHost = options.timerHost ?? systemTimerHost;
|
|
34
|
+
this.#now = options.now ?? Date.now;
|
|
35
|
+
this.#scenarioDeadlineAt = this.#now() + options.scenario.deadline_ms;
|
|
36
|
+
this.#onDecision = options.onDecision;
|
|
37
|
+
this.#snapshot = createProcessReactiveSnapshot(options.scenario);
|
|
38
|
+
this.#scheduleScenarioDeadline();
|
|
39
|
+
this.#scheduleStateDeadline();
|
|
40
|
+
}
|
|
41
|
+
/** Current immutable reducer snapshot after all completed turns. */
|
|
42
|
+
get snapshot() {
|
|
43
|
+
return this.#snapshot;
|
|
44
|
+
}
|
|
45
|
+
/** Enqueue producer work without creating a floating promise. */
|
|
46
|
+
enqueue(input) {
|
|
47
|
+
if (!this.#accepting)
|
|
48
|
+
return;
|
|
49
|
+
if (this.#interruptsEffects(input))
|
|
50
|
+
this.#effectAbort?.abort();
|
|
51
|
+
this.#enqueueOwned(input);
|
|
52
|
+
}
|
|
53
|
+
/** Submit one input and wait until every turn queued before it completes. */
|
|
54
|
+
async submit(input) {
|
|
55
|
+
this.enqueue(input);
|
|
56
|
+
await this.drain();
|
|
57
|
+
return this.#snapshot;
|
|
58
|
+
}
|
|
59
|
+
/** Wait for currently queued reducer/effect work and surface host failures. */
|
|
60
|
+
async drain() {
|
|
61
|
+
for (;;) {
|
|
62
|
+
const observedTail = this.#tail;
|
|
63
|
+
await observedTail;
|
|
64
|
+
if (observedTail === this.#tail)
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (this.#failure !== undefined)
|
|
68
|
+
throw this.#failure;
|
|
69
|
+
}
|
|
70
|
+
/** Stop owned deadlines after queued work settles. */
|
|
71
|
+
async close() {
|
|
72
|
+
this.#accepting = false;
|
|
73
|
+
this.#clearTimers();
|
|
74
|
+
await this.drain();
|
|
75
|
+
this.#closed = true;
|
|
76
|
+
}
|
|
77
|
+
#enqueueOwned(input) {
|
|
78
|
+
if (this.#closed || this.#failure !== undefined)
|
|
79
|
+
return;
|
|
80
|
+
this.#pendingInputs.push(input);
|
|
81
|
+
this.#startWorker();
|
|
82
|
+
}
|
|
83
|
+
#startWorker() {
|
|
84
|
+
if (this.#running || this.#pendingInputs.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
this.#running = true;
|
|
87
|
+
this.#tail = this.#runQueue()
|
|
88
|
+
.catch((cause) => this.#fail(cause))
|
|
89
|
+
.finally(() => {
|
|
90
|
+
this.#running = false;
|
|
91
|
+
if (this.#pendingInputs.length > 0)
|
|
92
|
+
this.#startWorker();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async #runQueue() {
|
|
96
|
+
for (;;) {
|
|
97
|
+
const input = this.#nextInput();
|
|
98
|
+
if (input === undefined)
|
|
99
|
+
return;
|
|
100
|
+
await this.#advance(input);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
#nextInput() {
|
|
104
|
+
const terminalIndex = this.#pendingInputs.findIndex(({ kind }) => kind === "cancelled" || kind === "cleanup_failed");
|
|
105
|
+
if (terminalIndex >= 0)
|
|
106
|
+
return this.#pendingInputs.splice(terminalIndex, 1)[0];
|
|
107
|
+
let observationIndex = -1;
|
|
108
|
+
let captureOrder = Number.POSITIVE_INFINITY;
|
|
109
|
+
for (const [index, input] of this.#pendingInputs.entries())
|
|
110
|
+
if (input.kind === "observation" &&
|
|
111
|
+
input.observation.capture_order < captureOrder) {
|
|
112
|
+
observationIndex = index;
|
|
113
|
+
captureOrder = input.observation.capture_order;
|
|
114
|
+
}
|
|
115
|
+
return observationIndex >= 0
|
|
116
|
+
? this.#pendingInputs.splice(observationIndex, 1)[0]
|
|
117
|
+
: this.#pendingInputs.shift();
|
|
118
|
+
}
|
|
119
|
+
#fail(cause) {
|
|
120
|
+
this.#failure ??= cause;
|
|
121
|
+
this.#accepting = false;
|
|
122
|
+
this.#closed = true;
|
|
123
|
+
this.#effectAbort?.abort();
|
|
124
|
+
this.#pendingInputs.length = 0;
|
|
125
|
+
this.#clearTimers();
|
|
126
|
+
}
|
|
127
|
+
async #advance(input) {
|
|
128
|
+
const before = this.#snapshot;
|
|
129
|
+
let decision = reduceProcessReactiveScenario(this.#scenario, before, input);
|
|
130
|
+
let effectResults = [];
|
|
131
|
+
if (decision.kind === "proposal") {
|
|
132
|
+
const effectAbort = new AbortController();
|
|
133
|
+
this.#effectAbort = effectAbort;
|
|
134
|
+
try {
|
|
135
|
+
effectResults = await this.#executor.execute(decision.effects, effectAbort.signal);
|
|
136
|
+
}
|
|
137
|
+
catch (cause) {
|
|
138
|
+
if (effectAbort.signal.aborted)
|
|
139
|
+
return;
|
|
140
|
+
throw cause;
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
if (this.#effectAbort === effectAbort)
|
|
144
|
+
this.#effectAbort = undefined;
|
|
145
|
+
}
|
|
146
|
+
if (effectAbort.signal.aborted)
|
|
147
|
+
return;
|
|
148
|
+
const expired = this.#expiredDeadlineInput();
|
|
149
|
+
if (expired !== undefined) {
|
|
150
|
+
this.#enqueueOwned(expired);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
decision = commitProcessReactiveProposal(this.#scenario, decision, effectResults);
|
|
154
|
+
}
|
|
155
|
+
this.#snapshot = decision.snapshot;
|
|
156
|
+
this.#onDecision?.(decision);
|
|
157
|
+
if (this.#snapshot.status === "finished")
|
|
158
|
+
this.#clearTimers();
|
|
159
|
+
else if (this.#accepting &&
|
|
160
|
+
(before.active_state !== this.#snapshot.active_state ||
|
|
161
|
+
before.state_entry_capture_order !==
|
|
162
|
+
this.#snapshot.state_entry_capture_order))
|
|
163
|
+
this.#scheduleStateDeadline();
|
|
164
|
+
for (const result of effectResults)
|
|
165
|
+
if (result.status === "succeeded")
|
|
166
|
+
this.#enqueueOwned({
|
|
167
|
+
kind: "observation",
|
|
168
|
+
observation: result.observation,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
#interruptsEffects(input) {
|
|
172
|
+
if (input.kind === "cancelled" ||
|
|
173
|
+
input.kind === "cleanup_failed" ||
|
|
174
|
+
input.kind === "scenario_deadline")
|
|
175
|
+
return true;
|
|
176
|
+
return (input.kind === "state_deadline" &&
|
|
177
|
+
input.state_id === this.#snapshot.active_state &&
|
|
178
|
+
input.state_entry_capture_order ===
|
|
179
|
+
this.#snapshot.state_entry_capture_order);
|
|
180
|
+
}
|
|
181
|
+
#expiredDeadlineInput() {
|
|
182
|
+
const now = this.#now();
|
|
183
|
+
if (now >= this.#scenarioDeadlineAt)
|
|
184
|
+
return { kind: "scenario_deadline" };
|
|
185
|
+
if (now < this.#stateDeadlineAt)
|
|
186
|
+
return undefined;
|
|
187
|
+
return {
|
|
188
|
+
kind: "state_deadline",
|
|
189
|
+
state_id: this.#snapshot.active_state,
|
|
190
|
+
state_entry_capture_order: this.#snapshot.state_entry_capture_order,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
#scheduleScenarioDeadline() {
|
|
194
|
+
if (!this.#accepting)
|
|
195
|
+
return;
|
|
196
|
+
this.#scenarioTimer = this.#timerHost.schedule(() => this.enqueue({ kind: "scenario_deadline" }), this.#scenario.deadline_ms);
|
|
197
|
+
}
|
|
198
|
+
#scheduleStateDeadline() {
|
|
199
|
+
if (!this.#accepting)
|
|
200
|
+
return;
|
|
201
|
+
this.#stateTimer?.cancel();
|
|
202
|
+
const state = this.#scenario.states.find(({ id }) => id === this.#snapshot.active_state);
|
|
203
|
+
if (state === undefined)
|
|
204
|
+
return;
|
|
205
|
+
this.#stateDeadlineAt = this.#now() + state.deadline_ms;
|
|
206
|
+
const identity = {
|
|
207
|
+
state_id: state.id,
|
|
208
|
+
state_entry_capture_order: this.#snapshot.state_entry_capture_order,
|
|
209
|
+
};
|
|
210
|
+
this.#stateTimer = this.#timerHost.schedule(() => this.enqueue({ kind: "state_deadline", ...identity }), state.deadline_ms);
|
|
211
|
+
}
|
|
212
|
+
#clearTimers() {
|
|
213
|
+
this.#stateTimer?.cancel();
|
|
214
|
+
this.#scenarioTimer?.cancel();
|
|
215
|
+
this.#stateTimer = undefined;
|
|
216
|
+
this.#scenarioTimer = undefined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createProcessObservation } from "../domain/processObservation.js";
|
|
2
|
+
const elapsed = (host) => Math.max(0, host.now() - host.startedAtMs);
|
|
3
|
+
const interactionData = (action) => {
|
|
4
|
+
if (action.type === "send_input")
|
|
5
|
+
return action.sensitive
|
|
6
|
+
? `<redacted-input:${String(Buffer.byteLength(action.data))}-bytes>`
|
|
7
|
+
: action.data;
|
|
8
|
+
if (action.type === "resize")
|
|
9
|
+
return `${String(action.columns)}x${String(action.rows)}`;
|
|
10
|
+
return action.signal;
|
|
11
|
+
};
|
|
12
|
+
const interactionType = (action) => {
|
|
13
|
+
if (action.type === "send_input")
|
|
14
|
+
return "input";
|
|
15
|
+
if (action.type === "resize")
|
|
16
|
+
return "resize";
|
|
17
|
+
return "signal";
|
|
18
|
+
};
|
|
19
|
+
const performTerminalAction = (terminal, renderer, action, atMs) => {
|
|
20
|
+
if (action.type === "send_input")
|
|
21
|
+
terminal.write(action.data);
|
|
22
|
+
else if (action.type === "resize") {
|
|
23
|
+
terminal.resize(action.columns, action.rows);
|
|
24
|
+
renderer.resize(action.columns, action.rows, atMs);
|
|
25
|
+
}
|
|
26
|
+
else
|
|
27
|
+
terminal.kill(action.signal);
|
|
28
|
+
};
|
|
29
|
+
const recordInteraction = (host, action, atMs, outcome) => {
|
|
30
|
+
const index = host.interactions.length;
|
|
31
|
+
const event = {
|
|
32
|
+
sequence: index,
|
|
33
|
+
scheduled_at_ms: atMs,
|
|
34
|
+
dispatched_at_ms: atMs,
|
|
35
|
+
type: interactionType(action),
|
|
36
|
+
data: interactionData(action),
|
|
37
|
+
outcome,
|
|
38
|
+
};
|
|
39
|
+
host.interactions.push(event);
|
|
40
|
+
const location = host.journal.record("interaction_events", index);
|
|
41
|
+
if (outcome !== "dispatched")
|
|
42
|
+
return { status: "rejected" };
|
|
43
|
+
return {
|
|
44
|
+
status: "succeeded",
|
|
45
|
+
observation: createProcessObservation({
|
|
46
|
+
source: "interaction",
|
|
47
|
+
source_sequence: index,
|
|
48
|
+
captured_at_ms: atMs,
|
|
49
|
+
subject_id: null,
|
|
50
|
+
location,
|
|
51
|
+
payload: event,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const executeTerminalAction = (host, action) => {
|
|
56
|
+
if (action.type === "send_signal" && action.target.kind !== "root")
|
|
57
|
+
return { status: "rejected" };
|
|
58
|
+
const terminal = host.terminal();
|
|
59
|
+
if (terminal === undefined)
|
|
60
|
+
return { status: "target_lost" };
|
|
61
|
+
const atMs = elapsed(host);
|
|
62
|
+
let outcome = "dispatched";
|
|
63
|
+
try {
|
|
64
|
+
performTerminalAction(terminal, host.renderer, action, atMs);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
outcome = "failed";
|
|
68
|
+
}
|
|
69
|
+
return recordInteraction(host, action, atMs, outcome);
|
|
70
|
+
};
|
|
71
|
+
const executeCheckpoint = async (host, action, signal) => {
|
|
72
|
+
try {
|
|
73
|
+
const { index, checkpoint } = await host.checkpoints.captureAndRead(action.name, signal);
|
|
74
|
+
const location = host.journal.entries.findLast((entry) => entry.collection === "filesystem_checkpoints" && entry.index === index);
|
|
75
|
+
if (location === undefined)
|
|
76
|
+
return { status: "rejected" };
|
|
77
|
+
return {
|
|
78
|
+
status: "succeeded",
|
|
79
|
+
observation: createProcessObservation({
|
|
80
|
+
source: "filesystem",
|
|
81
|
+
source_sequence: index,
|
|
82
|
+
captured_at_ms: checkpoint.at_ms,
|
|
83
|
+
subject_id: `checkpoint:${checkpoint.name}`,
|
|
84
|
+
location,
|
|
85
|
+
payload: checkpoint,
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return { status: "rejected" };
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/** Execute proposed effects in order and stop after the first failure. */
|
|
94
|
+
export const executeProcessReactiveEffects = async (host, actions, signal) => {
|
|
95
|
+
const results = [];
|
|
96
|
+
for (const action of actions) {
|
|
97
|
+
if (signal.aborted)
|
|
98
|
+
return results;
|
|
99
|
+
const result = action.type === "checkpoint"
|
|
100
|
+
? await executeCheckpoint(host, action, signal)
|
|
101
|
+
: executeTerminalAction(host, action);
|
|
102
|
+
results.push(result);
|
|
103
|
+
if (result.status !== "succeeded")
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
return results;
|
|
107
|
+
};
|
|
108
|
+
const triggerSources = (trigger) => {
|
|
109
|
+
if (trigger.kind === "terminal_text")
|
|
110
|
+
return [];
|
|
111
|
+
if (trigger.kind === "event")
|
|
112
|
+
return ["terminal_raw", "interaction", "filesystem"].includes(trigger.source)
|
|
113
|
+
? []
|
|
114
|
+
: [trigger.source];
|
|
115
|
+
return trigger.kind === "repeat"
|
|
116
|
+
? triggerSources(trigger.trigger)
|
|
117
|
+
: trigger.triggers.flatMap(triggerSources);
|
|
118
|
+
};
|
|
119
|
+
/** Return unsupported sources, selectors, and checkpoint collisions pre-launch. */
|
|
120
|
+
export const unsupportedProcessReactiveFeatures = (scenario, reservedCheckpointNames = new Set([
|
|
121
|
+
"before",
|
|
122
|
+
"after_settlement",
|
|
123
|
+
])) => [
|
|
124
|
+
...scenario.states.flatMap((state) => state.on.flatMap((transition) => triggerSources(transition.when).map((source) => `${transition.id}:source:${source}`))),
|
|
125
|
+
...scenario.states.flatMap((state) => state.on.flatMap((transition) => transition.actions.flatMap((action) => {
|
|
126
|
+
if (action.type === "send_signal" && action.target.kind !== "root")
|
|
127
|
+
return [`${transition.id}:target:${action.target.kind}`];
|
|
128
|
+
if (action.type === "checkpoint" &&
|
|
129
|
+
reservedCheckpointNames.has(action.name))
|
|
130
|
+
return [`${transition.id}:checkpoint:${action.name}`];
|
|
131
|
+
return [];
|
|
132
|
+
}))),
|
|
133
|
+
];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createProcessObservation } from "../domain/processObservation.js";
|
|
2
|
+
/** Subscribe a reactive coordinator to normalized raw PTY journal records. */
|
|
3
|
+
export const subscribeProcessReactiveTerminalObservations = (options) => options.journal.subscribe((entry) => {
|
|
4
|
+
if (entry.collection !== "frames")
|
|
5
|
+
return;
|
|
6
|
+
const frame = options.frames[entry.index];
|
|
7
|
+
if (frame === undefined)
|
|
8
|
+
return;
|
|
9
|
+
options.sink.enqueue({
|
|
10
|
+
kind: "observation",
|
|
11
|
+
observation: createProcessObservation({
|
|
12
|
+
source: "terminal_raw",
|
|
13
|
+
source_sequence: frame.sequence,
|
|
14
|
+
captured_at_ms: frame.at_ms,
|
|
15
|
+
subject_id: null,
|
|
16
|
+
location: entry,
|
|
17
|
+
payload: frame,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -366,7 +366,9 @@ export const startProcessSampler = (options) => {
|
|
|
366
366
|
continue;
|
|
367
367
|
}
|
|
368
368
|
lastObservations.set(value.pid, observation);
|
|
369
|
+
const index = samples.length;
|
|
369
370
|
samples.push(value);
|
|
371
|
+
options.recordEvent?.("process_samples", index);
|
|
370
372
|
}
|
|
371
373
|
})
|
|
372
374
|
.catch((cause) => {
|
|
@@ -86,7 +86,7 @@ export class SessionProviderRouter {
|
|
|
86
86
|
compatibility: {},
|
|
87
87
|
binding: null,
|
|
88
88
|
selection: undefined,
|
|
89
|
-
createClient: (target) => createLegacyClient(runtime, target, undefined),
|
|
89
|
+
createClient: (target, context) => createLegacyClient(runtime, target, undefined, context),
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
/** Resolve target routes before any provider client is created. */
|
|
@@ -122,7 +122,7 @@ export class SessionProviderRouter {
|
|
|
122
122
|
compatibility: normalized.value.compatibility,
|
|
123
123
|
binding: null,
|
|
124
124
|
selection: undefined,
|
|
125
|
-
createClient: (openedTarget) => createLegacyClient(runtime, openedTarget, normalized.value.profile ?? undefined),
|
|
125
|
+
createClient: (openedTarget, context) => createLegacyClient(runtime, openedTarget, normalized.value.profile ?? undefined, context),
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
/** Candidate status authoritative for the active or target-free route. */
|
|
@@ -156,12 +156,13 @@ const selectableRoute = (auxiliaryProviders, selection) => {
|
|
|
156
156
|
compatibility: structuredClone(binding?.compatibility ?? {}),
|
|
157
157
|
binding,
|
|
158
158
|
selection,
|
|
159
|
-
createClient: (target) => provider?.createClient(target, binding?.profile) ??
|
|
159
|
+
createClient: (target, context) => provider?.createClient(target, binding?.profile, context) ??
|
|
160
|
+
emptyClient(identity),
|
|
160
161
|
};
|
|
161
162
|
};
|
|
162
|
-
const createLegacyClient = (runtime, target, profile) => typeof runtime.provider === "function"
|
|
163
|
-
? runtime.provider(target, profile)
|
|
164
|
-
: runtime.provider.createClient(target, profile);
|
|
163
|
+
const createLegacyClient = (runtime, target, profile, context) => typeof runtime.provider === "function"
|
|
164
|
+
? runtime.provider(target, profile, context)
|
|
165
|
+
: runtime.provider.createClient(target, profile, context);
|
|
165
166
|
const emptyClient = (identity) => ({
|
|
166
167
|
execute: (operation) => operation === "health"
|
|
167
168
|
? Promise.resolve(ok({
|
|
@@ -13,16 +13,18 @@ import { discoverSetupActions } from "./SetupPlan.js";
|
|
|
13
13
|
import { clientConfigurationAligned, configureJsonClient, configureTomlClient, inspectClientConfiguration, } from "./SetupClientConfiguration.js";
|
|
14
14
|
export { configureJsonClient, configureTomlClient };
|
|
15
15
|
import { setupInstallFailure, } from "./SetupInstallFailure.js";
|
|
16
|
+
import { providerRegistrationEnvironment, registrationPermissionEnvironment, } from "./SetupRegistrationEnvironment.js";
|
|
17
|
+
export { registrationPermissionEnvironment } from "./SetupRegistrationEnvironment.js";
|
|
16
18
|
export { canonicalSkillNeedsInstall, installCanonicalSkill };
|
|
17
19
|
const registrationCommand = () => process.env.npm_command === "exec"
|
|
18
|
-
?
|
|
20
|
+
? PRODUCT_IDENTITY.mcpCommand.split(" ")
|
|
19
21
|
: [resolve(process.argv[1] ?? PRODUCT_IDENTITY.cliBinary), "mcp"];
|
|
20
22
|
/** Discover, approve, and apply setup actions idempotently. */
|
|
21
23
|
export const runSetup = async (options, host = systemSetupHost(), confirm) => {
|
|
22
24
|
const appliedActions = [];
|
|
23
25
|
let plannedActions = [];
|
|
24
26
|
const clients = {};
|
|
25
|
-
const fail = (remediation) => setupFailure(remediation, [host, plannedActions, appliedActions, clients]);
|
|
27
|
+
const fail = (remediation) => setupFailure(remediation, [host, plannedActions, appliedActions, clients], options.readinessScope);
|
|
26
28
|
const unsupported = await hostRemediation(host, options.installHopper);
|
|
27
29
|
if (unsupported !== undefined)
|
|
28
30
|
return fail(unsupported);
|
|
@@ -36,6 +38,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
|
|
|
36
38
|
proposeHopper: options.proposeHopper ?? true,
|
|
37
39
|
selectedClientIds: options.clientIds,
|
|
38
40
|
installSkillSelection: options.installSkill,
|
|
41
|
+
doctorScope: options.readinessScope,
|
|
39
42
|
});
|
|
40
43
|
if (discovery.blocker !== undefined)
|
|
41
44
|
return fail(discovery.blocker);
|
|
@@ -47,7 +50,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
|
|
|
47
50
|
plannedActions,
|
|
48
51
|
appliedActions,
|
|
49
52
|
clients,
|
|
50
|
-
doctor:
|
|
53
|
+
doctor: discovery.initialDoctor,
|
|
51
54
|
remediation: "Review the setup plan, then rerun interactively or with --yes.",
|
|
52
55
|
};
|
|
53
56
|
let detectedClients = selection.detectedClients;
|
|
@@ -61,6 +64,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
|
|
|
61
64
|
clients,
|
|
62
65
|
detectedClients,
|
|
63
66
|
providerEnvironment,
|
|
67
|
+
doctorScope: options.readinessScope,
|
|
64
68
|
});
|
|
65
69
|
if ("failure" in install)
|
|
66
70
|
return install.failure;
|
|
@@ -82,7 +86,7 @@ export const runSetup = async (options, host = systemSetupHost(), confirm) => {
|
|
|
82
86
|
if (selection.installSkill &&
|
|
83
87
|
!(await installSkillAction(host, options, appliedActions)))
|
|
84
88
|
return fail("REA analysis skill could not be installed or verified. Check permissions for `~/.agents/skills`, then rerun setup.");
|
|
85
|
-
const doctor = await host.doctor();
|
|
89
|
+
const doctor = await host.doctor(options.readinessScope);
|
|
86
90
|
const remediation = finalSetupRemediation(host.platform, appliedActions.includes("installed_hopper"), doctor.healthy, hopperPath);
|
|
87
91
|
return {
|
|
88
92
|
status: remediation === undefined ? "ready" : "needs_human",
|
|
@@ -110,7 +114,7 @@ const resolveSetupSelection = async (options, discovery, confirm) => {
|
|
|
110
114
|
installSkill = plannedActions.some(({ kind }) => kind === "install_skill");
|
|
111
115
|
detectedClients = detectedClients.filter((client) => selected.has(`configure_client:${client.name}`));
|
|
112
116
|
interactiveApproval = decision.approved;
|
|
113
|
-
approved = decision.approved
|
|
117
|
+
approved = decision.approved;
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
return {
|
|
@@ -143,7 +147,7 @@ const installHopperAction = async (input) => {
|
|
|
143
147
|
plannedActions: input.plannedActions,
|
|
144
148
|
appliedActions: input.appliedActions,
|
|
145
149
|
clients: input.clients,
|
|
146
|
-
doctor: await input.host.doctor(),
|
|
150
|
+
doctor: await input.host.doctor(input.doctorScope),
|
|
147
151
|
code: installed.code,
|
|
148
152
|
remediation: installed.remediation,
|
|
149
153
|
},
|
|
@@ -192,12 +196,12 @@ const installSkillAction = async (host, options, appliedActions) => {
|
|
|
192
196
|
});
|
|
193
197
|
return true;
|
|
194
198
|
};
|
|
195
|
-
const setupFailure = async (remediation, [host, plannedActions, appliedActions, clients]) => ({
|
|
199
|
+
const setupFailure = async (remediation, [host, plannedActions, appliedActions, clients], scope) => ({
|
|
196
200
|
status: "needs_human",
|
|
197
201
|
plannedActions,
|
|
198
202
|
appliedActions,
|
|
199
203
|
clients,
|
|
200
|
-
doctor: await host.doctor(),
|
|
204
|
+
doctor: await host.doctor(scope),
|
|
201
205
|
remediation,
|
|
202
206
|
});
|
|
203
207
|
const emitProgress = (options, event) => options.onProgress?.(event);
|
|
@@ -250,6 +254,7 @@ export const systemSetupHost = (doctorHost = systemDoctorHost()) => {
|
|
|
250
254
|
const diagnosis = await runDoctor(undefined, doctorHost);
|
|
251
255
|
return {
|
|
252
256
|
...providerRegistrationEnvironment(diagnosis.providerInspections ?? []),
|
|
257
|
+
...registrationPermissionEnvironment(process.env),
|
|
253
258
|
...(diagnosis.hopperPath === undefined
|
|
254
259
|
? {}
|
|
255
260
|
: { HOPPER_LAUNCHER_PATH: diagnosis.hopperPath }),
|
|
@@ -273,7 +278,7 @@ export const systemSetupHost = (doctorHost = systemDoctorHost()) => {
|
|
|
273
278
|
inspectClientConfiguration: inspectClientConfiguration,
|
|
274
279
|
skillNeedsInstall: () => canonicalSkillNeedsInstall(homedir()),
|
|
275
280
|
installSkill: () => installCanonicalSkill(homedir()),
|
|
276
|
-
doctor: () => runDoctor(undefined, doctorHost),
|
|
281
|
+
doctor: (scope) => runDoctor(undefined, doctorHost, scope),
|
|
277
282
|
};
|
|
278
283
|
};
|
|
279
284
|
/** Detect supported agents from stable per-user installation markers. */
|
|
@@ -294,7 +299,3 @@ const exists = async (path) => {
|
|
|
294
299
|
return false;
|
|
295
300
|
}
|
|
296
301
|
};
|
|
297
|
-
const providerRegistrationEnvironment = (inspections) => Object.fromEntries(inspections
|
|
298
|
-
.filter(({ available }) => available)
|
|
299
|
-
.flatMap(({ registrationEnvironment }) => Object.entries(registrationEnvironment))
|
|
300
|
-
.sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -9,9 +9,10 @@ import { resolveClientConfigTransactionPath } from "./ClientConfigPath.js";
|
|
|
9
9
|
const defaultCommand = () => [
|
|
10
10
|
"npx",
|
|
11
11
|
"-y",
|
|
12
|
-
PRODUCT_IDENTITY.
|
|
12
|
+
PRODUCT_IDENTITY.registrationPackageSpecifier,
|
|
13
13
|
"mcp",
|
|
14
14
|
];
|
|
15
|
+
const CODEX_STARTUP_TIMEOUT_SECONDS = 30;
|
|
15
16
|
/** Back up, atomically update, and semantically read back one JSON MCP configuration. */
|
|
16
17
|
export const configureJsonClient = async (client, environment = {}, command = defaultCommand()) => {
|
|
17
18
|
const transactionPath = await resolveClientConfigTransactionPath(client.configPath);
|
|
@@ -34,7 +35,7 @@ export const configureJsonClient = async (client, environment = {}, command = de
|
|
|
34
35
|
catch {
|
|
35
36
|
return { status: "failed", reason: "readback" };
|
|
36
37
|
}
|
|
37
|
-
const desired = clientConfigurationDesired(normalizeProviderEnvironment(environment), command);
|
|
38
|
+
const desired = clientConfigurationDesired(client, normalizeProviderEnvironment(environment), command);
|
|
38
39
|
if (sameConfiguration(servers[PRODUCT_IDENTITY.mcpServerKey], desired))
|
|
39
40
|
return { status: "unchanged" };
|
|
40
41
|
let backupPath;
|
|
@@ -95,7 +96,7 @@ export const configureTomlClient = async (client, environment = {}, command = de
|
|
|
95
96
|
catch {
|
|
96
97
|
return { status: "failed", reason: "readback" };
|
|
97
98
|
}
|
|
98
|
-
const desired = clientConfigurationDesired(normalizeProviderEnvironment(environment), command);
|
|
99
|
+
const desired = clientConfigurationDesired(client, normalizeProviderEnvironment(environment), command);
|
|
99
100
|
if (sameConfiguration(servers[PRODUCT_IDENTITY.mcpServerKey], desired))
|
|
100
101
|
return { status: "unchanged" };
|
|
101
102
|
const backupPath = original === undefined ? undefined : `${client.configPath}.rea.backup`;
|
|
@@ -127,7 +128,7 @@ export const configureTomlClient = async (client, environment = {}, command = de
|
|
|
127
128
|
};
|
|
128
129
|
/** Determine whether an existing client configuration matches the desired registration. */
|
|
129
130
|
export const clientConfigurationAligned = async (client, providerEnvironment, command) => {
|
|
130
|
-
const desired = clientConfigurationDesired(providerEnvironment, command);
|
|
131
|
+
const desired = clientConfigurationDesired(client, providerEnvironment, command);
|
|
131
132
|
try {
|
|
132
133
|
const original = await readFile(client.configPath, "utf8");
|
|
133
134
|
const document = client.format === "toml"
|
|
@@ -162,7 +163,7 @@ export const inspectClientConfiguration = async (client, providerEnvironment, co
|
|
|
162
163
|
remediation: "The configuration file could not be read. Check its permissions before rerunning setup.",
|
|
163
164
|
};
|
|
164
165
|
}
|
|
165
|
-
const desired = clientConfigurationDesired(providerEnvironment, command);
|
|
166
|
+
const desired = clientConfigurationDesired(client, providerEnvironment, command);
|
|
166
167
|
try {
|
|
167
168
|
const document = client.format === "toml"
|
|
168
169
|
? objectSchema.parse(parseToml(original))
|
|
@@ -207,11 +208,14 @@ const restoreConfig = async (path, original) => {
|
|
|
207
208
|
// The backup remains available for the remediation reported by setup.
|
|
208
209
|
}
|
|
209
210
|
};
|
|
210
|
-
const clientConfigurationDesired = (providerEnvironment, command) => {
|
|
211
|
+
const clientConfigurationDesired = (client, providerEnvironment, command) => {
|
|
211
212
|
const environment = Object.fromEntries(Object.entries(providerEnvironment).sort(([left], [right]) => left.localeCompare(right)));
|
|
212
213
|
return {
|
|
213
214
|
command: command[0] ?? PRODUCT_IDENTITY.cliBinary,
|
|
214
215
|
args: command.slice(1),
|
|
216
|
+
...(client.name === "codex"
|
|
217
|
+
? { startup_timeout_sec: CODEX_STARTUP_TIMEOUT_SECONDS }
|
|
218
|
+
: {}),
|
|
215
219
|
...(Object.keys(environment).length === 0 ? {} : { env: environment }),
|
|
216
220
|
};
|
|
217
221
|
};
|
|
@@ -48,8 +48,8 @@ const setupPlan = (input) => [
|
|
|
48
48
|
id: "install_skill",
|
|
49
49
|
kind: "install_skill",
|
|
50
50
|
label: "REA reverse-engineering skill",
|
|
51
|
-
target: join(homedir(), ".agents/skills", PRODUCT_IDENTITY.skillName
|
|
52
|
-
detail: "Install or update the bundled REA reverse-engineering skill.",
|
|
51
|
+
target: join(homedir(), ".agents/skills", PRODUCT_IDENTITY.skillName),
|
|
52
|
+
detail: "Install or update the bundled REA reverse-engineering skill and on-demand references.",
|
|
53
53
|
external: false,
|
|
54
54
|
operation: "install",
|
|
55
55
|
},
|
|
@@ -59,7 +59,7 @@ const setupPlan = (input) => [
|
|
|
59
59
|
/** Discover and preflight the complete set of selectable setup actions. */
|
|
60
60
|
export const discoverSetupActions = async (input) => {
|
|
61
61
|
const { host, providerEnvironment, command } = input;
|
|
62
|
-
const initialDoctor = await host.doctor();
|
|
62
|
+
const initialDoctor = await host.doctor(input.doctorScope);
|
|
63
63
|
const linuxHopperRepairNeeded = initialDoctor.checks.some(({ name, ok, detail }) => !ok &&
|
|
64
64
|
(name === "hopper-demo-runtime" ||
|
|
65
65
|
(name === "hopper-version" && detail === "/opt/hopper/bin/Hopper")));
|
|
@@ -112,6 +112,7 @@ export const discoverSetupActions = async (input) => {
|
|
|
112
112
|
: []);
|
|
113
113
|
const linuxDistribution = host.platform === "linux" ? await host.linuxDistribution() : undefined;
|
|
114
114
|
return {
|
|
115
|
+
initialDoctor,
|
|
115
116
|
installHopper,
|
|
116
117
|
installSkill,
|
|
117
118
|
detectedClients: clientPlans.map(({ client }) => client),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Select explicit non-secret permission settings safe for managed MCP registrations. */
|
|
2
|
+
export const registrationPermissionEnvironment = (environment) => {
|
|
3
|
+
const investigationRoots = environment.REA_INVESTIGATION_INPUT_ROOTS_JSON;
|
|
4
|
+
return investigationRoots === undefined
|
|
5
|
+
? {}
|
|
6
|
+
: { REA_INVESTIGATION_INPUT_ROOTS_JSON: investigationRoots };
|
|
7
|
+
};
|
|
8
|
+
/** Select exact non-secret settings published by available providers. */
|
|
9
|
+
export const providerRegistrationEnvironment = (inspections) => Object.fromEntries(inspections
|
|
10
|
+
.filter(({ available }) => available)
|
|
11
|
+
.flatMap(({ registrationEnvironment }) => Object.entries(registrationEnvironment))
|
|
12
|
+
.sort(([left], [right]) => left.localeCompare(right)));
|