@tokamak-private-dapps/private-state-cli 0.1.1 → 0.1.3
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/CHANGELOG.md +12 -0
- package/README.md +80 -3
- package/cli-assistant.html +3 -1
- package/package.json +1 -1
- package/private-state-bridge-cli.mjs +559 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3 - 2026-04-28
|
|
4
|
+
|
|
5
|
+
- Installed the Groth16 runtime during `private-state-cli --install` and reported Groth16 readiness from `--doctor`.
|
|
6
|
+
- Added live NVIDIA and Docker GPU probes to `--doctor`, with a hard failure when live Docker GPU readiness does not match the recorded Tokamak CLI metadata.
|
|
7
|
+
- Renamed `get-my-address` to `get-my-wallet-meta`, added `get-my-l1-address`, and added `list-local-wallets`.
|
|
8
|
+
- Documented the private-state CLI helper commands, common flow examples, and LLM agent guidance.
|
|
9
|
+
|
|
10
|
+
## 0.1.2 - 2026-04-28
|
|
11
|
+
|
|
12
|
+
- Added `private-state-cli --doctor` to report CLI and install-time dependency versions through `tokamak-l2js`.
|
|
13
|
+
- Reported Tokamak zk-EVM runtime install mode, Docker mode, and CUDA runtime metadata.
|
|
14
|
+
|
|
3
15
|
## 0.1.1 - 2026-04-28
|
|
4
16
|
|
|
5
17
|
- Updated channel balance proof generation to use the fixed Groth16 runtime workspace proof paths.
|
package/README.md
CHANGED
|
@@ -8,7 +8,8 @@ Command-line client for the Tokamak private-state DApp.
|
|
|
8
8
|
npm install -g @tokamak-private-dapps/private-state-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Install the local Tokamak zk-EVM runtime workspace and public private-state deployment
|
|
11
|
+
Install the local Tokamak zk-EVM runtime workspace, Groth16 runtime workspace, and public private-state deployment
|
|
12
|
+
artifacts:
|
|
12
13
|
|
|
13
14
|
```bash
|
|
14
15
|
private-state-cli --install
|
|
@@ -27,9 +28,15 @@ Run the CLI with:
|
|
|
27
28
|
private-state-cli <command> ...
|
|
28
29
|
```
|
|
29
30
|
|
|
31
|
+
Check the installed package and runtime state with:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
private-state-cli --doctor
|
|
35
|
+
```
|
|
36
|
+
|
|
30
37
|
## Commands
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
A common private-state flow is:
|
|
33
40
|
|
|
34
41
|
1. `create-channel`
|
|
35
42
|
2. `deposit-bridge`
|
|
@@ -44,6 +51,24 @@ The normal private-state flow is:
|
|
|
44
51
|
|
|
45
52
|
Use `private-state-cli --help` for the full command list and required options.
|
|
46
53
|
|
|
54
|
+
`private-state-cli --doctor` reports the CLI package version, dependency versions recorded by the last
|
|
55
|
+
`private-state-cli --install`, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
|
|
56
|
+
install mode, Docker mode, CUDA runtime metadata, live `nvidia-smi` and Docker GPU probe results, and Groth16
|
|
57
|
+
runtime health. The doctor check fails when the Tokamak Docker `useGpus` metadata does not match the live GPU probes.
|
|
58
|
+
|
|
59
|
+
Local helper commands:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
private-state-cli list-local-wallets --network sepolia --channel-name cuda
|
|
63
|
+
private-state-cli get-my-wallet-meta --wallet <WALLET_NAME> --password <PASSWORD> --network sepolia
|
|
64
|
+
private-state-cli get-my-l1-address --private-key <HEX>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`list-local-wallets` reads only the local workspace and prints saved wallet names that can be reused with `--wallet`.
|
|
68
|
+
`get-my-wallet-meta` opens an encrypted local wallet and reports the stored L1/L2 identity metadata plus the current
|
|
69
|
+
on-chain channel registration match state. `get-my-l1-address` is a simple offline helper that derives the L1 address
|
|
70
|
+
for a private key.
|
|
71
|
+
|
|
47
72
|
## Workspace
|
|
48
73
|
|
|
49
74
|
The CLI stores user workspaces under:
|
|
@@ -54,6 +79,57 @@ The CLI stores user workspaces under:
|
|
|
54
79
|
|
|
55
80
|
Wallet data is encrypted with the password supplied to `join-channel` or `recover-wallet`.
|
|
56
81
|
|
|
82
|
+
## LLM Agent Guidance
|
|
83
|
+
|
|
84
|
+
You may act as an interactive guide for users who do not understand this CLI or the private-state DApp. Assume the
|
|
85
|
+
user only knows that the DApp is a way to send funds privately. Translate the user's intent into safe, step-by-step CLI
|
|
86
|
+
actions.
|
|
87
|
+
|
|
88
|
+
Primary goal: help the user privately send funds by guiding them through the required private-state CLI commands,
|
|
89
|
+
explaining each step only as much as needed to proceed safely.
|
|
90
|
+
|
|
91
|
+
Operating rules:
|
|
92
|
+
|
|
93
|
+
- Do not ask the user to reveal raw private keys in chat. Use environment variable placeholders such as `$ADDR6`,
|
|
94
|
+
`$CREATOR`, or `$PRIVATE_STATE_TEST_PK`.
|
|
95
|
+
- Prefer testnet examples unless the user explicitly asks for mainnet.
|
|
96
|
+
- Before any proof-backed or bridge-facing workflow, ask the user to run `private-state-cli --doctor` and inspect
|
|
97
|
+
whether the runtime, Docker mode, CUDA/GPU probes, Groth16 runtime, and deployment artifacts are healthy.
|
|
98
|
+
- Use `private-state-cli list-local-wallets` to discover local wallet names instead of asking the user to inspect
|
|
99
|
+
filesystem paths manually.
|
|
100
|
+
- Use `private-state-cli get-my-l1-address --private-key "$KEY_ENV"` to derive the L1 address for a private-key
|
|
101
|
+
environment variable when wallet ownership needs to be identified.
|
|
102
|
+
- Use `private-state-cli get-my-wallet-meta --wallet <WALLET> --password <PASSWORD> --network <NETWORK>` to inspect
|
|
103
|
+
local wallet metadata and on-chain channel registration state.
|
|
104
|
+
- Use `private-state-cli get-my-bridge-fund` and `private-state-cli get-my-channel-fund` to check balances before
|
|
105
|
+
telling the user to move funds.
|
|
106
|
+
- Explain that wallet names are local CLI identifiers, while private transfers use notes owned by L2 addresses
|
|
107
|
+
registered in the channel.
|
|
108
|
+
- Do not present one fixed command sequence as universally correct. Some flows start from an existing channel or wallet,
|
|
109
|
+
while others require creating or joining a channel first.
|
|
110
|
+
- When the user asks for a transfer, first determine whether the sender has minted notes available. If not, guide them
|
|
111
|
+
through funding the bridge, joining or recovering the channel wallet, depositing into the channel, and minting notes.
|
|
112
|
+
- When generating commands, use placeholders for secrets and explicit values for public fields. Show one command at a
|
|
113
|
+
time unless the user asks for a batch.
|
|
114
|
+
|
|
115
|
+
Suggested interaction flow:
|
|
116
|
+
|
|
117
|
+
1. Identify the target network, usually `sepolia` for testing.
|
|
118
|
+
2. Identify whether a channel already exists.
|
|
119
|
+
3. Identify the sender and recipient wallets or private-key environment variables.
|
|
120
|
+
4. Run `--doctor`.
|
|
121
|
+
5. Run `list-local-wallets` and relevant metadata or balance checks.
|
|
122
|
+
6. If needed, guide the user through `create-channel`, `deposit-bridge`, `join-channel`, `deposit-channel`, and
|
|
123
|
+
`mint-notes`.
|
|
124
|
+
7. For a private transfer, select available note IDs from `get-my-notes`, find the recipient L2 address from
|
|
125
|
+
`get-my-wallet-meta`, then build `transfer-notes`.
|
|
126
|
+
8. After transfer, guide the recipient to run `get-my-notes` to recover received notes from event logs.
|
|
127
|
+
|
|
128
|
+
Example style: if the user says, "ADDR6 sends 10 tokens privately to ADDR8", do not assume the required note exists.
|
|
129
|
+
First ask or check which channel and network to use, whether ADDR6 and ADDR8 are already joined, what the local wallet
|
|
130
|
+
names are, and whether ADDR6 has an unused note worth exactly 10 or notes that sum to 10. Then provide the next concrete
|
|
131
|
+
command.
|
|
132
|
+
|
|
57
133
|
## Artifacts
|
|
58
134
|
|
|
59
135
|
Proof-backed commands require installed bridge, DApp, and Groth16 artifacts. Run `private-state-cli --install` before
|
|
@@ -71,7 +147,8 @@ Release order matters for npm publication. `@tokamak-private-dapps/common-librar
|
|
|
71
147
|
### What does this package install?
|
|
72
148
|
|
|
73
149
|
It installs the `private-state-cli` terminal command and the local files needed by that command.
|
|
74
|
-
It does not install bridge contracts, app contracts, or local deployment outputs.
|
|
150
|
+
It does not install bridge contracts, app contracts, or local deployment outputs. The `private-state-cli --install`
|
|
151
|
+
command provisions the local Tokamak zk-EVM and Groth16 runtime workspaces used by proof-backed commands.
|
|
75
152
|
|
|
76
153
|
### When should I run `private-state-cli --install`?
|
|
77
154
|
|
package/cli-assistant.html
CHANGED
|
@@ -528,7 +528,9 @@
|
|
|
528
528
|
{ id: "get-my-bridge-fund", description: "Read the current bridge-vault balance.", fields: ["network", "privateKey", "alchemyApiKey"] },
|
|
529
529
|
{ id: "join-channel", description: "Bind the caller to a channel-specific L2 identity.", fields: ["channelName", "password", "network", "privateKey", "alchemyApiKey"] },
|
|
530
530
|
{ id: "recover-wallet", description: "Rebuild the recoverable portion of a wallet.", fields: ["channelName", "password", "network", "privateKey", "alchemyApiKey"] },
|
|
531
|
-
{ id: "get-my-
|
|
531
|
+
{ id: "get-my-wallet-meta", description: "Check whether a saved wallet matches on-chain registration.", fields: ["wallet", "password", "network"] },
|
|
532
|
+
{ id: "get-my-l1-address", description: "Derive the L1 address for a private key.", fields: ["privateKey"] },
|
|
533
|
+
{ id: "list-local-wallets", description: "List saved local wallet names that can be reused with --wallet.", fields: ["network", "channelName"] },
|
|
532
534
|
{ id: "deposit-channel", description: "Move bridged funds into the channel L2 accounting balance.", fields: ["wallet", "password", "network", "amount"] },
|
|
533
535
|
{ id: "withdraw-channel", description: "Move channel L2 balance back into the shared bridge vault.", fields: ["wallet", "password", "network", "amount"] },
|
|
534
536
|
{ id: "get-my-channel-fund", description: "Read the current channel L2 accounting balance.", fields: ["wallet", "password", "network"] },
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
8
9
|
import {
|
|
9
10
|
createCipheriv,
|
|
10
11
|
createDecipheriv,
|
|
@@ -46,6 +47,7 @@ import { deriveRpcUrl, resolveCliNetwork } from "@tokamak-private-dapps/common-l
|
|
|
46
47
|
import {
|
|
47
48
|
buildTokamakCliInvocation,
|
|
48
49
|
resolveTokamakBlockInputConfig,
|
|
50
|
+
resolveTokamakCliPackageRoot,
|
|
49
51
|
resolveTokamakCliResourceDir,
|
|
50
52
|
resolveTokamakCliRuntimeRoot,
|
|
51
53
|
} from "@tokamak-private-dapps/common-library/tokamak-runtime-paths";
|
|
@@ -80,12 +82,16 @@ import {
|
|
|
80
82
|
walletNameForChannelAndAddress,
|
|
81
83
|
} from "./lib/private-state-cli-shared.mjs";
|
|
82
84
|
|
|
85
|
+
const require = createRequire(import.meta.url);
|
|
83
86
|
const defaultCommandCwd = process.cwd();
|
|
87
|
+
const privateStateCliPackageRoot = path.dirname(require.resolve("./package.json"));
|
|
84
88
|
const workspaceRoot = path.resolve(os.homedir(), "tokamak-private-channels", "workspace");
|
|
85
89
|
const tokamakCliInvocation = buildTokamakCliInvocation();
|
|
86
90
|
const tokamakCliCommand = tokamakCliInvocation.command;
|
|
87
91
|
const tokamakCliBaseArgs = tokamakCliInvocation.args;
|
|
88
92
|
const flatDeploymentArtifactPathsByChainId = new Map();
|
|
93
|
+
const DOCKER_CUDA_PROBE_IMAGE = "nvidia/cuda:12.2.0-base-ubuntu22.04";
|
|
94
|
+
const DOCTOR_GPU_PROBE_TIMEOUT_MS = 120000;
|
|
89
95
|
|
|
90
96
|
const abiCoder = AbiCoder.defaultAbiCoder();
|
|
91
97
|
const erc20MetadataAbi = [
|
|
@@ -221,6 +227,24 @@ async function main() {
|
|
|
221
227
|
return;
|
|
222
228
|
}
|
|
223
229
|
|
|
230
|
+
if (args.command === "--doctor") {
|
|
231
|
+
assertDoctorArgs(args);
|
|
232
|
+
await handleDoctor({ args });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (args.command === "get-my-l1-address") {
|
|
237
|
+
assertGetMyL1AddressArgs(args);
|
|
238
|
+
handleGetMyL1Address({ args });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (args.command === "list-local-wallets") {
|
|
243
|
+
assertListLocalWalletsArgs(args);
|
|
244
|
+
handleListLocalWallets({ args });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
224
248
|
const walletCommandHandlers = {
|
|
225
249
|
"mint-notes": {
|
|
226
250
|
assert: assertMintNotesArgs,
|
|
@@ -246,9 +270,9 @@ async function main() {
|
|
|
246
270
|
assert: (parsedArgs) => assertWalletChannelMoveArgs(parsedArgs, "withdraw-channel"),
|
|
247
271
|
run: ({ provider }) => handleGrothVaultMove({ args, provider, direction: "withdraw" }),
|
|
248
272
|
},
|
|
249
|
-
"get-my-
|
|
250
|
-
assert:
|
|
251
|
-
run: ({ provider }) =>
|
|
273
|
+
"get-my-wallet-meta": {
|
|
274
|
+
assert: assertGetMyWalletMetaArgs,
|
|
275
|
+
run: ({ provider }) => handleGetMyWalletMeta({ args, provider }),
|
|
252
276
|
},
|
|
253
277
|
"get-my-channel-fund": {
|
|
254
278
|
assert: assertGetMyChannelFundArgs,
|
|
@@ -932,15 +956,26 @@ async function handleInstallZkEvm({ args }) {
|
|
|
932
956
|
}
|
|
933
957
|
run(tokamakCliCommand, installArgs);
|
|
934
958
|
const tokamakRuntimeRoot = resolveTokamakCliRuntimeRoot();
|
|
959
|
+
const groth16Runtime = installGroth16RuntimeForPrivateState({
|
|
960
|
+
docker: Boolean(args.docker),
|
|
961
|
+
});
|
|
935
962
|
const localDeploymentBaseRoot = args.includeLocalArtifacts ? process.cwd() : null;
|
|
936
963
|
const deploymentArtifacts = await installPrivateStateCliArtifacts({
|
|
937
964
|
dappName: PRIVATE_STATE_DAPP_LABEL,
|
|
938
965
|
localDeploymentBaseRoot,
|
|
939
966
|
});
|
|
967
|
+
const installManifest = writePrivateStateCliInstallManifest({
|
|
968
|
+
dockerRequested: Boolean(args.docker),
|
|
969
|
+
includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
|
|
970
|
+
localDeploymentBaseRoot,
|
|
971
|
+
deploymentArtifacts,
|
|
972
|
+
groth16Runtime,
|
|
973
|
+
});
|
|
940
974
|
printJson({
|
|
941
975
|
action: "install",
|
|
942
976
|
tokamakCli: tokamakCliBaseArgs[0],
|
|
943
977
|
runtimeRoot: tokamakRuntimeRoot,
|
|
978
|
+
groth16Runtime,
|
|
944
979
|
docker: Boolean(args.docker),
|
|
945
980
|
includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
|
|
946
981
|
localDeploymentBaseRoot,
|
|
@@ -953,6 +988,7 @@ async function handleInstallZkEvm({ args }) {
|
|
|
953
988
|
dappTimestamp: entry.dappTimestamp,
|
|
954
989
|
artifactDir: entry.artifactDir,
|
|
955
990
|
})),
|
|
991
|
+
installManifestPath: installManifest.manifestPath,
|
|
956
992
|
});
|
|
957
993
|
}
|
|
958
994
|
|
|
@@ -972,7 +1008,46 @@ async function handleUninstallZkEvm() {
|
|
|
972
1008
|
});
|
|
973
1009
|
}
|
|
974
1010
|
|
|
975
|
-
async function
|
|
1011
|
+
async function handleDoctor() {
|
|
1012
|
+
const report = buildDoctorReport();
|
|
1013
|
+
printJson(report);
|
|
1014
|
+
if (!report.ok) {
|
|
1015
|
+
process.exitCode = 1;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function handleGetMyL1Address({ args }) {
|
|
1020
|
+
const privateKey = normalizePrivateKey(requireArg(args.privateKey, "--private-key"));
|
|
1021
|
+
const signer = new Wallet(privateKey);
|
|
1022
|
+
printJson({
|
|
1023
|
+
action: "get-my-l1-address",
|
|
1024
|
+
l1Address: signer.address,
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function handleListLocalWallets({ args }) {
|
|
1029
|
+
const networkFilter = args.network ? requireNetworkName(args) : null;
|
|
1030
|
+
if (networkFilter) {
|
|
1031
|
+
resolveCliNetwork(networkFilter);
|
|
1032
|
+
}
|
|
1033
|
+
const channelFilter = args.channelName ? slugifyPathComponent(requireArg(args.channelName, "--channel-name")) : null;
|
|
1034
|
+
const wallets = listLocalWallets({
|
|
1035
|
+
networkFilter,
|
|
1036
|
+
channelFilter,
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
printJson({
|
|
1040
|
+
action: "list-local-wallets",
|
|
1041
|
+
workspaceRoot,
|
|
1042
|
+
filters: {
|
|
1043
|
+
network: networkFilter,
|
|
1044
|
+
channelName: args.channelName ?? null,
|
|
1045
|
+
},
|
|
1046
|
+
wallets,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
async function handleGetMyWalletMeta({ args, provider }) {
|
|
976
1051
|
const { wallet, walletMetadata } = loadUnlockedWalletWithMetadata(args);
|
|
977
1052
|
const { signer, l2Identity } = restoreWalletParticipant(wallet, provider);
|
|
978
1053
|
const context = await loadChannelContext({
|
|
@@ -990,7 +1065,7 @@ async function handleGetMyAddress({ args, provider }) {
|
|
|
990
1065
|
=== ethers.toBigInt(normalizeBytes32Hex(expectedStorageKey));
|
|
991
1066
|
|
|
992
1067
|
printJson({
|
|
993
|
-
action: "get-my-
|
|
1068
|
+
action: "get-my-wallet-meta",
|
|
994
1069
|
wallet: wallet.walletName,
|
|
995
1070
|
network: walletMetadata.network,
|
|
996
1071
|
channelName: walletMetadata.channelName,
|
|
@@ -4217,6 +4292,10 @@ function parseArgs(argv) {
|
|
|
4217
4292
|
parsed.command = "--install";
|
|
4218
4293
|
parsed.positional = ["--install"];
|
|
4219
4294
|
}
|
|
4295
|
+
if (!parsed.command && parsed.doctor === true) {
|
|
4296
|
+
parsed.command = "--doctor";
|
|
4297
|
+
parsed.positional = ["--doctor"];
|
|
4298
|
+
}
|
|
4220
4299
|
return parsed;
|
|
4221
4300
|
}
|
|
4222
4301
|
|
|
@@ -4348,6 +4427,48 @@ function resolveWalletPathCandidates(walletName) {
|
|
|
4348
4427
|
return candidates;
|
|
4349
4428
|
}
|
|
4350
4429
|
|
|
4430
|
+
function listLocalWallets({ networkFilter = null, channelFilter = null } = {}) {
|
|
4431
|
+
if (!fs.existsSync(workspaceRoot)) {
|
|
4432
|
+
return [];
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
const wallets = [];
|
|
4436
|
+
for (const networkEntry of fs.readdirSync(workspaceRoot, { withFileTypes: true })) {
|
|
4437
|
+
if (!networkEntry.isDirectory() || (networkFilter && networkEntry.name !== slugifyPathComponent(networkFilter))) {
|
|
4438
|
+
continue;
|
|
4439
|
+
}
|
|
4440
|
+
const networkDir = path.join(workspaceRoot, networkEntry.name);
|
|
4441
|
+
for (const channelEntry of fs.readdirSync(networkDir, { withFileTypes: true })) {
|
|
4442
|
+
if (!channelEntry.isDirectory() || (channelFilter && channelEntry.name !== channelFilter)) {
|
|
4443
|
+
continue;
|
|
4444
|
+
}
|
|
4445
|
+
const walletsDir = path.join(networkDir, channelEntry.name, "wallets");
|
|
4446
|
+
if (!fs.existsSync(walletsDir)) {
|
|
4447
|
+
continue;
|
|
4448
|
+
}
|
|
4449
|
+
for (const walletEntry of fs.readdirSync(walletsDir, { withFileTypes: true })) {
|
|
4450
|
+
if (!walletEntry.isDirectory()) {
|
|
4451
|
+
continue;
|
|
4452
|
+
}
|
|
4453
|
+
const walletDir = path.join(walletsDir, walletEntry.name);
|
|
4454
|
+
wallets.push({
|
|
4455
|
+
wallet: walletEntry.name,
|
|
4456
|
+
network: networkEntry.name,
|
|
4457
|
+
channelName: channelEntry.name,
|
|
4458
|
+
walletDir,
|
|
4459
|
+
metadataPath: walletMetadataPath(walletDir),
|
|
4460
|
+
hasMetadata: fs.existsSync(walletMetadataPath(walletDir)),
|
|
4461
|
+
hasEncryptedWallet: walletConfigExists(walletDir),
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
return wallets.sort((left, right) =>
|
|
4467
|
+
[left.network, left.channelName, left.wallet].join("\0")
|
|
4468
|
+
.localeCompare([right.network, right.channelName, right.wallet].join("\0")),
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4351
4472
|
function channelDataPath(workspaceDir) {
|
|
4352
4473
|
return workspaceChannelDir(workspaceDir);
|
|
4353
4474
|
}
|
|
@@ -4421,6 +4542,10 @@ function assertUninstallZkEvmArgs(args) {
|
|
|
4421
4542
|
assertAllowedCommandKeys(args, "uninstall-zk-evm", new Set(["command", "positional"]), "no options");
|
|
4422
4543
|
}
|
|
4423
4544
|
|
|
4545
|
+
function assertDoctorArgs(args) {
|
|
4546
|
+
assertAllowedCommandKeys(args, "--doctor", new Set(["command", "positional", "doctor"]), "no options");
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4424
4549
|
function assertMintNotesArgs(args) {
|
|
4425
4550
|
requireArg(args.amounts, "--amounts");
|
|
4426
4551
|
assertWalletPasswordArgs(args, "mint-notes", ["amounts"], "--wallet, --password, --network, and --amounts");
|
|
@@ -4533,8 +4658,33 @@ function assertJoinChannelArgs(args) {
|
|
|
4533
4658
|
assertExplicitSignerPasswordCommandArgs(args, "join-channel");
|
|
4534
4659
|
}
|
|
4535
4660
|
|
|
4536
|
-
function
|
|
4537
|
-
assertWalletPasswordArgs(args, "get-my-
|
|
4661
|
+
function assertGetMyWalletMetaArgs(args) {
|
|
4662
|
+
assertWalletPasswordArgs(args, "get-my-wallet-meta", [], "--wallet, --password, and --network");
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4665
|
+
function assertGetMyL1AddressArgs(args) {
|
|
4666
|
+
requireArg(args.privateKey, "--private-key");
|
|
4667
|
+
assertAllowedCommandKeys(
|
|
4668
|
+
args,
|
|
4669
|
+
"get-my-l1-address",
|
|
4670
|
+
new Set(["command", "positional", "privateKey"]),
|
|
4671
|
+
"--private-key",
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
|
|
4675
|
+
function assertListLocalWalletsArgs(args) {
|
|
4676
|
+
if (args.network !== undefined) {
|
|
4677
|
+
requireNetworkName(args);
|
|
4678
|
+
}
|
|
4679
|
+
if (args.channelName !== undefined) {
|
|
4680
|
+
requireArg(args.channelName, "--channel-name");
|
|
4681
|
+
}
|
|
4682
|
+
assertAllowedCommandKeys(
|
|
4683
|
+
args,
|
|
4684
|
+
"list-local-wallets",
|
|
4685
|
+
new Set(["command", "positional", "network", "channelName"]),
|
|
4686
|
+
"optional --network and --channel-name",
|
|
4687
|
+
);
|
|
4538
4688
|
}
|
|
4539
4689
|
|
|
4540
4690
|
function assertWithdrawBridgeArgs(args) {
|
|
@@ -4596,13 +4746,16 @@ function printHelp() {
|
|
|
4596
4746
|
console.log(`
|
|
4597
4747
|
Commands:
|
|
4598
4748
|
--install [--docker] [--include-local-artifacts]
|
|
4599
|
-
Install the Tokamak zk-EVM CLI runtime
|
|
4600
|
-
Use --docker on Linux to forward
|
|
4749
|
+
Install the Tokamak zk-EVM CLI runtime, Groth16 runtime, and private-state deployment artifacts
|
|
4750
|
+
Use --docker on Linux to forward Docker mode to the Tokamak zk-EVM and Groth16 runtimes
|
|
4601
4751
|
Use --include-local-artifacts to also install local deployment/ artifacts from the current working directory
|
|
4602
4752
|
|
|
4603
4753
|
uninstall-zk-evm
|
|
4604
4754
|
Remove the Tokamak zk-EVM CLI runtime workspace
|
|
4605
4755
|
|
|
4756
|
+
--doctor
|
|
4757
|
+
Check private-state CLI package versions, runtime install state, Docker mode, CUDA mode, and deployment artifacts
|
|
4758
|
+
|
|
4606
4759
|
create-channel --channel-name <NAME> --join-fee <TOKENS> --network <NAME> --private-key <HEX> --alchemy-api-key <KEY>
|
|
4607
4760
|
Create a bridge channel and initialize its workspace
|
|
4608
4761
|
|
|
@@ -4624,9 +4777,15 @@ Commands:
|
|
|
4624
4777
|
join-channel --channel-name <NAME> --password <PASSWORD> --network <NAME> --private-key <HEX> --alchemy-api-key <KEY>
|
|
4625
4778
|
Pay the channel join fee and bind a wallet to a channel-specific L2 identity
|
|
4626
4779
|
|
|
4627
|
-
get-my-
|
|
4780
|
+
get-my-wallet-meta --wallet <NAME> --password <PASSWORD> --network <NAME>
|
|
4628
4781
|
Check whether a wallet matches the on-chain channel registration
|
|
4629
4782
|
|
|
4783
|
+
get-my-l1-address --private-key <HEX>
|
|
4784
|
+
Derive the L1 address for a private key
|
|
4785
|
+
|
|
4786
|
+
list-local-wallets [--network <NAME>] [--channel-name <NAME>]
|
|
4787
|
+
List saved local wallet names that can be reused with --wallet
|
|
4788
|
+
|
|
4630
4789
|
deposit-channel --wallet <NAME> --password <PASSWORD> --network <NAME> --amount <TOKENS>
|
|
4631
4790
|
Move bridged funds into the channel L2 accounting balance
|
|
4632
4791
|
|
|
@@ -4882,6 +5041,396 @@ function privateStateCliArtifactPaths(cacheBaseRoot = resolveArtifactCacheBaseRo
|
|
|
4882
5041
|
};
|
|
4883
5042
|
}
|
|
4884
5043
|
|
|
5044
|
+
function privateStateCliInstallManifestPath(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
5045
|
+
return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "install-manifest.json");
|
|
5046
|
+
}
|
|
5047
|
+
|
|
5048
|
+
function writePrivateStateCliInstallManifest({
|
|
5049
|
+
dockerRequested,
|
|
5050
|
+
includeLocalArtifacts,
|
|
5051
|
+
localDeploymentBaseRoot,
|
|
5052
|
+
deploymentArtifacts,
|
|
5053
|
+
groth16Runtime,
|
|
5054
|
+
}) {
|
|
5055
|
+
const manifestPath = privateStateCliInstallManifestPath(deploymentArtifacts.cacheBaseRoot);
|
|
5056
|
+
const manifest = {
|
|
5057
|
+
installedAt: new Date().toISOString(),
|
|
5058
|
+
package: summarizePackageReport(readPackageReport({
|
|
5059
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
5060
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
5061
|
+
})),
|
|
5062
|
+
dependencies: collectDependencyPackageReports().map(summarizePackageReport),
|
|
5063
|
+
install: {
|
|
5064
|
+
dockerRequested,
|
|
5065
|
+
includeLocalArtifacts,
|
|
5066
|
+
localDeploymentBaseRoot,
|
|
5067
|
+
artifactCacheRoot: deploymentArtifacts.cacheBaseRoot,
|
|
5068
|
+
groth16Runtime,
|
|
5069
|
+
installedDeploymentArtifacts: deploymentArtifacts.installed.map((entry) => ({
|
|
5070
|
+
chainId: entry.chainId,
|
|
5071
|
+
source: entry.source,
|
|
5072
|
+
bridgeTimestamp: entry.bridgeTimestamp,
|
|
5073
|
+
dappTimestamp: entry.dappTimestamp,
|
|
5074
|
+
})),
|
|
5075
|
+
},
|
|
5076
|
+
};
|
|
5077
|
+
writeJson(manifestPath, manifest);
|
|
5078
|
+
return { manifestPath, manifest };
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
function summarizePackageReport(report) {
|
|
5082
|
+
return {
|
|
5083
|
+
name: report.name,
|
|
5084
|
+
version: report.version,
|
|
5085
|
+
};
|
|
5086
|
+
}
|
|
5087
|
+
|
|
5088
|
+
function buildDoctorReport() {
|
|
5089
|
+
const cacheBaseRoot = resolveArtifactCacheBaseRoot();
|
|
5090
|
+
const installManifestPath = privateStateCliInstallManifestPath(cacheBaseRoot);
|
|
5091
|
+
const installManifest = readJsonIfExists(installManifestPath);
|
|
5092
|
+
const dependencyReports = collectDependencyPackageReports(installManifest);
|
|
5093
|
+
const tokamakCli = inspectTokamakCliRuntime();
|
|
5094
|
+
const groth16Runtime = inspectGroth16Runtime();
|
|
5095
|
+
const gpuDockerReadiness = inspectGpuDockerReadiness(tokamakCli);
|
|
5096
|
+
const checks = [
|
|
5097
|
+
{
|
|
5098
|
+
name: "dependency package versions",
|
|
5099
|
+
ok: dependencyReports.every((entry) => entry.ok),
|
|
5100
|
+
details: dependencyReports.map((entry) => ({
|
|
5101
|
+
name: entry.name,
|
|
5102
|
+
currentVersion: entry.version,
|
|
5103
|
+
installVersion: entry.installVersion,
|
|
5104
|
+
ok: entry.ok,
|
|
5105
|
+
error: entry.error,
|
|
5106
|
+
})),
|
|
5107
|
+
},
|
|
5108
|
+
{
|
|
5109
|
+
name: "tokamak zk-evm runtime",
|
|
5110
|
+
ok: tokamakCli.installed,
|
|
5111
|
+
details: {
|
|
5112
|
+
doctorStatus: tokamakCli.doctor.status,
|
|
5113
|
+
runtimeRoot: tokamakCli.runtimeRoot,
|
|
5114
|
+
installations: tokamakCli.installations.map(({ platform, installMode, packageVersion, docker }) => ({
|
|
5115
|
+
platform,
|
|
5116
|
+
installMode,
|
|
5117
|
+
packageVersion,
|
|
5118
|
+
dockerEnvironment: docker?.dockerEnvironment ?? null,
|
|
5119
|
+
useGpus: docker?.useGpus ?? null,
|
|
5120
|
+
})),
|
|
5121
|
+
},
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
name: "tokamak docker gpu readiness",
|
|
5125
|
+
ok: gpuDockerReadiness.ok,
|
|
5126
|
+
details: {
|
|
5127
|
+
expectedUseGpus: gpuDockerReadiness.expectedUseGpus,
|
|
5128
|
+
liveUseGpus: gpuDockerReadiness.liveUseGpus,
|
|
5129
|
+
mismatch: gpuDockerReadiness.mismatch,
|
|
5130
|
+
mismatchError: gpuDockerReadiness.mismatchError,
|
|
5131
|
+
hostNvidiaSmi: summarizeProbeResult(gpuDockerReadiness.hostNvidiaSmi),
|
|
5132
|
+
dockerNvidiaSmi: summarizeProbeResult(gpuDockerReadiness.dockerNvidiaSmi),
|
|
5133
|
+
},
|
|
5134
|
+
},
|
|
5135
|
+
{
|
|
5136
|
+
name: "groth16 runtime",
|
|
5137
|
+
ok: groth16Runtime.installed,
|
|
5138
|
+
details: {
|
|
5139
|
+
packageRoot: groth16Runtime.packageRoot,
|
|
5140
|
+
workspaceRoot: groth16Runtime.workspaceRoot,
|
|
5141
|
+
doctorStatus: groth16Runtime.doctor.status,
|
|
5142
|
+
checks: groth16Runtime.checks,
|
|
5143
|
+
},
|
|
5144
|
+
},
|
|
5145
|
+
];
|
|
5146
|
+
|
|
5147
|
+
return {
|
|
5148
|
+
action: "doctor",
|
|
5149
|
+
ok: checks.every((check) => check.ok),
|
|
5150
|
+
generatedAt: new Date().toISOString(),
|
|
5151
|
+
package: readPackageReport({
|
|
5152
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
5153
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
5154
|
+
}),
|
|
5155
|
+
installManifest: {
|
|
5156
|
+
path: installManifestPath,
|
|
5157
|
+
exists: Boolean(installManifest),
|
|
5158
|
+
installedAt: installManifest?.installedAt ?? null,
|
|
5159
|
+
dockerRequested: installManifest?.install?.dockerRequested ?? null,
|
|
5160
|
+
includeLocalArtifacts: installManifest?.install?.includeLocalArtifacts ?? null,
|
|
5161
|
+
},
|
|
5162
|
+
dependencies: dependencyReports,
|
|
5163
|
+
tokamakCli,
|
|
5164
|
+
groth16Runtime,
|
|
5165
|
+
gpuDockerReadiness,
|
|
5166
|
+
checks,
|
|
5167
|
+
};
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
function installGroth16RuntimeForPrivateState({ docker }) {
|
|
5171
|
+
const packageRoot = resolveGroth16PackageRoot();
|
|
5172
|
+
const entryPath = resolveGroth16CliEntryPath(packageRoot);
|
|
5173
|
+
const args = [entryPath, "--install"];
|
|
5174
|
+
if (docker) {
|
|
5175
|
+
args.push("--docker");
|
|
5176
|
+
}
|
|
5177
|
+
run(process.execPath, args, { cwd: packageRoot });
|
|
5178
|
+
const runtime = inspectGroth16Runtime();
|
|
5179
|
+
expect(runtime.installed, "Groth16 runtime install completed, but tokamak-groth16 --doctor still reports an unhealthy runtime.");
|
|
5180
|
+
return runtime;
|
|
5181
|
+
}
|
|
5182
|
+
|
|
5183
|
+
function collectDependencyPackageReports(installManifest = null) {
|
|
5184
|
+
const installVersions = new Map(
|
|
5185
|
+
Array.isArray(installManifest?.dependencies)
|
|
5186
|
+
? installManifest.dependencies.map((entry) => [entry.name, entry.version])
|
|
5187
|
+
: [],
|
|
5188
|
+
);
|
|
5189
|
+
const targets = [
|
|
5190
|
+
{
|
|
5191
|
+
name: "@tokamak-zk-evm/cli",
|
|
5192
|
+
packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
|
|
5193
|
+
},
|
|
5194
|
+
{
|
|
5195
|
+
name: "@tokamak-private-dapps/groth16",
|
|
5196
|
+
resolveTarget: "@tokamak-private-dapps/groth16/public-drive-crs",
|
|
5197
|
+
},
|
|
5198
|
+
{
|
|
5199
|
+
name: "@tokamak-private-dapps/common-library",
|
|
5200
|
+
resolveTarget: "@tokamak-private-dapps/common-library/artifact-cache",
|
|
5201
|
+
},
|
|
5202
|
+
{ name: "tokamak-l2js", resolveTarget: "tokamak-l2js" },
|
|
5203
|
+
];
|
|
5204
|
+
|
|
5205
|
+
return targets.map((target) => {
|
|
5206
|
+
const report = readPackageReport(target);
|
|
5207
|
+
const installVersion = installVersions.get(report.name) ?? null;
|
|
5208
|
+
return {
|
|
5209
|
+
...report,
|
|
5210
|
+
installVersion,
|
|
5211
|
+
ok: Boolean(report.version) && (installVersion === null || installVersion === report.version),
|
|
5212
|
+
};
|
|
5213
|
+
});
|
|
5214
|
+
}
|
|
5215
|
+
|
|
5216
|
+
function readPackageReport({ name, packageJsonPath = null, resolveTarget = null }) {
|
|
5217
|
+
try {
|
|
5218
|
+
const resolvedPackageJsonPath = packageJsonPath
|
|
5219
|
+
? path.resolve(packageJsonPath)
|
|
5220
|
+
: findPackageJsonForName(path.dirname(require.resolve(resolveTarget ?? name)), name);
|
|
5221
|
+
const packageJson = readJson(resolvedPackageJsonPath);
|
|
5222
|
+
return {
|
|
5223
|
+
name: packageJson.name ?? name,
|
|
5224
|
+
version: packageJson.version ?? null,
|
|
5225
|
+
packageRoot: path.dirname(resolvedPackageJsonPath),
|
|
5226
|
+
error: null,
|
|
5227
|
+
};
|
|
5228
|
+
} catch (error) {
|
|
5229
|
+
return {
|
|
5230
|
+
name,
|
|
5231
|
+
version: null,
|
|
5232
|
+
packageRoot: null,
|
|
5233
|
+
error: error.message,
|
|
5234
|
+
ok: false,
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
|
|
5239
|
+
function findPackageJsonForName(startDir, expectedName) {
|
|
5240
|
+
let current = path.resolve(startDir);
|
|
5241
|
+
while (current !== path.dirname(current)) {
|
|
5242
|
+
const candidate = path.join(current, "package.json");
|
|
5243
|
+
if (fs.existsSync(candidate)) {
|
|
5244
|
+
const packageJson = readJson(candidate);
|
|
5245
|
+
if (packageJson.name === expectedName) {
|
|
5246
|
+
return candidate;
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
current = path.dirname(current);
|
|
5250
|
+
}
|
|
5251
|
+
throw new Error(`Cannot locate package.json for ${expectedName} above ${startDir}.`);
|
|
5252
|
+
}
|
|
5253
|
+
|
|
5254
|
+
function resolveGroth16PackageRoot() {
|
|
5255
|
+
const publicDriveCrsPath = require.resolve("@tokamak-private-dapps/groth16/public-drive-crs");
|
|
5256
|
+
return path.dirname(findPackageJsonForName(path.dirname(publicDriveCrsPath), "@tokamak-private-dapps/groth16"));
|
|
5257
|
+
}
|
|
5258
|
+
|
|
5259
|
+
function resolveGroth16CliEntryPath(packageRoot = resolveGroth16PackageRoot()) {
|
|
5260
|
+
return path.join(packageRoot, "cli", "tokamak-groth16-cli.mjs");
|
|
5261
|
+
}
|
|
5262
|
+
|
|
5263
|
+
function inspectGroth16Runtime() {
|
|
5264
|
+
const packageRoot = resolveGroth16PackageRoot();
|
|
5265
|
+
const entryPath = resolveGroth16CliEntryPath(packageRoot);
|
|
5266
|
+
const doctor = runCaptured(process.execPath, [entryPath, "--doctor", "--verbose"], { cwd: packageRoot });
|
|
5267
|
+
const stdout = stripAnsi(doctor.stdout).trim();
|
|
5268
|
+
const stderr = stripAnsi(doctor.stderr).trim();
|
|
5269
|
+
const report = parseJsonReport(stdout);
|
|
5270
|
+
return {
|
|
5271
|
+
installed: doctor.status === 0 && report?.ok === true,
|
|
5272
|
+
packageRoot,
|
|
5273
|
+
workspaceRoot: report?.workspaceRoot ?? null,
|
|
5274
|
+
checks: report?.checks ?? [],
|
|
5275
|
+
doctor: {
|
|
5276
|
+
status: doctor.status,
|
|
5277
|
+
stdout,
|
|
5278
|
+
stderr,
|
|
5279
|
+
},
|
|
5280
|
+
};
|
|
5281
|
+
}
|
|
5282
|
+
|
|
5283
|
+
function inspectTokamakCliRuntime() {
|
|
5284
|
+
const doctor = runCaptured(tokamakCliCommand, [...tokamakCliBaseArgs, "--doctor"], {
|
|
5285
|
+
cwd: resolveTokamakCliPackageRoot(),
|
|
5286
|
+
});
|
|
5287
|
+
const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
|
|
5288
|
+
const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
|
|
5289
|
+
const cacheRoot = resolveTokamakCliCacheRoot();
|
|
5290
|
+
const installations = readTokamakCliInstallations(cacheRoot);
|
|
5291
|
+
const dockerModeInstalled = installations.some((entry) => entry.installMode === "docker" || entry.docker);
|
|
5292
|
+
const cudaCompatible = installations.some((entry) => entry.docker?.useGpus === true);
|
|
5293
|
+
|
|
5294
|
+
return {
|
|
5295
|
+
installed: doctor.status === 0 || installations.length > 0,
|
|
5296
|
+
packageRoot: resolveTokamakCliPackageRoot(),
|
|
5297
|
+
cacheRoot,
|
|
5298
|
+
runtimeRoot,
|
|
5299
|
+
packageVersion: readPackageReport({
|
|
5300
|
+
name: "@tokamak-zk-evm/cli",
|
|
5301
|
+
packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
|
|
5302
|
+
}).version,
|
|
5303
|
+
dockerModeInstalled,
|
|
5304
|
+
cudaCompatible,
|
|
5305
|
+
doctor: {
|
|
5306
|
+
status: doctor.status,
|
|
5307
|
+
stdout: stripAnsi(doctor.stdout).trim(),
|
|
5308
|
+
stderr: stripAnsi(doctor.stderr).trim(),
|
|
5309
|
+
},
|
|
5310
|
+
installations,
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5314
|
+
function inspectGpuDockerReadiness(tokamakCli) {
|
|
5315
|
+
const hostNvidiaSmi = runProbe("nvidia-smi", ["--query-gpu=name,driver_version", "--format=csv,noheader"]);
|
|
5316
|
+
const dockerNvidiaSmi = runProbe("docker", [
|
|
5317
|
+
"run",
|
|
5318
|
+
"--rm",
|
|
5319
|
+
"--gpus",
|
|
5320
|
+
"all",
|
|
5321
|
+
DOCKER_CUDA_PROBE_IMAGE,
|
|
5322
|
+
"nvidia-smi",
|
|
5323
|
+
]);
|
|
5324
|
+
const expectedUseGpus = Boolean(tokamakCli.cudaCompatible);
|
|
5325
|
+
const liveUseGpus = hostNvidiaSmi.ok && dockerNvidiaSmi.ok;
|
|
5326
|
+
const mismatch = expectedUseGpus !== liveUseGpus;
|
|
5327
|
+
return {
|
|
5328
|
+
ok: !mismatch,
|
|
5329
|
+
expectedUseGpus,
|
|
5330
|
+
liveUseGpus,
|
|
5331
|
+
mismatch,
|
|
5332
|
+
mismatchError: mismatch
|
|
5333
|
+
? [
|
|
5334
|
+
"Tokamak CLI Docker GPU metadata does not match live NVIDIA/Docker GPU probes.",
|
|
5335
|
+
`metadata useGpus=${expectedUseGpus}; live useGpus=${liveUseGpus}.`,
|
|
5336
|
+
].join(" ")
|
|
5337
|
+
: null,
|
|
5338
|
+
probeImage: DOCKER_CUDA_PROBE_IMAGE,
|
|
5339
|
+
hostNvidiaSmi,
|
|
5340
|
+
dockerNvidiaSmi,
|
|
5341
|
+
};
|
|
5342
|
+
}
|
|
5343
|
+
|
|
5344
|
+
function runProbe(command, args) {
|
|
5345
|
+
const result = spawnSync(command, args, {
|
|
5346
|
+
encoding: "utf8",
|
|
5347
|
+
timeout: DOCTOR_GPU_PROBE_TIMEOUT_MS,
|
|
5348
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5349
|
+
});
|
|
5350
|
+
return {
|
|
5351
|
+
command,
|
|
5352
|
+
args,
|
|
5353
|
+
ok: !result.error && result.status === 0,
|
|
5354
|
+
status: result.status,
|
|
5355
|
+
signal: result.signal,
|
|
5356
|
+
error: result.error ? result.error.message : null,
|
|
5357
|
+
stdout: stripAnsi(result.stdout ?? "").trim(),
|
|
5358
|
+
stderr: stripAnsi(result.stderr ?? "").trim(),
|
|
5359
|
+
timedOut: result.error?.code === "ETIMEDOUT",
|
|
5360
|
+
};
|
|
5361
|
+
}
|
|
5362
|
+
|
|
5363
|
+
function summarizeProbeResult(result) {
|
|
5364
|
+
return {
|
|
5365
|
+
command: [result.command, ...result.args].join(" "),
|
|
5366
|
+
ok: result.ok,
|
|
5367
|
+
status: result.status,
|
|
5368
|
+
signal: result.signal,
|
|
5369
|
+
error: result.error,
|
|
5370
|
+
timedOut: result.timedOut,
|
|
5371
|
+
stdout: truncateText(result.stdout, 2000),
|
|
5372
|
+
stderr: truncateText(result.stderr, 2000),
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
function truncateText(value, maxLength) {
|
|
5377
|
+
const text = String(value ?? "");
|
|
5378
|
+
if (text.length <= maxLength) {
|
|
5379
|
+
return text;
|
|
5380
|
+
}
|
|
5381
|
+
return `${text.slice(0, maxLength)}...`;
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
function parseJsonReport(value) {
|
|
5385
|
+
try {
|
|
5386
|
+
return JSON.parse(value);
|
|
5387
|
+
} catch {
|
|
5388
|
+
return null;
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
|
|
5392
|
+
function resolveTokamakCliCacheRoot() {
|
|
5393
|
+
return path.resolve(process.env.TOKAMAK_ZKEVM_CLI_CACHE_DIR ?? path.join(os.homedir(), ".tokamak-zk-evm"));
|
|
5394
|
+
}
|
|
5395
|
+
|
|
5396
|
+
function readTokamakCliInstallations(cacheRoot) {
|
|
5397
|
+
if (!fs.existsSync(cacheRoot)) {
|
|
5398
|
+
return [];
|
|
5399
|
+
}
|
|
5400
|
+
return fs.readdirSync(cacheRoot, { withFileTypes: true })
|
|
5401
|
+
.filter((entry) => entry.isDirectory())
|
|
5402
|
+
.map((entry) => {
|
|
5403
|
+
const platformDir = path.join(cacheRoot, entry.name);
|
|
5404
|
+
const statePath = path.join(platformDir, "installation.json");
|
|
5405
|
+
if (!fs.existsSync(statePath)) {
|
|
5406
|
+
return null;
|
|
5407
|
+
}
|
|
5408
|
+
const state = readJsonIfExists(statePath);
|
|
5409
|
+
const dockerBootstrapPath = path.join(platformDir, "docker", "bootstrap.json");
|
|
5410
|
+
const docker = readJsonIfExists(dockerBootstrapPath);
|
|
5411
|
+
return {
|
|
5412
|
+
platform: entry.name,
|
|
5413
|
+
statePath,
|
|
5414
|
+
runtimeRoot: path.join(platformDir, "runtime"),
|
|
5415
|
+
installMode: state?.installMode ?? (docker ? "docker" : null),
|
|
5416
|
+
packageVersion: state?.packageVersion ?? docker?.packageVersion ?? null,
|
|
5417
|
+
installedAt: state?.installedAt ?? null,
|
|
5418
|
+
dockerBootstrapPath,
|
|
5419
|
+
docker,
|
|
5420
|
+
};
|
|
5421
|
+
})
|
|
5422
|
+
.filter(Boolean);
|
|
5423
|
+
}
|
|
5424
|
+
|
|
5425
|
+
function parseRuntimeRootFromTokamakDoctorOutput(output) {
|
|
5426
|
+
const match = String(output ?? "").match(/^\[ ok \] Runtime workspace:\s*(.+)$/m);
|
|
5427
|
+
return match ? path.resolve(match[1].trim()) : null;
|
|
5428
|
+
}
|
|
5429
|
+
|
|
5430
|
+
function stripAnsi(value) {
|
|
5431
|
+
return String(value ?? "").replace(/\u001b\[[0-9;]*m/g, "");
|
|
5432
|
+
}
|
|
5433
|
+
|
|
4885
5434
|
async function installPrivateStateCliArtifacts({
|
|
4886
5435
|
dappName,
|
|
4887
5436
|
indexFileId = process.env.PRIVATE_STATE_DRIVE_ARTIFACT_INDEX_FILE_ID
|