rea-agents 2.4.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -19
- package/bridge/ghidra/ReaGhidraBridge.java +524 -4
- package/bridge/hopper_bridge.py +313 -28
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +20 -8
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +84 -14
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +13 -4
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +71 -127
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +11 -3
- package/dist/cli/artifactCommands.js +44 -3
- package/dist/cli/coreAnalysisCommands.js +78 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +9 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +29 -2
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +35 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +8 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +34 -25
- package/dist/contracts/toolEffects.js +25 -0
- package/dist/contracts/toolOutputSchemaGroups.js +120 -30
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +9 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +14 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +13 -16
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +12 -15
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +40 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +93 -99
- package/dist/hopper/HopperProvider.js +61 -30
- package/dist/hopper/HopperRequestQueue.js +215 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +63 -20
- package/dist/main/transport.js +24 -27
- package/dist/main.js +14 -6
- package/dist/mcpDoctor.js +27 -4
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +92 -154
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +28 -7
- package/dist/server/registerEvidenceResources.js +54 -101
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
- package/dist/server/registerReconstructionReadinessResource.js +32 -0
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +32 -58
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +28 -11
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -2,21 +2,22 @@ import { randomBytes, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { access } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { HopperCancelledError, HopperProcessError, HopperProtocolError, HopperStartError, hopperStartupFailure, HopperTimeoutError, } from "../domain/errors.js";
|
|
5
|
+
import { AnalysisCapabilityUnavailableError, HopperCancelledError, HopperProcessError, HopperProtocolError, HopperRemoteError, HopperStartError, hopperStartupFailure, HopperTimeoutError, } from "../domain/errors.js";
|
|
6
6
|
import { err, ok } from "../domain/result.js";
|
|
7
7
|
import { silentLogger } from "../logger.js";
|
|
8
|
-
import { PendingOperations } from "../process/PendingOperations.js";
|
|
9
8
|
import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js";
|
|
10
9
|
import { ProviderStartupDeadline } from "../process/ProviderDeadline.js";
|
|
11
10
|
import { ProviderRunLineage } from "../process/ProviderRunLineage.js";
|
|
12
11
|
import { ProviderProcessSupervisor, } from "../process/ProviderProcess.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { cleanupHopperSession } from "./HopperCleanup.js";
|
|
13
|
+
import { parseHopperServerInfo, } from "./HopperSessionValues.js";
|
|
15
14
|
import { connectHopperSocketOnce } from "./HopperSocketConnection.js";
|
|
16
15
|
import { hopperLauncherFailureDiagnostic } from "./HopperProcessDiagnostic.js";
|
|
17
|
-
import {
|
|
18
|
-
|
|
16
|
+
import { HopperRequestQueue, } from "./HopperRequestQueue.js";
|
|
17
|
+
import { HopperResponseStream } from "./HopperResponseStream.js";
|
|
18
|
+
import { responseResult, } from "./protocol.js";
|
|
19
19
|
const SHUTDOWN_TIMEOUT_MS = 30_000;
|
|
20
|
+
const MAX_QUEUED_REQUESTS = 64;
|
|
20
21
|
const SESSION_ROOT = process.platform === "darwin" ? "/tmp" : tmpdir();
|
|
21
22
|
/** Safe launcher telemetry; stderr content is intentionally never exposed. */
|
|
22
23
|
/**
|
|
@@ -29,7 +30,8 @@ const SESSION_ROOT = process.platform === "darwin" ? "/tmp" : tmpdir();
|
|
|
29
30
|
*/
|
|
30
31
|
export class HopperClient {
|
|
31
32
|
#options;
|
|
32
|
-
#
|
|
33
|
+
#requests;
|
|
34
|
+
#responses;
|
|
33
35
|
#logger;
|
|
34
36
|
#socket;
|
|
35
37
|
#launch;
|
|
@@ -38,7 +40,6 @@ export class HopperClient {
|
|
|
38
40
|
#token;
|
|
39
41
|
#lineage = new ProviderRunLineage();
|
|
40
42
|
#nextId = 1;
|
|
41
|
-
#buffer = "";
|
|
42
43
|
#closing = false;
|
|
43
44
|
#launcherExitCode;
|
|
44
45
|
#launcherFailureDiagnostic;
|
|
@@ -46,7 +47,7 @@ export class HopperClient {
|
|
|
46
47
|
#startPromise;
|
|
47
48
|
#closePromise;
|
|
48
49
|
#onSocketData = (chunk) => {
|
|
49
|
-
this.#
|
|
50
|
+
this.#responses.push(chunk);
|
|
50
51
|
};
|
|
51
52
|
#onSocketError = () => {
|
|
52
53
|
this.#failAll(new HopperProcessError(null));
|
|
@@ -62,11 +63,33 @@ export class HopperClient {
|
|
|
62
63
|
requestTimeoutMs: options.requestTimeoutMs ?? 30_000,
|
|
63
64
|
startupTimeoutMs: options.startupTimeoutMs ?? 120_000,
|
|
64
65
|
};
|
|
66
|
+
this.#requests = new HopperRequestQueue(MAX_QUEUED_REQUESTS, ({ id, method, params }, failed) => {
|
|
67
|
+
const socket = this.#socket;
|
|
68
|
+
const token = this.#token;
|
|
69
|
+
if (socket === undefined || socket.destroyed || token === undefined) {
|
|
70
|
+
failed();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
socket.write(`${JSON.stringify({ id, token, method, params })}\n`, (cause) => {
|
|
74
|
+
if (cause !== undefined && cause !== null)
|
|
75
|
+
failed();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
this.#responses = new HopperResponseStream({
|
|
79
|
+
accept: (message) => this.#acceptBridgeMessage(message),
|
|
80
|
+
hasQueued: (id) => this.#requests.hasQueued(id),
|
|
81
|
+
nextRequestId: () => this.#nextId,
|
|
82
|
+
abort: (message, cause) => this.#abortProtocol(message, cause),
|
|
83
|
+
});
|
|
65
84
|
}
|
|
66
85
|
/** Latest token-verified launcher lineage for the active run. */
|
|
67
86
|
runtimeLineage() {
|
|
68
87
|
return this.#lineage.snapshot();
|
|
69
88
|
}
|
|
89
|
+
/** Observe a request that still occupies Hopper after caller timeout/cancel. */
|
|
90
|
+
requestActivity() {
|
|
91
|
+
return this.#requests.activity();
|
|
92
|
+
}
|
|
70
93
|
/** Launch the bridge once and complete its authenticated health handshake. */
|
|
71
94
|
start(signal) {
|
|
72
95
|
if (this.#closePromise !== undefined) {
|
|
@@ -186,20 +209,29 @@ export class HopperClient {
|
|
|
186
209
|
const started = await this.start(options.signal);
|
|
187
210
|
if (!started.ok)
|
|
188
211
|
return started;
|
|
189
|
-
|
|
212
|
+
const result = await this.#request(name, arguments_, options);
|
|
213
|
+
return !result.ok &&
|
|
214
|
+
result.error instanceof HopperRemoteError &&
|
|
215
|
+
result.error.diagnosticType === "capability_unavailable"
|
|
216
|
+
? err(new AnalysisCapabilityUnavailableError("hopper", name, result.error.safeMessage))
|
|
217
|
+
: result;
|
|
190
218
|
}
|
|
191
219
|
/** Stop the bridge, settle outstanding requests, and remove session artifacts. */
|
|
192
220
|
close() {
|
|
221
|
+
return this.closeWithOutcome().then(() => undefined);
|
|
222
|
+
}
|
|
223
|
+
/** Stop the bridge and report whether every owned resource was verified clean. */
|
|
224
|
+
closeWithOutcome(options = {}) {
|
|
193
225
|
const starting = this.#startPromise;
|
|
194
226
|
const controller = this.#startupController;
|
|
195
227
|
controller?.abort();
|
|
196
|
-
this.#closePromise ??= Promise.resolve().then(() => this.#close(starting, controller));
|
|
228
|
+
this.#closePromise ??= Promise.resolve().then(() => this.#close(starting, controller, options));
|
|
197
229
|
return this.#closePromise;
|
|
198
230
|
}
|
|
199
|
-
async #close(starting, controller) {
|
|
231
|
+
async #close(starting, controller, options) {
|
|
200
232
|
try {
|
|
201
233
|
await starting?.catch(() => undefined);
|
|
202
|
-
await this.#cleanup();
|
|
234
|
+
return await this.#cleanup(options);
|
|
203
235
|
}
|
|
204
236
|
finally {
|
|
205
237
|
if (this.#startPromise === starting)
|
|
@@ -210,63 +242,39 @@ export class HopperClient {
|
|
|
210
242
|
this.#closePromise = undefined;
|
|
211
243
|
}
|
|
212
244
|
}
|
|
213
|
-
async #cleanup() {
|
|
245
|
+
async #cleanup(options = {}) {
|
|
214
246
|
this.#closing = true;
|
|
215
247
|
try {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
!isHopperShutdownAcknowledgement(shutdown.value))
|
|
236
|
-
this.#logger.warn({ status: shutdown.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown was not confirmed");
|
|
237
|
-
}
|
|
238
|
-
this.#failAll(new HopperProcessError(null));
|
|
239
|
-
if (socket !== undefined)
|
|
240
|
-
this.#detachSocket(socket);
|
|
241
|
-
socket?.destroy();
|
|
248
|
+
return await cleanupHopperSession({
|
|
249
|
+
socket: this.#socket,
|
|
250
|
+
launch: this.#launch,
|
|
251
|
+
processSupervisor: this.#process,
|
|
252
|
+
runtimeRoot: this.#runtimeRoot,
|
|
253
|
+
activeRequest: this.#requests.activity(),
|
|
254
|
+
progress: options.progress,
|
|
255
|
+
logger: this.#logger,
|
|
256
|
+
onDiagnostic: this.#options.onDiagnostic,
|
|
257
|
+
request: (method) => this.#request(method, {}, { timeoutMs: SHUTDOWN_TIMEOUT_MS }),
|
|
258
|
+
releaseTransport: (socket) => {
|
|
259
|
+
this.#failAll(new HopperProcessError(null));
|
|
260
|
+
if (socket !== undefined)
|
|
261
|
+
this.#detachSocket(socket);
|
|
262
|
+
socket?.destroy();
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
242
267
|
this.#socket = undefined;
|
|
243
|
-
if (this.#process !== undefined) {
|
|
244
|
-
const stopped = await this.#process.stop(cleanupResult === undefined ? {} : { cleanupResult });
|
|
245
|
-
const diagnostic = createOwnedHopperShutdownDiagnostic(this.#process.launch, stopped, cleanupResult);
|
|
246
|
-
this.#options.onDiagnostic?.(diagnostic);
|
|
247
|
-
this.#logger.info(diagnostic, "Owned Hopper launcher shutdown completed");
|
|
248
|
-
if (stopped.status === "incomplete")
|
|
249
|
-
this.#logger.warn({ reason: stopped.reason }, "Owned launcher cleanup failed closed");
|
|
250
|
-
}
|
|
251
268
|
this.#process = undefined;
|
|
252
269
|
this.#launch = undefined;
|
|
253
|
-
const runtimeRoot = this.#runtimeRoot;
|
|
254
270
|
this.#runtimeRoot = undefined;
|
|
255
|
-
await runtimeRoot?.close();
|
|
256
271
|
this.#token = undefined;
|
|
257
|
-
this.#
|
|
272
|
+
this.#responses.reset();
|
|
258
273
|
this.#launcherExitCode = undefined;
|
|
259
274
|
this.#launcherFailureDiagnostic = undefined;
|
|
260
|
-
}
|
|
261
|
-
finally {
|
|
262
275
|
this.#closing = false;
|
|
263
276
|
}
|
|
264
277
|
}
|
|
265
|
-
async #fallbackDocumentShutdown() {
|
|
266
|
-
const fallback = await this.#request("shutdown_document", {}, { timeoutMs: SHUTDOWN_TIMEOUT_MS }).catch(() => err(new HopperProcessError(null)));
|
|
267
|
-
if (!fallback.ok || !isHopperShutdownAcknowledgement(fallback.value))
|
|
268
|
-
this.#logger.warn({ status: fallback.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown fallback was not confirmed");
|
|
269
|
-
}
|
|
270
278
|
async #connect(socketPath, deadline) {
|
|
271
279
|
while (deadline.remainingMs() > 0) {
|
|
272
280
|
if (deadline.signal.aborted)
|
|
@@ -306,17 +314,11 @@ export class HopperClient {
|
|
|
306
314
|
const id = this.#nextId++;
|
|
307
315
|
const startedAt = performance.now();
|
|
308
316
|
const timeoutMs = options.timeoutMs ?? this.#options.requestTimeoutMs;
|
|
309
|
-
const
|
|
317
|
+
const result = await this.#requests.run(id, method, params, {
|
|
310
318
|
timeoutMs,
|
|
311
319
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
312
|
-
|
|
313
|
-
cancelledValue: () => err(new HopperCancelledError()),
|
|
320
|
+
...(options.progress !== undefined ? { progress: options.progress } : {}),
|
|
314
321
|
});
|
|
315
|
-
socket.write(`${JSON.stringify({ id, token, method, params })}\n`, (cause) => {
|
|
316
|
-
if (cause !== undefined && cause !== null)
|
|
317
|
-
this.#pending.settle(id, err(new HopperProcessError(null)));
|
|
318
|
-
});
|
|
319
|
-
const result = await response;
|
|
320
322
|
this.#logger[result.ok ? "debug" : "warn"]({
|
|
321
323
|
method,
|
|
322
324
|
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
@@ -331,6 +333,30 @@ export class HopperClient {
|
|
|
331
333
|
socket.on("error", this.#onSocketError);
|
|
332
334
|
socket.on("close", this.#onSocketClose);
|
|
333
335
|
}
|
|
336
|
+
#acceptBridgeMessage(message) {
|
|
337
|
+
if (!("event" in message))
|
|
338
|
+
return this.#requests.accept(message.id, responseResult(message));
|
|
339
|
+
if (!this.#requests.acceptEvent(message))
|
|
340
|
+
return false;
|
|
341
|
+
this.#emitBridgeDiagnostic(message);
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
#emitBridgeDiagnostic(message) {
|
|
345
|
+
if (message.event.type !== "diagnostic")
|
|
346
|
+
return;
|
|
347
|
+
try {
|
|
348
|
+
this.#options.onDiagnostic?.({
|
|
349
|
+
type: "bridge-diagnostic",
|
|
350
|
+
request_id: message.id,
|
|
351
|
+
code: message.event.error.code,
|
|
352
|
+
category: message.event.error.type,
|
|
353
|
+
message: message.event.error.message,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
this.#logger.warn({ requestId: message.id }, "Hopper bridge diagnostic consumer rejected an event");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
334
360
|
#detachSocket(socket) {
|
|
335
361
|
socket.off("data", this.#onSocketData);
|
|
336
362
|
socket.off("error", this.#onSocketError);
|
|
@@ -364,44 +390,12 @@ export class HopperClient {
|
|
|
364
390
|
if (launch.ownsProcessLifetime && !this.#closing)
|
|
365
391
|
this.#failAll(new HopperProcessError(event.code, this.#launcherFailureDiagnostic));
|
|
366
392
|
}
|
|
367
|
-
#onData(chunk) {
|
|
368
|
-
this.#buffer += chunk;
|
|
369
|
-
let newline = this.#buffer.indexOf("\n");
|
|
370
|
-
while (newline >= 0) {
|
|
371
|
-
const line = this.#buffer.slice(0, newline).trim();
|
|
372
|
-
this.#buffer = this.#buffer.slice(newline + 1);
|
|
373
|
-
if (Buffer.byteLength(line) > MAX_LINE_BYTES) {
|
|
374
|
-
this.#abortProtocol("Hopper response exceeded the maximum line size");
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
if (line.length > 0)
|
|
378
|
-
this.#onLine(line);
|
|
379
|
-
newline = this.#buffer.indexOf("\n");
|
|
380
|
-
}
|
|
381
|
-
if (Buffer.byteLength(this.#buffer) > MAX_LINE_BYTES) {
|
|
382
|
-
this.#abortProtocol("Hopper response exceeded the maximum line size");
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
#onLine(line) {
|
|
386
|
-
const parsed = parseResponseLine(line);
|
|
387
|
-
if (!parsed.ok) {
|
|
388
|
-
this.#abortProtocol(parsed.error.message, parsed.error);
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
if (!this.#pending.has(parsed.value.id)) {
|
|
392
|
-
if (parsed.value.id >= this.#nextId) {
|
|
393
|
-
this.#abortProtocol("Hopper returned an unknown response id");
|
|
394
|
-
}
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
this.#pending.settle(parsed.value.id, responseResult(parsed.value));
|
|
398
|
-
}
|
|
399
393
|
#abortProtocol(message, cause) {
|
|
400
394
|
this.#failAll(new HopperProtocolError(message, { cause }));
|
|
401
395
|
this.#socket?.destroy();
|
|
402
396
|
}
|
|
403
397
|
#failAll(error) {
|
|
404
|
-
this.#
|
|
398
|
+
this.#requests.failAll(error);
|
|
405
399
|
}
|
|
406
400
|
}
|
|
407
401
|
const isAborted = (signal) => signal?.aborted === true;
|
|
@@ -3,7 +3,7 @@ import { accessSync, constants } from "node:fs";
|
|
|
3
3
|
import { createAnalysisExecution } from "../application/AnalysisProvider.js";
|
|
4
4
|
import { AnalysisCapabilityUnavailableError } from "../domain/errors.js";
|
|
5
5
|
import { err } from "../domain/result.js";
|
|
6
|
-
import {
|
|
6
|
+
import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
|
|
7
7
|
import { HopperApplicationLauncher } from "./BridgeLauncher.js";
|
|
8
8
|
import { hopperLoaderArgsForTarget, resolveHopperAnalysisProfile, } from "./HopperAnalysisProfile.js";
|
|
9
9
|
import { HopperClient } from "./HopperClient.js";
|
|
@@ -24,36 +24,47 @@ const MUTATING_OPERATIONS = new Set([
|
|
|
24
24
|
]);
|
|
25
25
|
/** Tool contracts implemented directly by the Hopper adapter. */
|
|
26
26
|
export const HOPPER_PROVIDER_TOOL_CONTRACTS = Object.freeze([
|
|
27
|
-
...
|
|
28
|
-
...
|
|
27
|
+
...GENERATED_MCP_TOOL_CATALOG.filter(({ kind }) => kind === "official-proxy"),
|
|
28
|
+
...GENERATED_MCP_TOOL_CATALOG.filter(({ name }) => name === "analyze_function"),
|
|
29
29
|
]);
|
|
30
|
-
const CAPABILITIES = Object.freeze(HOPPER_PROVIDER_TOOL_CONTRACTS.map((contract) =>
|
|
31
|
-
|
|
32
|
-
operation
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
const CAPABILITIES = Object.freeze(HOPPER_PROVIDER_TOOL_CONTRACTS.map((contract) => {
|
|
31
|
+
const operation = contract.analysisOperation;
|
|
32
|
+
if (operation === null)
|
|
33
|
+
throw new TypeError(`Missing analysis operation for ${contract.name}`);
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
provider: IDENTITY,
|
|
36
|
+
operation,
|
|
37
|
+
inputContractVersion: 1,
|
|
38
|
+
outputContractVersion: 1,
|
|
39
|
+
available: true,
|
|
40
|
+
reason: null,
|
|
41
|
+
pagination: isRecord(contract.inputSchema.properties) &&
|
|
42
|
+
"offset" in contract.inputSchema.properties
|
|
43
|
+
? "offset"
|
|
44
|
+
: "none",
|
|
45
|
+
exhaustive: false,
|
|
46
|
+
effects: Object.freeze({
|
|
47
|
+
mutatesArtifact: MUTATING_OPERATIONS.has(contract.name),
|
|
48
|
+
launchesProcess: true,
|
|
49
|
+
mayShowUi: true,
|
|
50
|
+
mayAccessNetwork: false,
|
|
51
|
+
mayWriteFilesystem: MUTATING_OPERATIONS.has(contract.name),
|
|
52
|
+
changesPermissions: false,
|
|
53
|
+
requiresRoot: false,
|
|
54
|
+
}),
|
|
55
|
+
limits: Object.freeze({
|
|
56
|
+
maxResults: null,
|
|
57
|
+
maxPayloadBytes: contract.name === "read_bytes" ? 4_096 : null,
|
|
58
|
+
timeoutMs: null,
|
|
59
|
+
}),
|
|
60
|
+
limitations: Object.freeze([
|
|
61
|
+
"Results depend on Hopper's completed static analysis.",
|
|
62
|
+
]),
|
|
63
|
+
});
|
|
64
|
+
}));
|
|
65
|
+
function isRecord(value) {
|
|
66
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67
|
+
}
|
|
57
68
|
/** Concrete analysis provider backed by REA's private Hopper bridge. */
|
|
58
69
|
export class HopperProvider {
|
|
59
70
|
config;
|
|
@@ -178,6 +189,7 @@ export class HopperProvider {
|
|
|
178
189
|
}),
|
|
179
190
|
...(context === undefined ? {} : { runId: context.runId }),
|
|
180
191
|
logger: this.logger.child({ layer: "bridge" }),
|
|
192
|
+
onDiagnostic: (diagnostic) => this.logger.info(diagnostic, "Hopper bridge reported a diagnostic"),
|
|
181
193
|
});
|
|
182
194
|
return {
|
|
183
195
|
execute: async (operation, parameters, options) => {
|
|
@@ -200,6 +212,25 @@ export class HopperProvider {
|
|
|
200
212
|
? []
|
|
201
213
|
: [{ provider: executionProvider, observation }];
|
|
202
214
|
},
|
|
215
|
+
requestActivitySnapshots: () => {
|
|
216
|
+
const activity = client.requestActivity();
|
|
217
|
+
return [
|
|
218
|
+
{
|
|
219
|
+
provider: executionProvider,
|
|
220
|
+
active: activity === null
|
|
221
|
+
? null
|
|
222
|
+
: {
|
|
223
|
+
requestId: activity.requestId,
|
|
224
|
+
operation: activity.operation,
|
|
225
|
+
elapsedMs: activity.elapsedMs,
|
|
226
|
+
timeoutMs: activity.timeoutMs,
|
|
227
|
+
callerState: activity.callerState,
|
|
228
|
+
},
|
|
229
|
+
queuedRequests: activity?.queuedRequests ?? 0,
|
|
230
|
+
},
|
|
231
|
+
];
|
|
232
|
+
},
|
|
233
|
+
closeWithOutcome: (options) => client.closeWithOutcome(options),
|
|
203
234
|
close: () => client.close(),
|
|
204
235
|
};
|
|
205
236
|
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { HopperCancelledError, HopperProcessError, HopperProtocolError, HopperTimeoutError, } from "../domain/errors.js";
|
|
3
|
+
import { err } from "../domain/result.js";
|
|
4
|
+
/** Bounded FIFO that keeps the wire serialized until Hopper actually replies. */
|
|
5
|
+
export class HopperRequestQueue {
|
|
6
|
+
maximum;
|
|
7
|
+
send;
|
|
8
|
+
#queue = [];
|
|
9
|
+
#active;
|
|
10
|
+
constructor(maximum, send) {
|
|
11
|
+
this.maximum = maximum;
|
|
12
|
+
this.send = send;
|
|
13
|
+
}
|
|
14
|
+
/** Queue one request, counting queue wait against its caller deadline. */
|
|
15
|
+
run(id, method, params, options) {
|
|
16
|
+
if (options.signal?.aborted === true)
|
|
17
|
+
return Promise.resolve(err(new HopperCancelledError()));
|
|
18
|
+
if (options.timeoutMs <= 0)
|
|
19
|
+
return Promise.resolve(err(new HopperTimeoutError(options.timeoutMs)));
|
|
20
|
+
if (this.#size() >= this.maximum)
|
|
21
|
+
return Promise.resolve(err(new HopperProtocolError(`Hopper serial request queue reached its ${String(this.maximum)}-request limit`)));
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
let entry;
|
|
24
|
+
const onAbort = options.signal === undefined ? undefined : () => this.#cancel(entry);
|
|
25
|
+
const timer = setTimeout(() => this.#timeout(entry), options.timeoutMs);
|
|
26
|
+
entry = {
|
|
27
|
+
id,
|
|
28
|
+
method,
|
|
29
|
+
params,
|
|
30
|
+
signal: options.signal,
|
|
31
|
+
timeoutMs: options.timeoutMs,
|
|
32
|
+
deadline: performance.now() + options.timeoutMs,
|
|
33
|
+
progress: options.progress,
|
|
34
|
+
resolve,
|
|
35
|
+
timer,
|
|
36
|
+
onAbort,
|
|
37
|
+
callerState: "waiting",
|
|
38
|
+
callerSettled: false,
|
|
39
|
+
startedAt: undefined,
|
|
40
|
+
heartbeat: undefined,
|
|
41
|
+
};
|
|
42
|
+
this.#queue.push(entry);
|
|
43
|
+
if (onAbort !== undefined)
|
|
44
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
45
|
+
if (this.#active !== undefined || this.#queue.length > 1)
|
|
46
|
+
this.#report(entry, `${method} queued behind ${String(this.#size() - 1)} Hopper request(s)`);
|
|
47
|
+
this.#drain();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Settle the active wire request. False identifies an unexpected response id. */
|
|
51
|
+
accept(id, result) {
|
|
52
|
+
const entry = this.#active;
|
|
53
|
+
if (entry === undefined || entry.id !== id)
|
|
54
|
+
return false;
|
|
55
|
+
this.#releaseWire(entry);
|
|
56
|
+
if (!entry.callerSettled)
|
|
57
|
+
this.#settleCaller(entry, result, "waiting");
|
|
58
|
+
this.#active = undefined;
|
|
59
|
+
this.#drain();
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/** Forward one correlated bridge event without releasing the wire request. */
|
|
63
|
+
acceptEvent(event) {
|
|
64
|
+
const entry = this.#active;
|
|
65
|
+
if (entry === undefined || entry.id !== event.id)
|
|
66
|
+
return false;
|
|
67
|
+
if (event.event.type === "progress" && !entry.callerSettled) {
|
|
68
|
+
if (event.event.terminal === true && entry.heartbeat !== undefined) {
|
|
69
|
+
clearInterval(entry.heartbeat);
|
|
70
|
+
entry.heartbeat = undefined;
|
|
71
|
+
}
|
|
72
|
+
this.#reportUpdate(entry, {
|
|
73
|
+
phase: event.event.phase,
|
|
74
|
+
completed: event.event.completed,
|
|
75
|
+
total: event.event.total,
|
|
76
|
+
message: event.event.message,
|
|
77
|
+
...(event.event.terminal === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { terminal: event.event.terminal }),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
/** Fail the active wire request and every request still waiting in the FIFO. */
|
|
85
|
+
failAll(error) {
|
|
86
|
+
const active = this.#active;
|
|
87
|
+
this.#active = undefined;
|
|
88
|
+
if (active !== undefined) {
|
|
89
|
+
this.#releaseWire(active);
|
|
90
|
+
if (!active.callerSettled)
|
|
91
|
+
this.#settleCaller(active, err(error), "waiting");
|
|
92
|
+
}
|
|
93
|
+
for (const entry of this.#queue.splice(0))
|
|
94
|
+
this.#settleCaller(entry, err(error), "waiting");
|
|
95
|
+
}
|
|
96
|
+
/** Snapshot the one request still occupying Hopper's serial Python thread. */
|
|
97
|
+
activity() {
|
|
98
|
+
const active = this.#active;
|
|
99
|
+
if (active === undefined || active.startedAt === undefined)
|
|
100
|
+
return null;
|
|
101
|
+
return {
|
|
102
|
+
requestId: active.id,
|
|
103
|
+
operation: active.method,
|
|
104
|
+
elapsedMs: Math.max(0, Math.round(performance.now() - active.startedAt)),
|
|
105
|
+
timeoutMs: active.timeoutMs,
|
|
106
|
+
callerState: active.callerState,
|
|
107
|
+
queuedRequests: this.#queue.length,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Return whether an id belongs to a request that has not crossed the wire. */
|
|
111
|
+
hasQueued(id) {
|
|
112
|
+
return this.#queue.some((entry) => entry.id === id);
|
|
113
|
+
}
|
|
114
|
+
/** Number of caller requests waiting behind the active bridge operation. */
|
|
115
|
+
queuedCount() {
|
|
116
|
+
return this.#queue.length;
|
|
117
|
+
}
|
|
118
|
+
#size() {
|
|
119
|
+
return this.#queue.length + (this.#active === undefined ? 0 : 1);
|
|
120
|
+
}
|
|
121
|
+
#drain() {
|
|
122
|
+
if (this.#active !== undefined)
|
|
123
|
+
return;
|
|
124
|
+
const entry = this.#queue.shift();
|
|
125
|
+
if (entry === undefined)
|
|
126
|
+
return;
|
|
127
|
+
const remaining = entry.deadline - performance.now();
|
|
128
|
+
if (entry.signal?.aborted === true || remaining <= 0) {
|
|
129
|
+
this.#settleCaller(entry, entry.signal?.aborted === true
|
|
130
|
+
? err(new HopperCancelledError())
|
|
131
|
+
: err(new HopperTimeoutError(entry.timeoutMs)), entry.signal?.aborted === true ? "cancelled" : "timed_out");
|
|
132
|
+
this.#drain();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.#active = entry;
|
|
136
|
+
entry.startedAt = performance.now();
|
|
137
|
+
this.#report(entry, `${entry.method} started on Hopper's serial bridge`);
|
|
138
|
+
entry.heartbeat = setInterval(() => {
|
|
139
|
+
if (entry.callerSettled || entry.startedAt === undefined)
|
|
140
|
+
return;
|
|
141
|
+
const elapsed = Math.round(performance.now() - entry.startedAt);
|
|
142
|
+
this.#report(entry, `${entry.method} is still running in Hopper (${String(elapsed)} ms elapsed)`);
|
|
143
|
+
}, 1_000);
|
|
144
|
+
try {
|
|
145
|
+
this.send({ id: entry.id, method: entry.method, params: entry.params }, () => this.accept(entry.id, err(new HopperProcessError(null))));
|
|
146
|
+
}
|
|
147
|
+
catch (cause) {
|
|
148
|
+
this.accept(entry.id, err(new HopperProtocolError("Hopper socket write failed", { cause })));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
#timeout(entry) {
|
|
152
|
+
if (entry.callerSettled)
|
|
153
|
+
return;
|
|
154
|
+
if (this.#active === entry) {
|
|
155
|
+
this.#settleCaller(entry, err(new HopperTimeoutError(entry.timeoutMs)), "timed_out");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const index = this.#queue.indexOf(entry);
|
|
159
|
+
if (index < 0)
|
|
160
|
+
return;
|
|
161
|
+
this.#queue.splice(index, 1);
|
|
162
|
+
this.#settleCaller(entry, err(new HopperTimeoutError(entry.timeoutMs)), "timed_out");
|
|
163
|
+
}
|
|
164
|
+
#cancel(entry) {
|
|
165
|
+
if (entry.callerSettled)
|
|
166
|
+
return;
|
|
167
|
+
if (this.#active === entry) {
|
|
168
|
+
this.#settleCaller(entry, err(new HopperCancelledError()), "cancelled");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const index = this.#queue.indexOf(entry);
|
|
172
|
+
if (index < 0)
|
|
173
|
+
return;
|
|
174
|
+
this.#queue.splice(index, 1);
|
|
175
|
+
this.#settleCaller(entry, err(new HopperCancelledError()), "cancelled");
|
|
176
|
+
}
|
|
177
|
+
#settleCaller(entry, result, callerState) {
|
|
178
|
+
if (entry.callerSettled)
|
|
179
|
+
return;
|
|
180
|
+
entry.callerSettled = true;
|
|
181
|
+
entry.callerState = callerState;
|
|
182
|
+
if (this.#active === entry && entry.heartbeat !== undefined) {
|
|
183
|
+
clearInterval(entry.heartbeat);
|
|
184
|
+
entry.heartbeat = undefined;
|
|
185
|
+
}
|
|
186
|
+
clearTimeout(entry.timer);
|
|
187
|
+
if (entry.signal !== undefined && entry.onAbort !== undefined)
|
|
188
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
189
|
+
entry.resolve(result);
|
|
190
|
+
}
|
|
191
|
+
#releaseWire(entry) {
|
|
192
|
+
if (entry.heartbeat !== undefined)
|
|
193
|
+
clearInterval(entry.heartbeat);
|
|
194
|
+
entry.heartbeat = undefined;
|
|
195
|
+
clearTimeout(entry.timer);
|
|
196
|
+
if (entry.signal !== undefined && entry.onAbort !== undefined)
|
|
197
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
198
|
+
}
|
|
199
|
+
#report(entry, message) {
|
|
200
|
+
this.#reportUpdate(entry, {
|
|
201
|
+
phase: "hopper_request",
|
|
202
|
+
completed: 0,
|
|
203
|
+
total: 1,
|
|
204
|
+
message,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
#reportUpdate(entry, update) {
|
|
208
|
+
try {
|
|
209
|
+
void Promise.resolve(entry.progress?.report(update)).catch(() => undefined);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Progress observation cannot change the request outcome.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|