@zktx.io/sui-mcp 0.5.0 → 0.6.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 +6 -6
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +3 -3
- package/dist/core/activity/localDataService.js +71 -52
- package/dist/core/activity/localDataValidation.js +1 -3
- package/dist/core/activity/sqliteActivityStore.js +24 -16
- package/dist/core/activity/sqliteActivityStoreSchema.js +83 -12
- package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
- package/dist/core/session/cardSession.js +1 -1
- package/dist/core/session/cardSessionStore.js +53 -3
- package/dist/core/session/sessionStore.js +0 -17
- package/dist/core/session/settingsFileIdentity.js +11 -0
- package/dist/core/session/settingsFileProtocol.js +25 -0
- package/dist/core/session/settingsFileStore.js +346 -0
- package/dist/core/session/settingsInputError.js +34 -0
- package/dist/core/session/settingsOperation.js +224 -0
- package/dist/core/session/settingsPreparationSql.js +15 -0
- package/dist/core/session/settingsWorkflow.js +1219 -0
- package/dist/core/session/sqliteSessionStore.js +0 -40
- package/dist/core/session/sqliteSettingsWorkflowStore.js +463 -0
- package/dist/core/session/sqliteWalletWorkflowStore.js +164 -41
- package/dist/core/session/status.js +2 -2
- package/dist/core/session/walletConnection.js +27 -8
- package/dist/core/session/walletWorkflow.js +73 -15
- package/dist/core/session/workflowState.js +16 -8
- package/dist/core/session/workflowView.js +18 -2
- package/dist/mcp/responseGuidance.js +10 -5
- package/dist/mcp/serverInfo.js +3 -4
- package/dist/mcp/toolNames.js +2 -2
- package/dist/mcp/tools/account/index.js +1 -19
- package/dist/mcp/tools/read/readToolHelpers.js +5 -4
- package/dist/mcp/tools/session/executionResultTools.js +2 -2
- package/dist/mcp/tools/session/shared.js +1 -0
- package/dist/mcp/tools/session/statusTools.js +4 -3
- package/dist/mcp/tools/settings/index.js +8 -28
- package/dist/mcp-app/account.html +14 -14
- package/dist/mcp-app/account.notices.txt +1 -1
- package/dist/mcp-app/chart.html +9 -9
- package/dist/mcp-app/chart.notices.txt +1 -1
- package/dist/mcp-app/connect.html +14 -14
- package/dist/mcp-app/connect.notices.txt +1 -1
- package/dist/mcp-app/receipt.html +1298 -1463
- package/dist/mcp-app/receipt.notices.txt +226 -1204
- package/dist/mcp-app/review.html +1298 -1463
- package/dist/mcp-app/review.notices.txt +226 -1204
- package/dist/mcp-app/settings.html +3133 -0
- package/dist/mcp-app/settings.notices.txt +973 -0
- package/dist/mcp-ui/contracts.js +8 -2
- package/dist/mcp-ui/readCards.js +1 -1
- package/dist/mcp-ui/tools.js +33 -2
- package/dist/review-server/server.js +5 -51
- package/dist/runtime/application.js +41 -14
- package/dist/runtime/localSettingsService.js +26 -2
- package/dist/runtime/shared/authenticatedFetch.js +6 -2
- package/dist/runtime/shared/control.js +5 -6
- package/dist/runtime/shared/http.js +14 -0
- package/dist/runtime/shared/mcpHttp.js +9 -4
- package/dist/runtime/shared/server.js +1 -1
- package/dist/runtime/smokeMainnetRead.js +0 -1
- package/dist/runtime/walletConnectStorage.js +2 -4
- package/dist/runtime/walletSdkChildRuntime.js +2 -1
- package/dist/runtime/walletSdkIpc.js +2 -1
- package/dist/runtime/walletSdkProcess.js +3 -2
- package/docs/AGENT_BEHAVIOR.md +44 -10
- package/docs/AGENT_DEVELOPMENT_POLICY.md +16 -12
- package/docs/FRONTEND_POLICY.md +105 -28
- package/docs/LOCAL_DB_ARCHITECTURE.md +143 -20
- package/docs/MCP_SETUP.md +99 -39
- package/docs/MCP_TOOLS.md +273 -24
- package/docs/SDK_API.md +51 -10
- package/docs/UTILITY_INDEX.md +9 -6
- package/docs/WALLET_CONNECTION.md +30 -9
- package/package.json +13 -7
- package/dist/core/activity/schemaVersion.js +0 -2
- package/dist/core/session/keyedRecordStore.js +0 -18
- package/dist/core/session/settingsSession.js +0 -1
- package/dist/core/session/settingsSessions.js +0 -45
- package/dist/review-app/brand-dark.svg +0 -1
- package/dist/review-app/brand-light.svg +0 -1
- package/dist/review-app/favicon.svg +0 -1
- package/dist/review-app/settings.css +0 -1
- package/dist/review-app/settings.js +0 -1
- package/dist/review-app/ui.css +0 -809
- package/dist/review-server/assets.js +0 -48
- package/dist/review-server/html.js +0 -45
- package/dist/review-server/http.js +0 -47
- package/dist/review-server/middleware/reviewToken.js +0 -7
- package/dist/review-server/settingsApi.js +0 -158
package/docs/SDK_API.md
CHANGED
|
@@ -1,23 +1,62 @@
|
|
|
1
1
|
# SDK API Verification
|
|
2
2
|
|
|
3
|
-
This document records the pinned SDK APIs used by the current runtime.
|
|
3
|
+
This document records the pinned SDK APIs used by the current runtime. Backend
|
|
4
|
+
API facts come from the installed SDK source in `node_modules`. Browser
|
|
5
|
+
libraries are embedded during the build; their packaged versions are recorded
|
|
6
|
+
in the card dependency notices described below.
|
|
4
7
|
|
|
5
8
|
## Package Versions
|
|
6
9
|
|
|
7
10
|
- `@modelcontextprotocol/ext-apps`: `1.7.5`
|
|
8
|
-
- `@modelcontextprotocol/sdk`: `1.
|
|
11
|
+
- `@modelcontextprotocol/sdk`: `1.31.0`
|
|
9
12
|
- `@mysten/sui`: `2.17.0`
|
|
10
13
|
- `@mysten/deepbook-v3`: `1.3.6`
|
|
11
14
|
- `@walletconnect/sign-client`, `@walletconnect/types`, `@walletconnect/utils`: `2.23.10`
|
|
12
15
|
- `qrcode`: `1.5.4`
|
|
13
16
|
- `@zktx.io/ptb-model`: `0.5.0`
|
|
14
|
-
- `mermaid`: `11.
|
|
17
|
+
- `mermaid`: `11.16.1`
|
|
18
|
+
- `katex`: `0.18.2`, pinned by an override scoped to `mermaid@11.16.1`
|
|
19
|
+
|
|
20
|
+
Mermaid and its KaTeX dependency are build dependencies for the embedded card
|
|
21
|
+
resources, not Node server runtime dependencies. Their versions are fixed by
|
|
22
|
+
the source-checkout lockfile and included in the generated HTML. Installed npm
|
|
23
|
+
dependencies do not apply a dependency package's own overrides.
|
|
24
|
+
|
|
25
|
+
## Bundled Transaction Diagrams
|
|
26
|
+
|
|
27
|
+
The backend uses `@zktx.io/ptb-model` to generate a flowchart from verified
|
|
28
|
+
review material or a transaction read from Sui. The shared browser renderer
|
|
29
|
+
uses Mermaid to display that flowchart in the Review and Receipt cards.
|
|
30
|
+
The diagram is display evidence; it does not authorize signing or execution.
|
|
31
|
+
|
|
32
|
+
`scripts/build-mcp-ui.ts` embeds each card's script and styles in one HTML
|
|
33
|
+
resource and records the modules included in that resource. Check
|
|
34
|
+
`dist/mcp-app/review.notices.txt` and `receipt.notices.txt`, or the dependency
|
|
35
|
+
notices embedded in their HTML, for the actual browser-library versions.
|
|
36
|
+
The dependency versions in a new source-checkout build come from
|
|
37
|
+
`package-lock.json`. Reinstalling a published package's runtime dependencies
|
|
38
|
+
does not replace the libraries already embedded in its card HTML. A browser
|
|
39
|
+
dependency update requires rebuilding and distributing the card resources.
|
|
15
40
|
|
|
16
41
|
## MCP Apps
|
|
17
42
|
|
|
18
|
-
`@modelcontextprotocol/ext-apps` is pinned to `1.7.5`, with MCP SDK `1.
|
|
43
|
+
`@modelcontextprotocol/ext-apps` is pinned to `1.7.5`, with MCP SDK `1.31.0`. The server uses `registerAppTool`, `registerAppResource` and `getUiCapability`; the view uses `App` for host-mediated calls and teardown. Account, Receipt, Chart, Connect, Review and Settings are separate self-contained resources, so an account card does not load the PTB or chart renderer. Settings uses scoped app-only inputs for local actions and file transfer. The server keeps app-only permissions out of model content.
|
|
19
44
|
|
|
20
|
-
|
|
45
|
+
Settings completes a selected upload's verification and restore preparation on
|
|
46
|
+
the backend. The card renders the stored `restoreCheck` result and collects one
|
|
47
|
+
final restore confirmation; it does not parse backup contents, decide backend
|
|
48
|
+
eligibility or apply data automatically. The complete file, preparation and
|
|
49
|
+
final input remain bound to their card/operation identity and current revision.
|
|
50
|
+
The backend distinguishes unsealed receiving, active checking, a sealed file
|
|
51
|
+
needing a new check, a current confirmation and actual rejection. Definite
|
|
52
|
+
file/mainnet rejection is independent of local-data confirmation revision and
|
|
53
|
+
survives Back until explicit file selection changes. The View's current file-job
|
|
54
|
+
progress describes communication only; it cannot grant restoration authority.
|
|
55
|
+
|
|
56
|
+
Recorded backup locations use backend-native folder and file-name fields;
|
|
57
|
+
optional `App.downloadFile` delivery remains separate from local publication.
|
|
58
|
+
|
|
59
|
+
The shared server uses the pinned MCP SDK Streamable HTTP transport behind authenticated loopback access. Stdio clients forward tool, resource and prompt requests with their original client identity and capabilities. SDK 1.31.0 limits each internal HTTP request body to 4 MiB, including its complete MCP JSON-RPC envelope and metadata. Oversized bodies are refused before tool or card dispatch. Card calls additionally have a 64 KiB envelope budget; ordinary MCP calls do not inherit that smaller limit. Restore uploads retain their 16 MiB file limit and are split into messages within the card budget. The SDK's internal HTTP batch guard does not expose a stdio batch feature.
|
|
21
60
|
|
|
22
61
|
## WalletConnect Runtime
|
|
23
62
|
|
|
@@ -167,13 +206,15 @@ facts; its absence/timeout does not prove execution failure.
|
|
|
167
206
|
SDK session storage persists only the pinned connection namespaces. History,
|
|
168
207
|
requests and unknown queue keys are volatile. The SDK has no supported complete
|
|
169
208
|
in-process disposal; its owner process lifetime bounds callbacks and storage.
|
|
170
|
-
The parent uses a fixed packaged Node child entrypoint and private
|
|
209
|
+
The parent uses a fixed packaged Node child entrypoint and private package-bound IPC.
|
|
171
210
|
Only the SDK child owns SDK storage. The supervisor fences a terminating run,
|
|
172
211
|
sends SIGKILL to its owned child and waits for actual exit. Its explicit
|
|
173
|
-
replacement primitive
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
212
|
+
replacement primitive is private infrastructure. Settings/Troubleshooting
|
|
213
|
+
owns explicit run/impact preparation, atomic input revocation/admission,
|
|
214
|
+
old-child exit, next-run reservation and ready/failure recording. Connect does
|
|
215
|
+
not own restart. A new explicit Settings operation can supersede a startup
|
|
216
|
+
still pending; known failure and its first timestamp remain immutable.
|
|
217
|
+
Late callbacks cannot reopen old authority or submit another transaction.
|
|
177
218
|
|
|
178
219
|
Snapshots and per-session versions distinguish present, absent and unusable
|
|
179
220
|
sessions. Source inspection failure disables the service instead of becoming an
|
package/docs/UTILITY_INDEX.md
CHANGED
|
@@ -43,7 +43,7 @@ Source-checkout scripts are not packaged product commands, MCP tools, review-tim
|
|
|
43
43
|
| Live Sui sent-function activity summary | `read.summarize_sui_function_activity_scan` | Implemented | Summarizes bounded sent-function rows. Not P&L, route recommendation, or signing readiness. |
|
|
44
44
|
| Stored Sui activity summary | `read.summarize_sui_account_activity` | Implemented | Summarizes stored normalized Sui activity facts, including stored Move call, balance change, object/event, gas, execution error details, and optional protocol activity labels when evidence is available. |
|
|
45
45
|
| Stored account asset timeline | `read.get_account_asset_timeline` | Implemented | Builds stored local account asset net-flow bars and optional DeepBook USDC token-denominated candle references. Not held balances, complete wallet history, USD value, P&L, cost basis, route advice, transaction building, or signing readiness. |
|
|
46
|
-
| Local settings | `settings.create_local_settings_session`, `settings.get_local_settings` | Implemented | MCP
|
|
46
|
+
| Local settings | `settings.create_local_settings_session`, `settings.get_local_settings` | Implemented | MCP opens an internal Settings card or reads settings. App-only preparation/confirmation owns changes, files and explicit connection-service recovery. Stored endpoints apply after all participating apps restart. |
|
|
47
47
|
| Release package check | `npm run release:check` | Maintainer, source checkout | Verifies metadata, tests/build, tarball assets/licenses and installed MCP/Settings startup. Requires network and loopback access; no wallet pairing, signing or publishing. |
|
|
48
48
|
| Mainnet read smoke | `npm run smoke:mainnet` | Manual | Runs selected mainnet read paths from built `dist/`. Build first. Not part of CI or `release:check`; see notes below. |
|
|
49
49
|
| Sui GraphQL function filter probe | `npm exec -- tsx scripts/sui-graphql-function-filter-probe.ts [--endpoint <mainnet-graphql-url>] [--sample-size <1-50>] [--timeout-ms <ms>]` | Manual, source evidence only | Runs a read-only Sui mainnet GraphQL source-shape probe for function-filter diagnostics; see notes below. |
|
|
@@ -51,19 +51,22 @@ Source-checkout scripts are not packaged product commands, MCP tools, review-tim
|
|
|
51
51
|
|
|
52
52
|
## Release Package Check
|
|
53
53
|
|
|
54
|
-
`npm run release:check` is a source-checkout maintainer command. Node 22
|
|
54
|
+
`npm run release:check` is a source-checkout maintainer command. Node 22.12+ within
|
|
55
|
+
22.x, or Node 24+,
|
|
55
56
|
installed development dependencies, npm registry access for a temporary tarball
|
|
56
57
|
installation, local loopback sockets, and reachable Sui mainnet endpoints are
|
|
57
58
|
required. Native SQLite must work both in the checkout and the installed package.
|
|
59
|
+
The source build and tests use Vite, Rolldown and Vitest's common Node ranges; the
|
|
60
|
+
installed package's Node runtime requirement is documented in MCP Setup.
|
|
58
61
|
The runtime uses its built-in endpoints unless `SUI_GRPC_URL` or `SUI_GRAPHQL_URL`
|
|
59
62
|
is supplied; the actual mainnet checks remain enabled.
|
|
60
63
|
|
|
61
64
|
The command checks metadata, types, tests, builds, package contents and licenses,
|
|
62
65
|
then launches the installed package through stdio MCP with a new data directory
|
|
63
66
|
and an independent port. It reads the registered tools and resources, creates a
|
|
64
|
-
Settings
|
|
65
|
-
|
|
66
|
-
that restart input
|
|
67
|
+
Settings card only in that temporary database, and checks the six internal
|
|
68
|
+
HTML resources, app-only tools, scoped status and retired-route rejection. A scoped test card verifies
|
|
69
|
+
that Connect rejects retired restart input and opening manage does not pair. The check
|
|
67
70
|
verifies the isolated SDK retains exclusive storage ownership until shutdown. It waits
|
|
68
71
|
for process termination before removing its installation. A termination failure preserves the temporary files for cleanup
|
|
69
72
|
after the process stops. It does not pair, sign, submit, publish, or mutate the
|
|
@@ -259,7 +262,7 @@ It is not an MCP tool, not a CI check, not packaged product functionality, not r
|
|
|
259
262
|
- Local replay output is debug evidence only. The utility is not an MCP tool, review-time simulation, wallet authorization, signing readiness, or onchain transaction submission/execution.
|
|
260
263
|
- When a review activity output has `lowSampleWarning: true`, report raw counts and avoid inferring behavior patterns.
|
|
261
264
|
- For review activity list output, `dataScope.recordCount` is the full matching local review count. The returned `activities` array can be shorter when `truncated.activities: true`.
|
|
262
|
-
-
|
|
265
|
+
- Settings card actions require scoped permission and explicit confirmation. They change local settings/data or recover the connection service without authorizing a new transaction, signing or custody. Ordinary MCP opens the card or reads settings; app-only file transfer never exposes raw backup bytes to the model.
|
|
263
266
|
|
|
264
267
|
### Optional account-bound review smoke
|
|
265
268
|
|
|
@@ -19,9 +19,23 @@ an explicit retry even if the other connection later ends. If multiple valid con
|
|
|
19
19
|
review preparation or signing. The Connect card shows each exact target for
|
|
20
20
|
user-confirmed disconnection. After recorded disconnections leave one valid
|
|
21
21
|
connection and no pending connection operation, that connection becomes usable;
|
|
22
|
-
with
|
|
22
|
+
with no recorded or SDK-present connection and no pending operation, a new pairing is allowed. Terminal history alone does not
|
|
23
23
|
block it. A failed disconnect remains unconfirmed in the wallet app; its SDK
|
|
24
24
|
session cannot revive the failed product record.
|
|
25
|
+
The current SDK snapshot also gates pairing and account use. A known failed or
|
|
26
|
+
disconnected record whose SDK session remains present is a disconnect target
|
|
27
|
+
on a new card, not a usable wallet. `manageableConnectionIds` identifies those
|
|
28
|
+
exact targets and ordinary connected targets, including expired records.
|
|
29
|
+
An unmapped SDK session in a published available run sets
|
|
30
|
+
`connectionReconciliationRequired: true` after pending registration/cleanup
|
|
31
|
+
ends. An initializing or state_sync memory snapshot does not establish this
|
|
32
|
+
diagnosis, and an absent flag does not prove session absence. Pairing, account use, preparation and signing
|
|
33
|
+
remain blocked. Remove the retained connection in the wallet app, then confirm
|
|
34
|
+
its absence through the current connection service. Settings/Troubleshooting
|
|
35
|
+
can explicitly restart the service to inspect it again, but restarting never
|
|
36
|
+
promotes an unrecorded or failed session into a usable connection. The card does
|
|
37
|
+
not invent a connection ID or report physical SDK failure.
|
|
38
|
+
The SDK's availability and a recorded terminal outcome alone do not prove absence.
|
|
25
39
|
|
|
26
40
|
The backend validates accounts, Sui mainnet namespace, methods and expiry before
|
|
27
41
|
recording a connection. A single wallet-approved account can become read context
|
|
@@ -80,14 +94,21 @@ stored results. Normal startup is shown as initialization, not as a wallet
|
|
|
80
94
|
rejection. No implicit asset account is offered until the current service run
|
|
81
95
|
confirms a usable connection.
|
|
82
96
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
Settings owns explicit connection-service restart. Open its Troubleshooting
|
|
98
|
+
section, inspect the current run and affected requests, then confirm the
|
|
99
|
+
restart. Connect manages connections/approved addresses and retains individual
|
|
100
|
+
Disconnect controls for conflicting saved sessions; it does not own global
|
|
101
|
+
service controls. A restart waits for old child exit, new run reservation and
|
|
102
|
+
complete ready observation. Unsubmitted signing authority is revoked at
|
|
103
|
+
admission; late signatures cannot submit. Already submitted transactions keep
|
|
104
|
+
their original digest and observation. A new explicit Settings operation may
|
|
105
|
+
supersede a still-pending startup without erasing a known failure.
|
|
106
|
+
|
|
107
|
+
Restart completion does not confirm an interrupted disconnect or remove a
|
|
108
|
+
remote session. Check retained connections in the wallet app. If Settings or
|
|
109
|
+
the parent is unavailable, fully quit all apps using Sui MCP and reopen them;
|
|
110
|
+
closing a peer/window can leave the owner running. Full app restart, rather
|
|
111
|
+
than child restart, applies stored endpoints and installation changes.
|
|
91
112
|
|
|
92
113
|
A new owner records interrupted connection approval as stopped and unconfirmed
|
|
93
114
|
disconnection as failed. It does not repeat either request or restore their
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zktx.io/sui-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"mcpName": "io.github.zktx-mcp/sui-mcp",
|
|
5
5
|
"description": "Sui MCP is a local-first toolkit for helping AI applications and users inspect Sui DeFi actions before execution.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
42
|
-
"build": "npm run clean && tsc -p tsconfig.build.json &&
|
|
42
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && tsx scripts/build-mcp-ui.ts",
|
|
43
43
|
"prepublishOnly": "npm run release:check",
|
|
44
44
|
"release:check": "tsx scripts/release-check.ts",
|
|
45
45
|
"smoke:mainnet": "node dist/runtime/smokeMainnetRead.js",
|
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@modelcontextprotocol/ext-apps": "1.7.5",
|
|
52
|
-
"@modelcontextprotocol/sdk": "1.
|
|
52
|
+
"@modelcontextprotocol/sdk": "1.31.0",
|
|
53
53
|
"@mysten/deepbook-v3": "1.3.6",
|
|
54
54
|
"@mysten/sui": "2.17.0",
|
|
55
|
+
"@noble/hashes": "2.2.0",
|
|
55
56
|
"@scure/bip39": "2.2.0",
|
|
56
57
|
"@walletconnect/sign-client": "2.23.10",
|
|
57
58
|
"@walletconnect/types": "2.23.10",
|
|
@@ -59,19 +60,24 @@
|
|
|
59
60
|
"@zktx.io/ptb-model": "0.5.0",
|
|
60
61
|
"better-sqlite3": "12.9.0",
|
|
61
62
|
"lightweight-charts": "5.2.0",
|
|
62
|
-
"mermaid": "11.12.0",
|
|
63
63
|
"qrcode": "1.5.4",
|
|
64
64
|
"zod": "4.4.3"
|
|
65
65
|
},
|
|
66
|
+
"overrides": {
|
|
67
|
+
"mermaid@11.16.1": {
|
|
68
|
+
"katex": "0.18.2"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
66
71
|
"devDependencies": {
|
|
67
72
|
"@types/better-sqlite3": "7.6.13",
|
|
68
73
|
"@types/node": "25.6.2",
|
|
69
74
|
"@types/qrcode": "1.5.6",
|
|
70
75
|
"marked": "16.4.2",
|
|
71
|
-
"
|
|
76
|
+
"mermaid": "11.16.1",
|
|
77
|
+
"tsx": "4.22.0",
|
|
72
78
|
"typescript": "6.0.3",
|
|
73
|
-
"vite": "8.0.
|
|
74
|
-
"vitest": "4.1.
|
|
79
|
+
"vite": "8.0.16",
|
|
80
|
+
"vitest": "4.1.11"
|
|
75
81
|
},
|
|
76
82
|
"engines": {
|
|
77
83
|
"node": ">=22"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export class InMemoryKeyedRecordStore {
|
|
2
|
-
records = new Map();
|
|
3
|
-
get(id) {
|
|
4
|
-
return this.records.get(id);
|
|
5
|
-
}
|
|
6
|
-
set(id, value) {
|
|
7
|
-
this.records.set(id, value);
|
|
8
|
-
}
|
|
9
|
-
delete(id) {
|
|
10
|
-
this.records.delete(id);
|
|
11
|
-
}
|
|
12
|
-
ids() {
|
|
13
|
-
return [...this.records.keys()];
|
|
14
|
-
}
|
|
15
|
-
clear() {
|
|
16
|
-
this.records.clear();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { cloneLocalSession, createLocalSessionBase, isLocalSessionExpired, tokenMatchesHash } from "./localSession.js";
|
|
2
|
-
import { InMemoryKeyedRecordStore } from "./keyedRecordStore.js";
|
|
3
|
-
export class SettingsSessionManager {
|
|
4
|
-
options;
|
|
5
|
-
sessions;
|
|
6
|
-
constructor(options) {
|
|
7
|
-
this.options = options;
|
|
8
|
-
this.sessions = options.recordStore ?? new InMemoryKeyedRecordStore();
|
|
9
|
-
}
|
|
10
|
-
async create(now) {
|
|
11
|
-
const { base, token } = createLocalSessionBase(now, this.options.ttlMs);
|
|
12
|
-
const session = {
|
|
13
|
-
...base,
|
|
14
|
-
type: "local_settings"
|
|
15
|
-
};
|
|
16
|
-
this.sessions.set(session.id, session);
|
|
17
|
-
await this.options.appendEventLog({
|
|
18
|
-
type: "settings_session.created",
|
|
19
|
-
sessionId: session.id,
|
|
20
|
-
at: now.toISOString()
|
|
21
|
-
});
|
|
22
|
-
return { session: cloneLocalSession(session), token };
|
|
23
|
-
}
|
|
24
|
-
async get(id, now) {
|
|
25
|
-
const session = this.sessions.get(id);
|
|
26
|
-
if (!session) {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
if (isLocalSessionExpired(session, now)) {
|
|
30
|
-
this.sessions.delete(id);
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
return cloneLocalSession(session);
|
|
34
|
-
}
|
|
35
|
-
async validateToken(id, token, now) {
|
|
36
|
-
const session = await this.get(id, now);
|
|
37
|
-
if (!session) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
return tokenMatchesHash(session.tokenHash, token);
|
|
41
|
-
}
|
|
42
|
-
clear() {
|
|
43
|
-
this.sessions.clear();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#4DA2FF"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#1565C0"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sui MCP mark"><g transform="translate(50.000 56.944) scale(1.1031)" fill="#1565C0"><g transform="rotate(0)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(120)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g><g transform="rotate(240)"><path d="M0 -46 C11 -40 23 -32 23 -25 C23 -18 11 -10 0 -4 C-11 -10 -23 -18 -23 -25 C-23 -32 -11 -40 0 -46 Z"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.settings-title{margin:8px 0 6px;font-size:22px;line-height:1.2}.settings-file{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function e(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function t(e,t,n=`primary`){let r=document.createElement(`button`);return r.type=`button`,r.className=n===`primary`?`ui-btn ui-btn--primary`:n===`danger`?`ui-btn ui-btn--danger`:`ui-btn`,r.textContent=e,r.addEventListener(`click`,t),r}function n(e,t,n){let r=document.createElement(`button`);return r.type=`button`,r.className=`ui-icon-btn`,r.setAttribute(`aria-label`,t),r.innerHTML=e,r.addEventListener(`click`,n),r}function r(...t){let n=e(`div`,`ui-btn-row`);return n.append(...t),n}function i(e){let t=r(e);return t.classList.add(`ui-btn-row--end`),t}function a(e){let t=document.createElement(`input`);return t.className=`ui-input`,t.type=e.type??`text`,e.value!==void 0&&(t.value=e.value),e.placeholder!==void 0&&(t.placeholder=e.placeholder),e.id&&(t.id=e.id),e.name&&(t.name=e.name),t.autocomplete=`off`,t}function o(t){let n=e(`section`,`ui-card`);return t&&n.append(e(`h2`,`ui-card-head`,t)),n}function s(t,n){let r=e(`div`,`ui-row`);r.append(e(`span`,`ui-row-label`,t));let i=e(`span`,`ui-row-value`);return typeof n==`string`?i.textContent=n:i.append(n),r.append(i),r}function c(t,n,r){let i=e(`div`,t===`error`?`ui-feedback ui-feedback--error`:`ui-feedback ui-feedback--ok`);return i.setAttribute(`role`,`status`),i.setAttribute(`aria-live`,`polite`),i.append(e(`span`,void 0,n)),r&&i.append(e(`span`,`ui-feedback-detail`,r)),i}function l(t){return e(`p`,`ui-note`,t)}var u=`sui-mcp-theme`;function d(){try{let e=localStorage.getItem(u);return e===`light`||e===`dark`?e:void 0}catch{return}}function f(e){try{localStorage.setItem(u,e)}catch{}}function p(){try{return window.matchMedia(`(prefers-color-scheme: dark)`).matches}catch{return!1}}function m(){return document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`}function h(e){document.documentElement.setAttribute(`data-theme`,e)}function g(){let e=d()??(p()?`dark`:`light`);return h(e),e}function _(e){h(e),f(e)}function v(){let e=m()===`dark`?`light`:`dark`;return _(e),e}var y={brand:`Sui MCP`,shell:{toggleTheme:`Toggle theme`},account:{name:`Name`,address:`Account address`,balances:`Balances`,noBalances:`No token balances were returned.`,nfts:`NFTs`,noNfts:`No NFTs in the scanned objects.`,noImage:`No image`,objectsTruncated:`Some objects beyond the scanned range are omitted.`},receipt:{success:`Transaction succeeded on Sui`,failure:`Transaction failed on Sui`,sender:`Sender`,account:`Affected account`,object:`Object`,noBalanceChanges:`No balance changes were recorded.`,gasComputation:`Computation cost`,gasStorage:`Storage cost`,gasRebate:`Storage rebate`,gasBudget:`Network fee limit (gas budget)`,netFeeExplanation:`The network fee shown above includes the storage rebate. If it is negative, the rebate exceeds the computation and storage charges.`,estimatedNetFeeExplanation:`The estimated network fee shown above includes the storage rebate. If it is negative, the rebate exceeds the estimated computation and storage charges.`,gasLimitExplanation:`The gas budget limits network fee spending. It is not an estimate of the amount charged.`,gasPrice:`Gas price`,gasPayment:`Gas payment object`,graph:`Transaction graph`,graphTip:`A diagram of this transaction's programmable transaction block (PTB), derived from public on-chain data. It is a diagnostic view only, not transaction input, signing, or a safety verdict.`,graphReviewTip:`A diagram of the transaction prepared for this review and simulation. It shows what is being reviewed, not an actual result, permission to sign, or a guarantee of safety.`,graphRendering:`Rendering the transaction graph…`,graphFailed:`The transaction graph could not be rendered`,graphZoomIn:`Zoom in`,graphZoomOut:`Zoom out`,graphCenter:`Center`,graphShowAddresses:`Show full addresses`,graphShowNames:`Show readable labels`,moveCalls:`Move calls`,noMoveCalls:`No Move calls were recorded.`,inputs:`Inputs`,noInputs:`No inputs were recorded.`,inputKinds:{object:`Owned object`,shared_object:`Shared object`,receiving:`Receiving object`,pure:`Pure value`,withdrawal:`Balance withdrawal`,unknown:`Input`},bytes:`bytes`,events:`Events`,noEvents:`No events were recorded.`,objectChanges:`Object changes`,noObjectChanges:`No object changes were recorded.`},chart:{boundaryUsdc:`Prices are in USDC, not USD; dollar parity is not guaranteed.`,boundaryScope:`Historical prices, not live quotes.`,source:`Source: DeepBookV3 Indexer.`,renderedWith:`Rendered with`,library:`TradingView Lightweight Charts`},common:{walletStatusRecovery:`To restart the connection service, fully quit all apps using Sui MCP, then reopen them. This does not confirm removal of a connection in your wallet app.`,mainnet:`Sui mainnet`,retrievedAt:`Data retrieved`}},b={en:y};function ee(){try{return(navigator.language||`en`).slice(0,2).toLowerCase()}catch{return`en`}}function te(e=ee()){return b[e]??y}var x=te(),S=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>`,ne=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>`;function C(e){return e===`dark`?ne:S}function w(e){let t=document.createElement(`img`);return t.className=e,t.src=`/review-assets/${e===`ui-brand-light`?`brand-light`:`brand-dark`}.svg`,t.alt=``,t.width=24,t.height=24,t}function T(){let t=e(`span`,`ui-brand`);return t.append(w(`ui-brand-light`),w(`ui-brand-dark`),e(`span`,void 0,x.brand)),t}function E(t){g();let r=e(`div`,`ui-shell`),i=e(`header`,`ui-header`);i.append(T()),i.append(e(`span`,`ui-header-spacer`));let a=n(C(m()),x.shell.toggleTheme,()=>{a.innerHTML=C(v())});i.append(a);let o=e(`main`,`ui-main`),s=e(`div`,`ui-overlay`);s.setAttribute(`role`,`status`),s.setAttribute(`aria-live`,`polite`);let c=e(`div`,`ui-overlay-box`);c.append(e(`div`,`ui-spinner`));let l=e(`span`,`ui-overlay-label`,`Processing…`);return c.append(l),s.append(c),r.append(i),r.append(o,s),t.replaceChildren(r),{root:r,main:o,setBusy(e,t){t&&(l.textContent=t),e?r.setAttribute(`aria-busy`,`true`):r.removeAttribute(`aria-busy`)}}}var D=class extends Error{constructor(e,t){super(`HTTP JSON request failed: ${e} ${t}`),this.status=e,this.code=t}};async function O(e){try{let t=await e.json();if(typeof t.error==`string`&&t.error.length>0)return t.error}catch{}return`http_${e.status}`}function k(e,t){return e instanceof D?e.code===`wallet_request_unsettled`?`A wallet request is still being settled. Local data cannot be replaced yet.`:`${t} (${e.code})`:t}var A=`x-sui-mcp-token`;function j(){let e=window.location.hash;return e.startsWith(`#`)?e.slice(1):``}function re(e,t){return{...t,[A]:e}}var M=16*1024*1024,N=document.querySelector(`#settings-app`);if(!N)throw Error(`settings app root missing`);var P=N,F=P.dataset.settingsSessionId??``,I=j(),L=E(P).main,R,z,B,V,H,U;function W(e,n,r=`primary`){let i=t(e,n,r);return i.disabled=H!==void 0,i}function G(e){if(H?.scope===e)return l(H.text);if(U?.scope===e)return c(U.kind,U.text)}function K(e,t){let n=G(t);n&&e.append(n)}F&&I?X():q();function q(){let t=[];if(t.push(e(`h1`,`settings-title`,`Sui MCP Settings`)),t.push(l(`Use this local page to manage the default address for reads, local data, and Sui read endpoints. Settings changes do not sign transactions or grant custody.`)),V&&t.push(c(`error`,V)),!F||!I){t.push(c(`error`,`Missing settings session id or token. Open the settings URL from your AI client again.`)),L.replaceChildren(...t);return}t.push(J(),Y(),ie(),ae(),oe()),L.replaceChildren(...t)}function J(){let e=o(`Status`);return R?(e.append(s(`Server`,`${R.server.name} ${R.server.version}`)),e.append(s(`Network`,R.server.network)),e.append(s(`Endpoint source`,R.localSettings.suiGrpcUrl.source)),e.append(s(`Effective endpoint`,R.localSettings.suiGrpcUrl.effectiveValue)),e.append(s(`Stored endpoint`,R.localSettings.suiGrpcUrl.storedValue)),e.append(s(`GraphQL endpoint source`,R.localSettings.suiGraphqlUrl.source)),e.append(s(`Effective GraphQL endpoint`,R.localSettings.suiGraphqlUrl.effectiveValue)),e.append(s(`Stored GraphQL endpoint`,R.localSettings.suiGraphqlUrl.storedValue)),e.append(s(`Restart required`,R.restartRequired?`yes`:`no`)),e.append(s(`Default address`,R.activeAccount?.account??`none`)),e.append(s(`Data counts`,Object.entries(R.dataCounts).map(([e,t])=>`${e}: ${t}`).join(`, `))),e):(e.append(l(`Loading…`)),e)}function Y(){let e=o(`Default address for reads`);return e.append(l(`Clearing the default address does not disconnect your wallet or revoke onchain permission. Choose an approved address or disconnect in the wallet connection card.`)),R?.activeAccount?e.append(i(W(`Clear default address`,()=>void se(),`secondary`))):e.append(l(R?`No default address is selected.`:`Loading…`)),K(e,`wallet`),e}function ie(){let t=o(`Sui gRPC endpoint`),n=a({type:`url`,value:R?.localSettings.suiGrpcUrl.storedValue??``,placeholder:`https://fullnode.mainnet.sui.io:443`});n.maxLength=512,n.setAttribute(`aria-label`,`Sui gRPC endpoint URL`),t.append(n),t.append(r(W(`Save endpoint`,()=>void ce(n.value)),W(`Restore default Sui gRPC URL`,()=>void le(),`secondary`))),K(t,`grpc`);let i=o(`Sui GraphQL endpoint`),s=a({type:`url`,value:R?.localSettings.suiGraphqlUrl.storedValue??``,placeholder:`https://graphql.mainnet.sui.io/graphql`});s.maxLength=512,s.setAttribute(`aria-label`,`Sui GraphQL endpoint URL`),i.append(s),i.append(r(W(`Save GraphQL endpoint`,()=>void ue(s.value)),W(`Restore default Sui GraphQL URL`,()=>void de(),`secondary`))),K(i,`graphql`);let c=e(`div`,`settings-endpoints`);return c.append(t,i),c}function ae(){let e=o(`Local data`);e.append(l(`Export downloads a backup of your local data. Import previews a chosen backup file (shape only); endpoint verification runs when you confirm, and importing replaces current local data and invalidates open cards and settings sessions.`));let t=document.createElement(`input`);return t.type=`file`,t.accept=`application/json,.json`,t.className=`settings-file`,t.setAttribute(`aria-label`,`Choose a Sui MCP backup file to import`),t.onchange=()=>void pe(t),e.append(r(W(`Export local data`,()=>void fe()),W(`Choose backup file…`,()=>t.click(),`secondary`))),e.append(t),B&&(e.append(c(`ok`,`Import preview ready. Incoming accounts: ${B.incomingCounts.accounts}. Active account change: ${B.activeAccountChange}. Endpoint verification runs before replacement. This import replaces current local data.`)),e.append(l($())),e.append(i(W(`Import and replace local data`,()=>void me(),`danger`)))),K(e,`localData`),e}function oe(){let e=o(`Danger zone`);return e.append(l(`Reset permanently clears all local Sui MCP data and invalidates every open card and settings session. This cannot be undone.`)),e.append(l($())),e.append(i(W(`Reset local data`,()=>void he(),`danger`))),K(e,`danger`),e}async function X(){try{R=await Q(`/api/settings/${encodeURIComponent(F)}`,{method:`GET`}),V=void 0}catch(e){V=k(e,`The local server did not accept this settings session.`)}q()}async function se(){window.confirm(`Clear the default address for reads? This does not disconnect your wallet or revoke onchain permission.`)&&await Z(`wallet`,`clear-active-account`,`Default address cleared.`)}async function ce(e){if(e.length>512){U={scope:`grpc`,kind:`error`,text:`Sui gRPC endpoint must be 512 characters or fewer.`},q();return}await Z(`grpc`,`sui-grpc-url`,`Sui gRPC endpoint saved. Restart the MCP server for the stored value to apply.`,{url:e},`Verifying and saving the Sui gRPC endpoint...`)}async function le(){window.confirm(`Restore the built-in default Sui gRPC URL? Restart the MCP server for the restored value to apply.`)&&await Z(`grpc`,`sui-grpc-url/restore-default`,`Default Sui gRPC URL restored. Restart the MCP server for it to apply.`)}async function ue(e){if(e.length>512){U={scope:`graphql`,kind:`error`,text:`Sui GraphQL endpoint must be 512 characters or fewer.`},q();return}await Z(`graphql`,`sui-graphql-url`,`Sui GraphQL endpoint saved. Restart the MCP server for the stored value to apply.`,{url:e},`Verifying and saving the Sui GraphQL endpoint...`)}async function de(){window.confirm(`Restore the built-in default Sui GraphQL URL? Restart the MCP server for the restored value to apply.`)&&await Z(`graphql`,`sui-graphql-url/restore-default`,`Default Sui GraphQL URL restored. Restart the MCP server for it to apply.`)}async function fe(){try{H={scope:`localData`,text:`Preparing local data export...`},U=void 0,q();let e=await Q(`/api/settings/${encodeURIComponent(F)}/local-data/export`,{method:`GET`}),t=new Blob([JSON.stringify(e,null,2)],{type:`application/json`}),n=document.createElement(`a`),r=URL.createObjectURL(t);n.href=r,n.download=`sui-mcp-local-data-${new Date().toISOString().replaceAll(`:`,`-`)}.json`,n.click(),window.setTimeout(()=>URL.revokeObjectURL(r),0),H=void 0,U={scope:`localData`,kind:`ok`,text:`Local data export prepared.`}}catch(e){H=void 0,U={scope:`localData`,kind:`error`,text:k(e,`Could not export local data.`)}}q()}async function pe(e){let t=e.files?.[0];if(t){if(t.size>M){z=void 0,B=void 0,U={scope:`localData`,kind:`error`,text:`Import file is too large. Maximum size is ${ge(M)}.`},e.value=``,q();return}try{H={scope:`localData`,text:`Loading import preview...`},U=void 0,q(),z=JSON.parse(await t.text()),B=await Q(`/api/settings/${encodeURIComponent(F)}/local-data/import/preview`,{method:`POST`,body:JSON.stringify(z)}),H=void 0,U={scope:`localData`,kind:`ok`,text:`Import preview loaded. Endpoint verification will run only if you confirm import.`}}catch(t){z=void 0,B=void 0,e.value=``,H=void 0,U={scope:`localData`,kind:`error`,text:k(t,`Import preview failed. Check that the file is a Sui MCP local data export.`)}}q()}}async function me(){if(z&&window.confirm(`Import and replace all local data? This invalidates all open cards and settings sessions. `+$())){try{H={scope:`localData`,text:`Importing local data...`},U=void 0,q(),await Q(`/api/settings/${encodeURIComponent(F)}/local-data/import`,{method:`POST`,body:JSON.stringify(z)}),H=void 0,U={scope:`localData`,kind:`ok`,text:`Local data imported. This settings page is now invalid; create a new settings session to continue.`},z=void 0,B=void 0}catch(e){H=void 0,U={scope:`localData`,kind:`error`,text:k(e,`Could not import local data.`)}}q()}}async function he(){if(window.confirm(`Reset all local Sui MCP data? This invalidates all open cards and settings sessions. `+$())){try{H={scope:`danger`,text:`Resetting local data...`},U=void 0,q(),await Q(`/api/settings/${encodeURIComponent(F)}/local-data/reset`,{method:`POST`,body:`{}`}),H=void 0,U={scope:`danger`,kind:`ok`,text:`Local data reset. This settings page is now invalid; create a new settings session to continue.`}}catch(e){H=void 0,U={scope:`danger`,kind:`error`,text:k(e,`Could not reset local data.`)}}q()}}async function Z(e,t,n,r={},i=`Sending local settings request...`){H={scope:e,text:i},U=void 0,q();try{await Q(`/api/settings/${encodeURIComponent(F)}/${t}`,{method:`POST`,body:JSON.stringify(r)}),H=void 0,U={scope:e,kind:`ok`,text:n},await X()}catch(t){H=void 0,U={scope:e,kind:`error`,text:k(t,`The local settings request failed.`)},q()}}async function Q(e,t){let n=t.body===void 0?void 0:{"content-type":`application/json`},r=await fetch(e,{...t,headers:re(I,n)});if(!r.ok)throw new D(r.status,await O(r));return await r.json()}function ge(e){let t=e/(1024*1024);return Number.isInteger(t)?`${t} MiB`:`${e} bytes`}function $(){return`Replacement removes stored requests, including any with an unconfirmed chain outcome. Removing local records cannot cancel or reverse a transaction on Sui. Export the records before replacement if you need to investigate them later.`}
|