@zktx.io/sui-mcp 0.5.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/LICENSE +21 -0
- package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
- package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
- package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
- package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
- package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
- package/README.md +288 -0
- package/dist/adapters/adapterLifecycleValidators.js +5 -0
- package/dist/adapters/adapterPromptSurfaces.js +60 -0
- package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
- package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
- package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
- package/dist/adapters/intentPlanFactories.js +51 -0
- package/dist/adapters/reviewAdapters.js +47 -0
- package/dist/core/action/adapterLifecycleValidation.js +12 -0
- package/dist/core/action/contractNameRegistry.js +151 -0
- package/dist/core/action/forbiddenFields.js +43 -0
- package/dist/core/action/humanReadableReviewEvidence.js +193 -0
- package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
- package/dist/core/action/ptbVisualizationProducer.js +71 -0
- package/dist/core/action/receiptPtbGraph.js +29 -0
- package/dist/core/action/reviewCheckResults.js +6 -0
- package/dist/core/action/reviewStateValidation.js +11 -0
- package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
- package/dist/core/action/schemas.js +491 -0
- package/dist/core/action/signableAdapterContract.js +998 -0
- package/dist/core/action/suiChainReceiptEvidence.js +106 -0
- package/dist/core/action/suiChainReceiptMapping.js +107 -0
- package/dist/core/action/suiChainReceiptReader.js +211 -0
- package/dist/core/action/suiChainReceiptVerifier.js +178 -0
- package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
- package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
- package/dist/core/action/transactionFunding.js +244 -0
- package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
- package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
- package/dist/core/action/types.js +33 -0
- package/dist/core/action/walletReviewContractAssembler.js +286 -0
- package/dist/core/activity/accountAssetTimeline.js +157 -0
- package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
- package/dist/core/activity/activityStore.js +16 -0
- package/dist/core/activity/externalActivityCoverage.js +76 -0
- package/dist/core/activity/externalActivityTransactionStream.js +82 -0
- package/dist/core/activity/localDataService.js +296 -0
- package/dist/core/activity/localDataTypes.js +11 -0
- package/dist/core/activity/localDataValidation.js +439 -0
- package/dist/core/activity/schemaVersion.js +2 -0
- package/dist/core/activity/sqliteActivityStore.js +1068 -0
- package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
- package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
- package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
- package/dist/core/activity/suiFunctionTarget.js +43 -0
- package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
- package/dist/core/activity/transactionActivityAnalysis.js +295 -0
- package/dist/core/activity/transactionActivityClassifier.js +306 -0
- package/dist/core/activity/transactionActivityDetails.js +229 -0
- package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
- package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
- package/dist/core/activity/transactionActivityService.js +379 -0
- package/dist/core/activity/transactionActivityTypes.js +18 -0
- package/dist/core/eventlog/sink.js +35 -0
- package/dist/core/evidence/settlementFamilies.js +85 -0
- package/dist/core/evidence/userAnswerUse.js +1 -0
- package/dist/core/numeric/rawU64.js +63 -0
- package/dist/core/preferences/preferencesStore.js +26 -0
- package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
- package/dist/core/proposal/externalProposalReview.js +347 -0
- package/dist/core/proposal/schemas.js +208 -0
- package/dist/core/proposal/types.js +35 -0
- package/dist/core/read/amounts.js +14 -0
- package/dist/core/read/coinMetadata.js +60 -0
- package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
- package/dist/core/read/deepbookRawQuoteClient.js +86 -0
- package/dist/core/read/deepbookReadHelpers.js +321 -0
- package/dist/core/read/deepbookRegistry.js +157 -0
- package/dist/core/read/deepbookSourceOwners.js +159 -0
- package/dist/core/read/deepbookUsdcChartService.js +342 -0
- package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
- package/dist/core/read/readCardInputs.js +19 -0
- package/dist/core/read/readCardResult.js +40 -0
- package/dist/core/read/readResponseGuidance.js +402 -0
- package/dist/core/read/readService.js +1639 -0
- package/dist/core/read/readServiceTypes.js +88 -0
- package/dist/core/read/settlementParityFormatting.js +82 -0
- package/dist/core/read/walletReadHelpers.js +99 -0
- package/dist/core/review/reviewChecks.js +45 -0
- package/dist/core/review/reviewComputation.js +38 -0
- package/dist/core/review/reviewComputationResult.js +87 -0
- package/dist/core/session/cardSession.js +31 -0
- package/dist/core/session/cardSessionStore.js +204 -0
- package/dist/core/session/keyedRecordStore.js +18 -0
- package/dist/core/session/liveReviewSessionContract.js +3 -0
- package/dist/core/session/localSession.js +31 -0
- package/dist/core/session/privateReviewArtifacts.js +75 -0
- package/dist/core/session/reviewValidity.js +49 -0
- package/dist/core/session/sessionErrors.js +9 -0
- package/dist/core/session/sessionRecordStore.js +40 -0
- package/dist/core/session/sessionStore.js +655 -0
- package/dist/core/session/settingsSession.js +1 -0
- package/dist/core/session/settingsSessions.js +45 -0
- package/dist/core/session/sqliteCardStore.js +88 -0
- package/dist/core/session/sqliteSessionStore.js +213 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
- package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
- package/dist/core/session/status.js +60 -0
- package/dist/core/session/transactionMaterialStore.js +221 -0
- package/dist/core/session/transactionRequest.js +72 -0
- package/dist/core/session/wait.js +79 -0
- package/dist/core/session/walletConnection.js +92 -0
- package/dist/core/session/walletRuntime.js +38 -0
- package/dist/core/session/walletWorkflow.js +608 -0
- package/dist/core/session/workflowState.js +80 -0
- package/dist/core/session/workflowView.js +150 -0
- package/dist/core/suiAddress.js +18 -0
- package/dist/core/suiEndpoint.js +72 -0
- package/dist/mcp/activeAccountResponse.js +24 -0
- package/dist/mcp/prompts.js +146 -0
- package/dist/mcp/registerTool.js +19 -0
- package/dist/mcp/resources.js +72 -0
- package/dist/mcp/responseGuidance.js +326 -0
- package/dist/mcp/result.js +17 -0
- package/dist/mcp/schemas.js +11 -0
- package/dist/mcp/server.js +32 -0
- package/dist/mcp/serverInfo.js +131 -0
- package/dist/mcp/toolErrors.js +118 -0
- package/dist/mcp/toolNames.js +58 -0
- package/dist/mcp/tools/account/index.js +44 -0
- package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
- package/dist/mcp/tools/read/commonSchemas.js +43 -0
- package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
- package/dist/mcp/tools/read/index.js +14 -0
- package/dist/mcp/tools/read/readToolHelpers.js +97 -0
- package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
- package/dist/mcp/tools/read/serverStatusTools.js +100 -0
- package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
- package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
- package/dist/mcp/tools/read/walletReadTools.js +734 -0
- package/dist/mcp/tools/session/executionResultTools.js +44 -0
- package/dist/mcp/tools/session/index.js +8 -0
- package/dist/mcp/tools/session/shared.js +36 -0
- package/dist/mcp/tools/session/statusTools.js +69 -0
- package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
- package/dist/mcp/tools/settings/index.js +66 -0
- package/dist/mcp-app/account.html +3133 -0
- package/dist/mcp-app/account.notices.txt +973 -0
- package/dist/mcp-app/chart.html +3354 -0
- package/dist/mcp-app/chart.notices.txt +1194 -0
- package/dist/mcp-app/connect.html +3179 -0
- package/dist/mcp-app/connect.notices.txt +1012 -0
- package/dist/mcp-app/receipt.html +8430 -0
- package/dist/mcp-app/receipt.notices.txt +3549 -0
- package/dist/mcp-app/review.html +8430 -0
- package/dist/mcp-app/review.notices.txt +3549 -0
- package/dist/mcp-ui/contracts.js +31 -0
- package/dist/mcp-ui/readCards.js +28 -0
- package/dist/mcp-ui/tools.js +134 -0
- package/dist/review-app/brand-dark.svg +1 -0
- package/dist/review-app/brand-light.svg +1 -0
- package/dist/review-app/favicon.svg +1 -0
- package/dist/review-app/settings.css +1 -0
- package/dist/review-app/settings.js +1 -0
- package/dist/review-app/ui.css +809 -0
- package/dist/review-server/assets.js +48 -0
- package/dist/review-server/html.js +45 -0
- package/dist/review-server/http.js +47 -0
- package/dist/review-server/middleware/hostOrigin.js +48 -0
- package/dist/review-server/middleware/reviewToken.js +7 -0
- package/dist/review-server/reviewServerPolicy.js +1 -0
- package/dist/review-server/server.js +72 -0
- package/dist/review-server/settingsApi.js +158 -0
- package/dist/runtime/application.js +203 -0
- package/dist/runtime/config.js +97 -0
- package/dist/runtime/localSettingsService.js +198 -0
- package/dist/runtime/logger.js +66 -0
- package/dist/runtime/reviewDependencies.js +58 -0
- package/dist/runtime/reviewServerAcquire.js +93 -0
- package/dist/runtime/runtimeTermination.js +60 -0
- package/dist/runtime/shared/authenticatedFetch.js +127 -0
- package/dist/runtime/shared/control.js +93 -0
- package/dist/runtime/shared/dataAccess.js +66 -0
- package/dist/runtime/shared/mcpHttp.js +84 -0
- package/dist/runtime/shared/ownerLease.js +50 -0
- package/dist/runtime/shared/server.js +98 -0
- package/dist/runtime/shared/stdio.js +113 -0
- package/dist/runtime/smokeMainnetRead.js +391 -0
- package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
- package/dist/runtime/smokeReview.js +40 -0
- package/dist/runtime/start.js +90 -0
- package/dist/runtime/suiEndpoint.js +97 -0
- package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
- package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
- package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
- package/dist/runtime/walletConnectConfig.js +3 -0
- package/dist/runtime/walletConnectStorage.js +99 -0
- package/dist/runtime/walletConnectTransport.js +128 -0
- package/dist/runtime/walletSdkChild.js +3 -0
- package/dist/runtime/walletSdkChildRuntime.js +160 -0
- package/dist/runtime/walletSdkIpc.js +26 -0
- package/dist/runtime/walletSdkProcess.js +358 -0
- package/docs/AGENT_BEHAVIOR.md +524 -0
- package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
- package/docs/FRONTEND_POLICY.md +534 -0
- package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
- package/docs/MCP_SETUP.md +742 -0
- package/docs/MCP_TOOLS.md +1318 -0
- package/docs/SDK_API.md +206 -0
- package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
- package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
- package/docs/UTILITY_INDEX.md +275 -0
- package/docs/WALLET_CONNECTION.md +238 -0
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
- package/package.json +79 -0
- package/protocols/deepbook-margin.md +15 -0
- package/protocols/deepbook-v3.md +24 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { resolveActivityDatabasePath } from "../core/activity/sqliteActivityStore.js";
|
|
2
|
+
import { DEFAULT_SUI_GRAPHQL_URL, DEFAULT_SUI_GRPC_URL, SUI_MAINNET_CHAIN_IDENTIFIER, parseGraphqlUrl, parseGrpcUrl } from "./suiEndpoint.js";
|
|
3
|
+
import { SETTINGS_APPLIES_AFTER_RESTART } from "../core/preferences/preferencesStore.js";
|
|
4
|
+
export function loadBootConfig(env = process.env) {
|
|
5
|
+
const network = env.SUI_NETWORK ?? "mainnet";
|
|
6
|
+
if (network !== "mainnet") {
|
|
7
|
+
throw new Error("Sui MCP product runtime only supports mainnet");
|
|
8
|
+
}
|
|
9
|
+
if (env.SUI_RPC_URL) {
|
|
10
|
+
throw new Error("Sui JSON-RPC endpoint configuration is not supported; use local settings, SUI_GRPC_URL for gRPC, or SUI_GRAPHQL_URL for GraphQL");
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
network: "mainnet",
|
|
14
|
+
expectedChainIdentifier: SUI_MAINNET_CHAIN_IDENTIFIER,
|
|
15
|
+
reviewHost: "127.0.0.1",
|
|
16
|
+
reviewPort: parseReviewPort(env.SUI_MCP_REVIEW_PORT),
|
|
17
|
+
activityDatabasePath: resolveActivityDatabasePath(env)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Existing shared-backend port: clients using the same data directory must
|
|
21
|
+
// agree on this port and configuration. It is not browser-wallet authority.
|
|
22
|
+
export const DEFAULT_REVIEW_PORT = 8765;
|
|
23
|
+
function parseReviewPort(value) {
|
|
24
|
+
if (value === undefined || value === "") {
|
|
25
|
+
return DEFAULT_REVIEW_PORT;
|
|
26
|
+
}
|
|
27
|
+
const port = Number(value);
|
|
28
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
29
|
+
throw new Error(`SUI_MCP_REVIEW_PORT must be an integer between 1 and 65535, got: ${value}`);
|
|
30
|
+
}
|
|
31
|
+
return port;
|
|
32
|
+
}
|
|
33
|
+
export function composeRuntimeConfig(input) {
|
|
34
|
+
const env = input.env ?? process.env;
|
|
35
|
+
const defaultSuiGrpcUrl = parseGrpcUrl(input.defaultSuiGrpcUrl ?? DEFAULT_SUI_GRPC_URL);
|
|
36
|
+
const defaultSuiGraphqlUrl = parseGraphqlUrl(input.defaultSuiGraphqlUrl ?? DEFAULT_SUI_GRAPHQL_URL);
|
|
37
|
+
const storedValue = parseGrpcUrl(input.storedSuiGrpcUrl ?? defaultSuiGrpcUrl);
|
|
38
|
+
const storedGraphqlValue = parseGraphqlUrl(input.storedSuiGraphqlUrl ?? defaultSuiGraphqlUrl);
|
|
39
|
+
const envValue = env.SUI_GRPC_URL === undefined ? undefined : parseGrpcUrl(env.SUI_GRPC_URL);
|
|
40
|
+
const envGraphqlValue = env.SUI_GRAPHQL_URL === undefined ? undefined : parseGraphqlUrl(env.SUI_GRAPHQL_URL);
|
|
41
|
+
const suiGrpcUrl = resolveSuiGrpcUrlView({
|
|
42
|
+
defaultSuiGrpcUrl,
|
|
43
|
+
storedValue,
|
|
44
|
+
envValue
|
|
45
|
+
});
|
|
46
|
+
const suiGraphqlUrl = resolveSuiGraphqlUrlView({
|
|
47
|
+
defaultSuiGraphqlUrl,
|
|
48
|
+
storedValue: storedGraphqlValue,
|
|
49
|
+
envValue: envGraphqlValue
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
...input.bootConfig,
|
|
53
|
+
grpcUrl: suiGrpcUrl.effectiveValue,
|
|
54
|
+
graphqlUrl: suiGraphqlUrl.effectiveValue,
|
|
55
|
+
suiGrpcUrl,
|
|
56
|
+
suiGraphqlUrl
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function resolveSuiGrpcUrlView(input) {
|
|
60
|
+
const defaultSuiGrpcUrl = parseGrpcUrl(input.defaultSuiGrpcUrl ?? DEFAULT_SUI_GRPC_URL);
|
|
61
|
+
const storedValue = parseGrpcUrl(input.storedValue ?? defaultSuiGrpcUrl);
|
|
62
|
+
if (input.envValue !== undefined) {
|
|
63
|
+
const effectiveValue = parseGrpcUrl(input.envValue);
|
|
64
|
+
return {
|
|
65
|
+
storedValue,
|
|
66
|
+
effectiveValue,
|
|
67
|
+
source: "environment",
|
|
68
|
+
pendingStoredValue: effectiveValue === storedValue ? undefined : storedValue,
|
|
69
|
+
appliesAfter: effectiveValue === storedValue ? undefined : SETTINGS_APPLIES_AFTER_RESTART
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
storedValue,
|
|
74
|
+
effectiveValue: storedValue,
|
|
75
|
+
source: storedValue === defaultSuiGrpcUrl ? "builtin_default" : "local_db"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function resolveSuiGraphqlUrlView(input) {
|
|
79
|
+
const defaultSuiGraphqlUrl = parseGraphqlUrl(input.defaultSuiGraphqlUrl ?? DEFAULT_SUI_GRAPHQL_URL);
|
|
80
|
+
const storedValue = parseGraphqlUrl(input.storedValue ?? defaultSuiGraphqlUrl);
|
|
81
|
+
if (input.envValue !== undefined) {
|
|
82
|
+
const effectiveValue = parseGraphqlUrl(input.envValue);
|
|
83
|
+
return {
|
|
84
|
+
storedValue,
|
|
85
|
+
effectiveValue,
|
|
86
|
+
source: "environment",
|
|
87
|
+
pendingStoredValue: effectiveValue === storedValue ? undefined : storedValue,
|
|
88
|
+
appliesAfter: effectiveValue === storedValue ? undefined : SETTINGS_APPLIES_AFTER_RESTART
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
storedValue,
|
|
93
|
+
effectiveValue: storedValue,
|
|
94
|
+
source: storedValue === defaultSuiGraphqlUrl ? "builtin_default" : "local_db"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export { DEFAULT_SUI_GRAPHQL_URL, DEFAULT_SUI_GRPC_URL, SUI_MAINNET_CHAIN_IDENTIFIER };
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { SETTINGS_APPLIES_AFTER_RESTART, LocalSettingsError } from "../core/preferences/preferencesStore.js";
|
|
2
|
+
import { DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS, DEFAULT_SUI_GRAPHQL_URL, DEFAULT_SUI_GRPC_ENDPOINT_VERIFY_TIMEOUT_MS, DEFAULT_SUI_GRPC_URL, SUI_MAINNET_CHAIN_IDENTIFIER, SuiEndpointError, parseGraphqlUrl, parseGrpcUrl, verifyMainnetGraphqlEndpoint, verifyMainnetGrpcEndpoint } from "./suiEndpoint.js";
|
|
3
|
+
import { resolveSuiGraphqlUrlView, resolveSuiGrpcUrlView } from "./config.js";
|
|
4
|
+
export class RuntimeLocalSettingsService {
|
|
5
|
+
options;
|
|
6
|
+
env;
|
|
7
|
+
defaultSuiGrpcUrl;
|
|
8
|
+
defaultSuiGraphqlUrl;
|
|
9
|
+
bootSuiGrpcUrl;
|
|
10
|
+
bootSuiGraphqlUrl;
|
|
11
|
+
verifyGrpcEndpoint;
|
|
12
|
+
verifyGraphqlEndpoint;
|
|
13
|
+
now;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.env = options.env ?? process.env;
|
|
17
|
+
this.defaultSuiGrpcUrl = parseGrpcUrl(options.defaultSuiGrpcUrl ?? DEFAULT_SUI_GRPC_URL);
|
|
18
|
+
this.defaultSuiGraphqlUrl = parseGraphqlUrl(options.defaultSuiGraphqlUrl ?? DEFAULT_SUI_GRAPHQL_URL);
|
|
19
|
+
this.bootSuiGrpcUrl = normalizeBootSuiGrpcUrlView(options.bootSuiGrpcUrl ??
|
|
20
|
+
resolveSuiGrpcUrlView({
|
|
21
|
+
defaultSuiGrpcUrl: this.defaultSuiGrpcUrl,
|
|
22
|
+
envValue: this.env.SUI_GRPC_URL
|
|
23
|
+
}));
|
|
24
|
+
this.bootSuiGraphqlUrl = normalizeBootSuiGraphqlUrlView(options.bootSuiGraphqlUrl ??
|
|
25
|
+
resolveSuiGraphqlUrlView({
|
|
26
|
+
defaultSuiGraphqlUrl: this.defaultSuiGraphqlUrl,
|
|
27
|
+
envValue: this.env.SUI_GRAPHQL_URL
|
|
28
|
+
}));
|
|
29
|
+
this.verifyGrpcEndpoint = options.verifyGrpcEndpoint ?? defaultGrpcEndpointVerifier;
|
|
30
|
+
this.verifyGraphqlEndpoint = options.verifyGraphqlEndpoint ?? defaultGraphqlEndpointVerifier;
|
|
31
|
+
this.now = options.now ?? (() => new Date());
|
|
32
|
+
}
|
|
33
|
+
async getLocalSettings() {
|
|
34
|
+
const stored = await this.options.preferencesRepository.getSuiGrpcUrl();
|
|
35
|
+
const storedGraphql = await this.options.preferencesRepository.getSuiGraphqlUrl();
|
|
36
|
+
const currentStoredView = resolveSuiGrpcUrlView({
|
|
37
|
+
defaultSuiGrpcUrl: this.defaultSuiGrpcUrl,
|
|
38
|
+
storedValue: stored?.value,
|
|
39
|
+
envValue: this.env.SUI_GRPC_URL
|
|
40
|
+
});
|
|
41
|
+
const currentStoredGraphqlView = resolveSuiGraphqlUrlView({
|
|
42
|
+
defaultSuiGraphqlUrl: this.defaultSuiGraphqlUrl,
|
|
43
|
+
storedValue: storedGraphql?.value,
|
|
44
|
+
envValue: this.env.SUI_GRAPHQL_URL
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
suiGrpcUrl: withBootEffectiveEndpoint(currentStoredView, this.bootSuiGrpcUrl),
|
|
48
|
+
suiGraphqlUrl: withBootEffectiveEndpoint(currentStoredGraphqlView, this.bootSuiGraphqlUrl)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async setSuiGrpcUrl(url) {
|
|
52
|
+
const normalized = parseEndpointForTool(url);
|
|
53
|
+
await this.verifyEndpointForTool(normalized, "grpc");
|
|
54
|
+
const result = await this.options.preferencesRepository.setSuiGrpcUrl(normalized, this.now());
|
|
55
|
+
return {
|
|
56
|
+
status: "saved",
|
|
57
|
+
storedValue: result.storedValue,
|
|
58
|
+
previousStoredValue: result.previousStoredValue,
|
|
59
|
+
appliesAfter: SETTINGS_APPLIES_AFTER_RESTART
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async setSuiGraphqlUrl(url) {
|
|
63
|
+
const normalized = parseGraphqlEndpointForTool(url);
|
|
64
|
+
await this.verifyEndpointForTool(normalized, "graphql");
|
|
65
|
+
const result = await this.options.preferencesRepository.setSuiGraphqlUrl(normalized, this.now());
|
|
66
|
+
return {
|
|
67
|
+
status: "saved",
|
|
68
|
+
storedValue: result.storedValue,
|
|
69
|
+
previousStoredValue: result.previousStoredValue,
|
|
70
|
+
appliesAfter: SETTINGS_APPLIES_AFTER_RESTART
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async resetSuiGrpcUrl() {
|
|
74
|
+
const result = await this.options.preferencesRepository.resetSuiGrpcUrl(this.defaultSuiGrpcUrl, this.now());
|
|
75
|
+
return {
|
|
76
|
+
status: "reset",
|
|
77
|
+
storedValue: result.storedValue,
|
|
78
|
+
previousStoredValue: result.previousStoredValue,
|
|
79
|
+
appliesAfter: SETTINGS_APPLIES_AFTER_RESTART
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async resetSuiGraphqlUrl() {
|
|
83
|
+
const result = await this.options.preferencesRepository.resetSuiGraphqlUrl(this.defaultSuiGraphqlUrl, this.now());
|
|
84
|
+
return {
|
|
85
|
+
status: "reset",
|
|
86
|
+
storedValue: result.storedValue,
|
|
87
|
+
previousStoredValue: result.previousStoredValue,
|
|
88
|
+
appliesAfter: SETTINGS_APPLIES_AFTER_RESTART
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async verifyEndpointForTool(url, transport) {
|
|
92
|
+
try {
|
|
93
|
+
await (transport === "grpc" ? this.verifyGrpcEndpoint(url) : this.verifyGraphqlEndpoint(url));
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error instanceof LocalSettingsError) {
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
if (error instanceof SuiEndpointError) {
|
|
100
|
+
throw endpointErrorForTool(error, transport);
|
|
101
|
+
}
|
|
102
|
+
const label = transport === "grpc" ? "gRPC" : "GraphQL";
|
|
103
|
+
throw new LocalSettingsError("internal_error", `Sui ${label} endpoint verification failed`, {
|
|
104
|
+
message: `Sui ${label} endpoint verification failed`
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function normalizeBootSuiGrpcUrlView(view) {
|
|
110
|
+
return {
|
|
111
|
+
...view,
|
|
112
|
+
storedValue: parseGrpcUrl(view.storedValue),
|
|
113
|
+
effectiveValue: parseGrpcUrl(view.effectiveValue)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function normalizeBootSuiGraphqlUrlView(view) {
|
|
117
|
+
return {
|
|
118
|
+
...view,
|
|
119
|
+
storedValue: parseGraphqlUrl(view.storedValue),
|
|
120
|
+
effectiveValue: parseGraphqlUrl(view.effectiveValue)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function withBootEffectiveEndpoint(storedView, bootView) {
|
|
124
|
+
if (storedView.effectiveValue === bootView.effectiveValue) {
|
|
125
|
+
return storedView;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
storedValue: storedView.storedValue,
|
|
129
|
+
effectiveValue: bootView.effectiveValue,
|
|
130
|
+
source: bootView.source,
|
|
131
|
+
pendingStoredValue: storedView.storedValue === bootView.effectiveValue ? undefined : storedView.storedValue,
|
|
132
|
+
appliesAfter: SETTINGS_APPLIES_AFTER_RESTART
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function defaultGrpcEndpointVerifier(url) {
|
|
136
|
+
await verifyMainnetGrpcEndpoint({
|
|
137
|
+
url,
|
|
138
|
+
expectedChainIdentifier: SUI_MAINNET_CHAIN_IDENTIFIER,
|
|
139
|
+
timeoutMs: DEFAULT_SUI_GRPC_ENDPOINT_VERIFY_TIMEOUT_MS
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async function defaultGraphqlEndpointVerifier(url) {
|
|
143
|
+
await verifyMainnetGraphqlEndpoint({
|
|
144
|
+
url,
|
|
145
|
+
expectedChainIdentifier: SUI_MAINNET_CHAIN_IDENTIFIER,
|
|
146
|
+
timeoutMs: DEFAULT_SUI_GRAPHQL_ENDPOINT_VERIFY_TIMEOUT_MS
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function parseEndpointForTool(url) {
|
|
150
|
+
try {
|
|
151
|
+
return parseGrpcUrl(url);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (error instanceof SuiEndpointError) {
|
|
155
|
+
throw endpointErrorForTool(error, "grpc");
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function parseGraphqlEndpointForTool(url) {
|
|
161
|
+
try {
|
|
162
|
+
return parseGraphqlUrl(url);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof SuiEndpointError) {
|
|
166
|
+
throw endpointErrorForTool(error, "graphql");
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function endpointErrorForTool(error, transport) {
|
|
172
|
+
const label = transport === "grpc" ? "gRPC" : "GraphQL";
|
|
173
|
+
if (error.kind === "chain_identifier_mismatch") {
|
|
174
|
+
return new LocalSettingsError("input_invalid", `Sui ${label} endpoint did not report the mainnet chain identifier`, {
|
|
175
|
+
reason: error.kind,
|
|
176
|
+
...endpointErrorDetailsForTool(error, ["chainIdentifier", "expectedChainIdentifier"])
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (error.kind === "endpoint_timeout" || error.kind === "endpoint_unreachable") {
|
|
180
|
+
return new LocalSettingsError("input_invalid", `Sui ${label} endpoint could not be verified`, {
|
|
181
|
+
reason: error.kind,
|
|
182
|
+
...endpointErrorDetailsForTool(error, error.kind === "endpoint_timeout" ? ["timeoutMs"] : [])
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return new LocalSettingsError("input_invalid", error.message, {
|
|
186
|
+
reason: error.kind
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function endpointErrorDetailsForTool(error, allowedKeys) {
|
|
190
|
+
const details = {};
|
|
191
|
+
for (const key of allowedKeys) {
|
|
192
|
+
const value = error.details[key];
|
|
193
|
+
if (value !== undefined) {
|
|
194
|
+
details[key] = value;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return details;
|
|
198
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function flushStderr() {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const failed = (error) => { process.stderr.off("error", failed); reject(error); };
|
|
4
|
+
process.stderr.once("error", failed);
|
|
5
|
+
process.stderr.write("", (error) => {
|
|
6
|
+
// A failed stream write also emits error; keep its handler through that
|
|
7
|
+
// event instead of allowing EPIPE to replace the intended exit code.
|
|
8
|
+
if (error) {
|
|
9
|
+
reject(error);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
process.stderr.off("error", failed);
|
|
13
|
+
resolve();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function createStderrLogger(scope) {
|
|
18
|
+
const write = (level, message, meta) => {
|
|
19
|
+
const record = {
|
|
20
|
+
level,
|
|
21
|
+
scope,
|
|
22
|
+
message,
|
|
23
|
+
meta: meta ? redactMeta(meta) : undefined,
|
|
24
|
+
at: new Date().toISOString()
|
|
25
|
+
};
|
|
26
|
+
process.stderr.write(`${JSON.stringify(record)}\n`);
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
info: (message, meta) => write("info", message, meta),
|
|
30
|
+
warn: (message, meta) => write("warn", message, meta),
|
|
31
|
+
error: (message, meta) => write("error", message, meta)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const REDACTED = "[redacted]";
|
|
35
|
+
const CIRCULAR = "[circular]";
|
|
36
|
+
const SENSITIVE_META_KEY = /token|secret|private|signature|bytes/i;
|
|
37
|
+
export function redactMeta(meta) {
|
|
38
|
+
return redactRecord(meta, new WeakSet());
|
|
39
|
+
}
|
|
40
|
+
function redactRecord(record, seen) {
|
|
41
|
+
if (seen.has(record)) {
|
|
42
|
+
return { value: CIRCULAR };
|
|
43
|
+
}
|
|
44
|
+
seen.add(record);
|
|
45
|
+
const redacted = Object.fromEntries(Object.entries(record).map(([key, value]) => [
|
|
46
|
+
key,
|
|
47
|
+
SENSITIVE_META_KEY.test(key) ? REDACTED : redactValue(value, seen)
|
|
48
|
+
]));
|
|
49
|
+
seen.delete(record);
|
|
50
|
+
return redacted;
|
|
51
|
+
}
|
|
52
|
+
function redactValue(value, seen) {
|
|
53
|
+
if (value === null || typeof value !== "object") {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
if (seen.has(value)) {
|
|
58
|
+
return CIRCULAR;
|
|
59
|
+
}
|
|
60
|
+
seen.add(value);
|
|
61
|
+
const redacted = value.map((item) => redactValue(item, seen));
|
|
62
|
+
seen.delete(value);
|
|
63
|
+
return redacted;
|
|
64
|
+
}
|
|
65
|
+
return redactRecord(value, seen);
|
|
66
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mainnetCoins } from "@mysten/deepbook-v3";
|
|
2
|
+
import { buildSupportedReviewAdapters } from "../adapters/reviewAdapters.js";
|
|
3
|
+
import { validateSupportedAdapterLifecycle } from "../adapters/adapterLifecycleValidators.js";
|
|
4
|
+
import { createDeepbookSwapTransactionMaterialDigestProducer, createDeepbookSwapTransactionMaterialProducer } from "../adapters/deepbook/deepbookTransactionMaterialProducer.js";
|
|
5
|
+
import { createDeepbookSwapHumanReadableReviewProducer } from "../adapters/deepbook/deepbookHumanReviewProducer.js";
|
|
6
|
+
import { createTransactionObjectOwnershipProducer } from "../core/action/transactionObjectOwnershipProducer.js";
|
|
7
|
+
import { createReviewTimeSimulationProducer } from "../core/action/reviewTimeSimulationEvidence.js";
|
|
8
|
+
import { producePtbVisualizationArtifact } from "../core/action/ptbVisualizationProducer.js";
|
|
9
|
+
export function createRuntimeReviewDependencies(options) {
|
|
10
|
+
const { client: suiClient, chainIdentifier, materialStore: transactionMaterialStore, readService } = options;
|
|
11
|
+
const config = { network: "mainnet", expectedChainIdentifier: options.expectedChainIdentifier };
|
|
12
|
+
return {
|
|
13
|
+
validateAdapterLifecycle: validateSupportedAdapterLifecycle,
|
|
14
|
+
adapters: buildSupportedReviewAdapters((() => {
|
|
15
|
+
const transactionObjectOwnershipProducer = createTransactionObjectOwnershipProducer({
|
|
16
|
+
materialStore: transactionMaterialStore,
|
|
17
|
+
objectSource: suiClient,
|
|
18
|
+
fundingSource: suiClient.core,
|
|
19
|
+
network: config.network,
|
|
20
|
+
chainIdentifier,
|
|
21
|
+
expectedChainIdentifier: config.expectedChainIdentifier
|
|
22
|
+
});
|
|
23
|
+
const reviewTimeSimulationProducer = createReviewTimeSimulationProducer({
|
|
24
|
+
client: suiClient,
|
|
25
|
+
materialStore: transactionMaterialStore,
|
|
26
|
+
network: config.network,
|
|
27
|
+
chainIdentifier,
|
|
28
|
+
expectedChainIdentifier: config.expectedChainIdentifier
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
deepbook: {
|
|
32
|
+
deepbookQuoteSource: readService,
|
|
33
|
+
deepbookDeepBalanceSource: async (account) => {
|
|
34
|
+
const balance = await suiClient.core.getBalance({
|
|
35
|
+
owner: account,
|
|
36
|
+
coinType: mainnetCoins.DEEP.type
|
|
37
|
+
});
|
|
38
|
+
return balance.balance.balance.toString();
|
|
39
|
+
},
|
|
40
|
+
deepbookTransactionMaterialProducer: createDeepbookSwapTransactionMaterialProducer({
|
|
41
|
+
client: suiClient,
|
|
42
|
+
network: config.network,
|
|
43
|
+
chainIdentifier,
|
|
44
|
+
expectedChainIdentifier: config.expectedChainIdentifier,
|
|
45
|
+
materialStore: transactionMaterialStore
|
|
46
|
+
}),
|
|
47
|
+
deepbookTransactionMaterialDigestProducer: createDeepbookSwapTransactionMaterialDigestProducer({
|
|
48
|
+
materialStore: transactionMaterialStore
|
|
49
|
+
}),
|
|
50
|
+
transactionObjectOwnershipProducer,
|
|
51
|
+
deepbookHumanReadableReviewProducer: createDeepbookSwapHumanReadableReviewProducer(),
|
|
52
|
+
reviewTimeSimulationProducer,
|
|
53
|
+
ptbVisualizationProducer: (vizInput) => producePtbVisualizationArtifact({ materialStore: transactionMaterialStore, ...vizInput })
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
})())
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const DEFAULT_REACQUIRE_INTERVAL_MS = 3000;
|
|
2
|
+
function errorCode(error) {
|
|
3
|
+
return typeof error === "object" && error !== null
|
|
4
|
+
? error.code
|
|
5
|
+
: undefined;
|
|
6
|
+
}
|
|
7
|
+
// Bind the port: returns the server on success, undefined when the port is already
|
|
8
|
+
// in use, and rethrows any other (non-EADDRINUSE) startup error.
|
|
9
|
+
async function tryStart(start, port) {
|
|
10
|
+
try {
|
|
11
|
+
return await start(port);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (errorCode(error) === "EADDRINUSE") {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Bind the review server to its fixed port, or defer to a healthy peer already
|
|
22
|
+
* serving it.
|
|
23
|
+
*
|
|
24
|
+
* - Port free → bind and own the single review origin.
|
|
25
|
+
* - Port held by a separate healthy instance of our review server → defer (run no
|
|
26
|
+
* local server; the peer serves the shared database for every client) and watch for
|
|
27
|
+
* the owner to exit, then take the origin over. No peer process is signalled.
|
|
28
|
+
* - Port held by anything else (foreign, no identity answer, or our own pid) → clear
|
|
29
|
+
* error; the origin is never silently reassigned.
|
|
30
|
+
*/
|
|
31
|
+
export async function startOrDeferReviewServer(start, port, deps) {
|
|
32
|
+
const owned = await tryStart(start, port);
|
|
33
|
+
if (owned) {
|
|
34
|
+
deps.logger.info("review server bound; owning the review origin", { port });
|
|
35
|
+
return { deferred: false, close: () => owned.close() };
|
|
36
|
+
}
|
|
37
|
+
const holder = await deps.probeIdentity(port);
|
|
38
|
+
if (!holder || holder.service !== deps.serviceName || holder.pid === deps.currentPid) {
|
|
39
|
+
throw Object.assign(new Error(`Review server port ${port} is already in use by a process that is not a separate ${deps.serviceName} review server. ` +
|
|
40
|
+
`Use the same current runtime and data directory for every client, or choose a different SUI_MCP_REVIEW_PORT. The listener is not replaced automatically.`), { code: "REVIEW_PORT_CONFLICT" });
|
|
41
|
+
}
|
|
42
|
+
deps.logger.info("review port owned by a healthy peer; deferring and watching for takeover", {
|
|
43
|
+
port,
|
|
44
|
+
ownerPid: holder.pid,
|
|
45
|
+
...(holder.version ? { ownerVersion: holder.version } : {})
|
|
46
|
+
});
|
|
47
|
+
const reacquireIntervalMs = deps.reacquireIntervalMs ?? DEFAULT_REACQUIRE_INTERVAL_MS;
|
|
48
|
+
let stopped = false;
|
|
49
|
+
let acquired;
|
|
50
|
+
const watch = (async () => {
|
|
51
|
+
while (!stopped && !acquired) {
|
|
52
|
+
await deps.delay(reacquireIntervalMs);
|
|
53
|
+
if (stopped || acquired) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
let next;
|
|
57
|
+
try {
|
|
58
|
+
next = await tryStart(start, port);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// An authenticated prior owner may have closed its port while its SDK
|
|
62
|
+
// still owns private storage. Reuse the existing takeover observation.
|
|
63
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "DATA_DIRECTORY_OWNED")
|
|
64
|
+
continue;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
if (!next) {
|
|
68
|
+
continue; // a peer still owns the port; keep deferring
|
|
69
|
+
}
|
|
70
|
+
if (stopped) {
|
|
71
|
+
await next.close();
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
acquired = next;
|
|
75
|
+
deps.logger.info("acquired review port after the previous owner exited", { port });
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
watch.catch((error) => {
|
|
79
|
+
if (!stopped)
|
|
80
|
+
deps.onFailure?.(error instanceof Error ? error : new Error("Shared server acquisition failed."));
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
deferred: true,
|
|
84
|
+
close: async () => {
|
|
85
|
+
// Stop watching; the loop's stopped-check closes any bind that lands in-flight,
|
|
86
|
+
// so we never await the (possibly mid-delay) watch loop here.
|
|
87
|
+
stopped = true;
|
|
88
|
+
if (acquired) {
|
|
89
|
+
await acquired.close();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { SuiEndpointError } from "../core/suiEndpoint.js";
|
|
2
|
+
// Keep arbitrary SDK/server exception text, endpoint values and stacks private.
|
|
3
|
+
export function runtimeFailureReason(error, stage) {
|
|
4
|
+
if (error instanceof SuiEndpointError)
|
|
5
|
+
return `Sui mainnet endpoint verification failed (${error.kind}). Check the configured endpoint.`;
|
|
6
|
+
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : undefined;
|
|
7
|
+
if (code === "DATA_DIRECTORY_OWNED")
|
|
8
|
+
return "The private data directory is already owned by another wallet runtime.";
|
|
9
|
+
if (code === "REVIEW_PORT_CONFLICT")
|
|
10
|
+
return "The review port is held by a foreign or incompatible runtime. Use the same runtime, data directory and port for every client.";
|
|
11
|
+
if (code && /^(SQLITE_[A-Z_]+|EACCES|EPERM|ENOENT|ENOSPC|EADDRINUSE)$/.test(code))
|
|
12
|
+
return `Local runtime resource access failed (${code}). Check the data directory and listener configuration.`;
|
|
13
|
+
if (stage === "configuration")
|
|
14
|
+
return "Invalid runtime configuration. Check mainnet, endpoint settings and SUI_MCP_REVIEW_PORT.";
|
|
15
|
+
if (stage === "control_identity")
|
|
16
|
+
return "The private runtime control identity could not be loaded. Check data directory ownership and permissions.";
|
|
17
|
+
return "The local runtime could not complete this stage. Check local data permissions and runtime configuration.";
|
|
18
|
+
}
|
|
19
|
+
// Bootstrap alone decides exit intent. Cleanup-induced onclose notifications
|
|
20
|
+
// join the same promise; they cannot replace a fatal or signal exit with EOF.
|
|
21
|
+
export class RuntimeTermination {
|
|
22
|
+
options;
|
|
23
|
+
closing;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
}
|
|
27
|
+
get requested() { return this.closing !== undefined; }
|
|
28
|
+
request(cause) {
|
|
29
|
+
if (this.closing)
|
|
30
|
+
return this.closing;
|
|
31
|
+
this.closing = Promise.resolve().then(async () => {
|
|
32
|
+
let code = cause.kind === "signal" ? cause.code : cause.kind === "failure" ? 1 : 0;
|
|
33
|
+
const report = (message, stage, error) => {
|
|
34
|
+
try {
|
|
35
|
+
this.options.logger.error(message, { stage, reason: runtimeFailureReason(error, stage) });
|
|
36
|
+
}
|
|
37
|
+
catch { /* Preserve failure intent even if stderr is unavailable. */ }
|
|
38
|
+
};
|
|
39
|
+
if (cause.kind === "failure")
|
|
40
|
+
report("fatal runtime error", cause.stage, cause.error);
|
|
41
|
+
try {
|
|
42
|
+
await this.options.close();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (cause.kind === "client")
|
|
46
|
+
code = 1;
|
|
47
|
+
report("runtime shutdown failed", "runtime_close", error);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
await this.options.flush();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
if (cause.kind === "client")
|
|
54
|
+
code = 1;
|
|
55
|
+
}
|
|
56
|
+
this.options.exit(code);
|
|
57
|
+
});
|
|
58
|
+
return this.closing;
|
|
59
|
+
}
|
|
60
|
+
}
|