@tokamak-private-dapps/private-state-cli 0.1.0 → 0.1.2
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 +9 -0
- package/README.md +14 -0
- package/package.json +1 -1
- package/private-state-bridge-cli.mjs +281 -48
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 - 2026-04-28
|
|
4
|
+
|
|
5
|
+
- Added `private-state-cli --doctor` to report CLI and install-time dependency versions through `tokamak-l2js`.
|
|
6
|
+
- Reported Tokamak zk-EVM runtime install mode, Docker mode, and CUDA runtime metadata.
|
|
7
|
+
|
|
8
|
+
## 0.1.1 - 2026-04-28
|
|
9
|
+
|
|
10
|
+
- Updated channel balance proof generation to use the fixed Groth16 runtime workspace proof paths.
|
|
11
|
+
|
|
3
12
|
## 0.1.0 - 2026-04-27
|
|
4
13
|
|
|
5
14
|
- Added the initial independently publishable private-state CLI package.
|
package/README.md
CHANGED
|
@@ -27,6 +27,12 @@ Run the CLI with:
|
|
|
27
27
|
private-state-cli <command> ...
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Check the installed package and runtime state with:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
private-state-cli --doctor
|
|
34
|
+
```
|
|
35
|
+
|
|
30
36
|
## Commands
|
|
31
37
|
|
|
32
38
|
The normal private-state flow is:
|
|
@@ -44,6 +50,10 @@ The normal private-state flow is:
|
|
|
44
50
|
|
|
45
51
|
Use `private-state-cli --help` for the full command list and required options.
|
|
46
52
|
|
|
53
|
+
`private-state-cli --doctor` reports the CLI package version, dependency versions recorded by the last
|
|
54
|
+
`private-state-cli --install`, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
|
|
55
|
+
install mode, Docker mode, and CUDA runtime metadata.
|
|
56
|
+
|
|
47
57
|
## Workspace
|
|
48
58
|
|
|
49
59
|
The CLI stores user workspaces under:
|
|
@@ -59,6 +69,10 @@ Wallet data is encrypted with the password supplied to `join-channel` or `recove
|
|
|
59
69
|
Proof-backed commands require installed bridge, DApp, and Groth16 artifacts. Run `private-state-cli --install` before
|
|
60
70
|
using bridge-facing commands on a new machine.
|
|
61
71
|
|
|
72
|
+
Channel balance commands such as `deposit-channel` and `withdraw-channel` use the installed Groth16 runtime workspace
|
|
73
|
+
directly. Proof generation writes to the fixed workspace paths under `~/tokamak-private-channels/groth16/proof`; the CLI
|
|
74
|
+
does not pass custom `--zkey`, proof-output, or public-output paths to the Groth16 prover.
|
|
75
|
+
|
|
62
76
|
Release order matters for npm publication. `@tokamak-private-dapps/common-library` and
|
|
63
77
|
`@tokamak-private-dapps/groth16` must be published before this package version.
|
|
64
78
|
|
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,7 +82,9 @@ 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;
|
|
@@ -221,6 +225,12 @@ async function main() {
|
|
|
221
225
|
return;
|
|
222
226
|
}
|
|
223
227
|
|
|
228
|
+
if (args.command === "--doctor") {
|
|
229
|
+
assertDoctorArgs(args);
|
|
230
|
+
await handleDoctor({ args });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
224
234
|
const walletCommandHandlers = {
|
|
225
235
|
"mint-notes": {
|
|
226
236
|
assert: assertMintNotesArgs,
|
|
@@ -937,6 +947,12 @@ async function handleInstallZkEvm({ args }) {
|
|
|
937
947
|
dappName: PRIVATE_STATE_DAPP_LABEL,
|
|
938
948
|
localDeploymentBaseRoot,
|
|
939
949
|
});
|
|
950
|
+
const installManifest = writePrivateStateCliInstallManifest({
|
|
951
|
+
dockerRequested: Boolean(args.docker),
|
|
952
|
+
includeLocalArtifacts: Boolean(args.includeLocalArtifacts),
|
|
953
|
+
localDeploymentBaseRoot,
|
|
954
|
+
deploymentArtifacts,
|
|
955
|
+
});
|
|
940
956
|
printJson({
|
|
941
957
|
action: "install",
|
|
942
958
|
tokamakCli: tokamakCliBaseArgs[0],
|
|
@@ -953,6 +969,7 @@ async function handleInstallZkEvm({ args }) {
|
|
|
953
969
|
dappTimestamp: entry.dappTimestamp,
|
|
954
970
|
artifactDir: entry.artifactDir,
|
|
955
971
|
})),
|
|
972
|
+
installManifestPath: installManifest.manifestPath,
|
|
956
973
|
});
|
|
957
974
|
}
|
|
958
975
|
|
|
@@ -972,6 +989,14 @@ async function handleUninstallZkEvm() {
|
|
|
972
989
|
});
|
|
973
990
|
}
|
|
974
991
|
|
|
992
|
+
async function handleDoctor() {
|
|
993
|
+
const report = buildDoctorReport();
|
|
994
|
+
printJson(report);
|
|
995
|
+
if (!report.ok) {
|
|
996
|
+
process.exitCode = 1;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
975
1000
|
async function handleGetMyAddress({ args, provider }) {
|
|
976
1001
|
const { wallet, walletMetadata } = loadUnlockedWalletWithMetadata(args);
|
|
977
1002
|
const { signer, l2Identity } = restoreWalletParticipant(wallet, provider);
|
|
@@ -3351,7 +3376,6 @@ async function assertWorkspaceAlignedWithChain(context) {
|
|
|
3351
3376
|
}
|
|
3352
3377
|
|
|
3353
3378
|
async function buildGrothTransition({ operationDir, workspace, stateManager, vaultAddress, keyHex, nextValue }) {
|
|
3354
|
-
const grothArtifacts = loadGroth16UpdateTreeArtifacts(Number(workspace.chainId));
|
|
3355
3379
|
const vaultAddressObj = createAddressFromString(vaultAddress);
|
|
3356
3380
|
const keyBigInt = ethers.toBigInt(keyHex);
|
|
3357
3381
|
const proof = stateManager.merkleTrees.getProof(vaultAddressObj, keyBigInt);
|
|
@@ -3378,22 +3402,14 @@ async function buildGrothTransition({ operationDir, workspace, stateManager, vau
|
|
|
3378
3402
|
};
|
|
3379
3403
|
|
|
3380
3404
|
const inputPath = path.join(operationDir, "input.json");
|
|
3381
|
-
const proofOutputPath = path.join(operationDir, "proof.json");
|
|
3382
|
-
const publicOutputPath = path.join(operationDir, "public.json");
|
|
3383
3405
|
writeJson(inputPath, input);
|
|
3384
|
-
await generateUpdateTreeProof([
|
|
3406
|
+
const proofManifest = await generateUpdateTreeProof([
|
|
3385
3407
|
"--input",
|
|
3386
3408
|
inputPath,
|
|
3387
|
-
"--zkey",
|
|
3388
|
-
grothArtifacts.zkeyPath,
|
|
3389
|
-
"--proof-output",
|
|
3390
|
-
proofOutputPath,
|
|
3391
|
-
"--public-output",
|
|
3392
|
-
publicOutputPath,
|
|
3393
3409
|
]);
|
|
3394
3410
|
|
|
3395
|
-
const proofJson = readJson(
|
|
3396
|
-
const publicSignals = readJson(
|
|
3411
|
+
const proofJson = readJson(proofManifest.proofPath);
|
|
3412
|
+
const publicSignals = readJson(proofManifest.publicPath);
|
|
3397
3413
|
|
|
3398
3414
|
return {
|
|
3399
3415
|
input,
|
|
@@ -4153,42 +4169,6 @@ function networkNameFromChainId(chainId) {
|
|
|
4153
4169
|
throw new Error(`Unsupported chain ID for private-state bridge CLI: ${chainId}`);
|
|
4154
4170
|
}
|
|
4155
4171
|
|
|
4156
|
-
function groth16UpdateTreeManifestPath(chainId) {
|
|
4157
|
-
return requireFlatDeploymentArtifactPathsForChainId(chainId).grothManifestPath;
|
|
4158
|
-
}
|
|
4159
|
-
|
|
4160
|
-
function resolveDeployManifestArtifactPath(manifestPath, artifactPath) {
|
|
4161
|
-
expect(
|
|
4162
|
-
typeof artifactPath === "string" && artifactPath.length > 0,
|
|
4163
|
-
`Invalid artifact path entry in ${manifestPath}.`,
|
|
4164
|
-
);
|
|
4165
|
-
return path.isAbsolute(artifactPath)
|
|
4166
|
-
? artifactPath
|
|
4167
|
-
: path.resolve(path.dirname(manifestPath), artifactPath);
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
|
-
function loadGroth16UpdateTreeArtifacts(chainId) {
|
|
4171
|
-
const manifestPath = groth16UpdateTreeManifestPath(chainId);
|
|
4172
|
-
expect(
|
|
4173
|
-
fs.existsSync(manifestPath),
|
|
4174
|
-
`Missing Groth16 updateTree manifest for chain ${chainId}: ${manifestPath}.`,
|
|
4175
|
-
);
|
|
4176
|
-
|
|
4177
|
-
const manifest = readJson(manifestPath);
|
|
4178
|
-
const zkeyPath = resolveDeployManifestArtifactPath(manifestPath, manifest.artifacts?.zkeyPath);
|
|
4179
|
-
|
|
4180
|
-
for (const [label, artifactPath] of [
|
|
4181
|
-
["Groth16 updateTree proving key", zkeyPath],
|
|
4182
|
-
]) {
|
|
4183
|
-
expect(fs.existsSync(artifactPath), `Missing ${label} for chain ${chainId}: ${artifactPath}.`);
|
|
4184
|
-
}
|
|
4185
|
-
|
|
4186
|
-
return {
|
|
4187
|
-
manifestPath,
|
|
4188
|
-
zkeyPath,
|
|
4189
|
-
};
|
|
4190
|
-
}
|
|
4191
|
-
|
|
4192
4172
|
function findStorageSlot(storageLayoutManifest, contractName, label) {
|
|
4193
4173
|
const contract = storageLayoutManifest.contracts[contractName];
|
|
4194
4174
|
if (!contract) {
|
|
@@ -4262,6 +4242,10 @@ function parseArgs(argv) {
|
|
|
4262
4242
|
parsed.command = "--install";
|
|
4263
4243
|
parsed.positional = ["--install"];
|
|
4264
4244
|
}
|
|
4245
|
+
if (!parsed.command && parsed.doctor === true) {
|
|
4246
|
+
parsed.command = "--doctor";
|
|
4247
|
+
parsed.positional = ["--doctor"];
|
|
4248
|
+
}
|
|
4265
4249
|
return parsed;
|
|
4266
4250
|
}
|
|
4267
4251
|
|
|
@@ -4466,6 +4450,10 @@ function assertUninstallZkEvmArgs(args) {
|
|
|
4466
4450
|
assertAllowedCommandKeys(args, "uninstall-zk-evm", new Set(["command", "positional"]), "no options");
|
|
4467
4451
|
}
|
|
4468
4452
|
|
|
4453
|
+
function assertDoctorArgs(args) {
|
|
4454
|
+
assertAllowedCommandKeys(args, "--doctor", new Set(["command", "positional", "doctor"]), "no options");
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4469
4457
|
function assertMintNotesArgs(args) {
|
|
4470
4458
|
requireArg(args.amounts, "--amounts");
|
|
4471
4459
|
assertWalletPasswordArgs(args, "mint-notes", ["amounts"], "--wallet, --password, --network, and --amounts");
|
|
@@ -4648,6 +4636,9 @@ Commands:
|
|
|
4648
4636
|
uninstall-zk-evm
|
|
4649
4637
|
Remove the Tokamak zk-EVM CLI runtime workspace
|
|
4650
4638
|
|
|
4639
|
+
--doctor
|
|
4640
|
+
Check private-state CLI package versions, runtime install state, Docker mode, CUDA mode, and deployment artifacts
|
|
4641
|
+
|
|
4651
4642
|
create-channel --channel-name <NAME> --join-fee <TOKENS> --network <NAME> --private-key <HEX> --alchemy-api-key <KEY>
|
|
4652
4643
|
Create a bridge channel and initialize its workspace
|
|
4653
4644
|
|
|
@@ -4927,6 +4918,248 @@ function privateStateCliArtifactPaths(cacheBaseRoot = resolveArtifactCacheBaseRo
|
|
|
4927
4918
|
};
|
|
4928
4919
|
}
|
|
4929
4920
|
|
|
4921
|
+
function privateStateCliInstallManifestPath(cacheBaseRoot = resolveArtifactCacheBaseRoot()) {
|
|
4922
|
+
return path.join(privateStateCliArtifactRoot(cacheBaseRoot), "install-manifest.json");
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4925
|
+
function writePrivateStateCliInstallManifest({
|
|
4926
|
+
dockerRequested,
|
|
4927
|
+
includeLocalArtifacts,
|
|
4928
|
+
localDeploymentBaseRoot,
|
|
4929
|
+
deploymentArtifacts,
|
|
4930
|
+
}) {
|
|
4931
|
+
const manifestPath = privateStateCliInstallManifestPath(deploymentArtifacts.cacheBaseRoot);
|
|
4932
|
+
const manifest = {
|
|
4933
|
+
installedAt: new Date().toISOString(),
|
|
4934
|
+
package: summarizePackageReport(readPackageReport({
|
|
4935
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
4936
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
4937
|
+
})),
|
|
4938
|
+
dependencies: collectDependencyPackageReports().map(summarizePackageReport),
|
|
4939
|
+
install: {
|
|
4940
|
+
dockerRequested,
|
|
4941
|
+
includeLocalArtifacts,
|
|
4942
|
+
localDeploymentBaseRoot,
|
|
4943
|
+
artifactCacheRoot: deploymentArtifacts.cacheBaseRoot,
|
|
4944
|
+
installedDeploymentArtifacts: deploymentArtifacts.installed.map((entry) => ({
|
|
4945
|
+
chainId: entry.chainId,
|
|
4946
|
+
source: entry.source,
|
|
4947
|
+
bridgeTimestamp: entry.bridgeTimestamp,
|
|
4948
|
+
dappTimestamp: entry.dappTimestamp,
|
|
4949
|
+
})),
|
|
4950
|
+
},
|
|
4951
|
+
};
|
|
4952
|
+
writeJson(manifestPath, manifest);
|
|
4953
|
+
return { manifestPath, manifest };
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
function summarizePackageReport(report) {
|
|
4957
|
+
return {
|
|
4958
|
+
name: report.name,
|
|
4959
|
+
version: report.version,
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4963
|
+
function buildDoctorReport() {
|
|
4964
|
+
const cacheBaseRoot = resolveArtifactCacheBaseRoot();
|
|
4965
|
+
const installManifestPath = privateStateCliInstallManifestPath(cacheBaseRoot);
|
|
4966
|
+
const installManifest = readJsonIfExists(installManifestPath);
|
|
4967
|
+
const dependencyReports = collectDependencyPackageReports(installManifest);
|
|
4968
|
+
const tokamakCli = inspectTokamakCliRuntime();
|
|
4969
|
+
const checks = [
|
|
4970
|
+
{
|
|
4971
|
+
name: "dependency package versions",
|
|
4972
|
+
ok: dependencyReports.every((entry) => entry.ok),
|
|
4973
|
+
details: dependencyReports.map((entry) => ({
|
|
4974
|
+
name: entry.name,
|
|
4975
|
+
currentVersion: entry.version,
|
|
4976
|
+
installVersion: entry.installVersion,
|
|
4977
|
+
ok: entry.ok,
|
|
4978
|
+
error: entry.error,
|
|
4979
|
+
})),
|
|
4980
|
+
},
|
|
4981
|
+
{
|
|
4982
|
+
name: "tokamak zk-evm runtime",
|
|
4983
|
+
ok: tokamakCli.installed,
|
|
4984
|
+
details: {
|
|
4985
|
+
doctorStatus: tokamakCli.doctor.status,
|
|
4986
|
+
runtimeRoot: tokamakCli.runtimeRoot,
|
|
4987
|
+
installations: tokamakCli.installations.map(({ platform, installMode, packageVersion, docker }) => ({
|
|
4988
|
+
platform,
|
|
4989
|
+
installMode,
|
|
4990
|
+
packageVersion,
|
|
4991
|
+
dockerEnvironment: docker?.dockerEnvironment ?? null,
|
|
4992
|
+
useGpus: docker?.useGpus ?? null,
|
|
4993
|
+
})),
|
|
4994
|
+
},
|
|
4995
|
+
},
|
|
4996
|
+
];
|
|
4997
|
+
|
|
4998
|
+
return {
|
|
4999
|
+
action: "doctor",
|
|
5000
|
+
ok: checks.every((check) => check.ok),
|
|
5001
|
+
generatedAt: new Date().toISOString(),
|
|
5002
|
+
package: readPackageReport({
|
|
5003
|
+
name: "@tokamak-private-dapps/private-state-cli",
|
|
5004
|
+
packageJsonPath: path.join(privateStateCliPackageRoot, "package.json"),
|
|
5005
|
+
}),
|
|
5006
|
+
installManifest: {
|
|
5007
|
+
path: installManifestPath,
|
|
5008
|
+
exists: Boolean(installManifest),
|
|
5009
|
+
installedAt: installManifest?.installedAt ?? null,
|
|
5010
|
+
dockerRequested: installManifest?.install?.dockerRequested ?? null,
|
|
5011
|
+
includeLocalArtifacts: installManifest?.install?.includeLocalArtifacts ?? null,
|
|
5012
|
+
},
|
|
5013
|
+
dependencies: dependencyReports,
|
|
5014
|
+
tokamakCli,
|
|
5015
|
+
checks,
|
|
5016
|
+
};
|
|
5017
|
+
}
|
|
5018
|
+
|
|
5019
|
+
function collectDependencyPackageReports(installManifest = null) {
|
|
5020
|
+
const installVersions = new Map(
|
|
5021
|
+
Array.isArray(installManifest?.dependencies)
|
|
5022
|
+
? installManifest.dependencies.map((entry) => [entry.name, entry.version])
|
|
5023
|
+
: [],
|
|
5024
|
+
);
|
|
5025
|
+
const targets = [
|
|
5026
|
+
{
|
|
5027
|
+
name: "@tokamak-zk-evm/cli",
|
|
5028
|
+
packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
|
|
5029
|
+
},
|
|
5030
|
+
{
|
|
5031
|
+
name: "@tokamak-private-dapps/groth16",
|
|
5032
|
+
resolveTarget: "@tokamak-private-dapps/groth16/public-drive-crs",
|
|
5033
|
+
},
|
|
5034
|
+
{
|
|
5035
|
+
name: "@tokamak-private-dapps/common-library",
|
|
5036
|
+
resolveTarget: "@tokamak-private-dapps/common-library/artifact-cache",
|
|
5037
|
+
},
|
|
5038
|
+
{ name: "tokamak-l2js", resolveTarget: "tokamak-l2js" },
|
|
5039
|
+
];
|
|
5040
|
+
|
|
5041
|
+
return targets.map((target) => {
|
|
5042
|
+
const report = readPackageReport(target);
|
|
5043
|
+
const installVersion = installVersions.get(report.name) ?? null;
|
|
5044
|
+
return {
|
|
5045
|
+
...report,
|
|
5046
|
+
installVersion,
|
|
5047
|
+
ok: Boolean(report.version) && (installVersion === null || installVersion === report.version),
|
|
5048
|
+
};
|
|
5049
|
+
});
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
function readPackageReport({ name, packageJsonPath = null, resolveTarget = null }) {
|
|
5053
|
+
try {
|
|
5054
|
+
const resolvedPackageJsonPath = packageJsonPath
|
|
5055
|
+
? path.resolve(packageJsonPath)
|
|
5056
|
+
: findPackageJsonForName(path.dirname(require.resolve(resolveTarget ?? name)), name);
|
|
5057
|
+
const packageJson = readJson(resolvedPackageJsonPath);
|
|
5058
|
+
return {
|
|
5059
|
+
name: packageJson.name ?? name,
|
|
5060
|
+
version: packageJson.version ?? null,
|
|
5061
|
+
packageRoot: path.dirname(resolvedPackageJsonPath),
|
|
5062
|
+
error: null,
|
|
5063
|
+
};
|
|
5064
|
+
} catch (error) {
|
|
5065
|
+
return {
|
|
5066
|
+
name,
|
|
5067
|
+
version: null,
|
|
5068
|
+
packageRoot: null,
|
|
5069
|
+
error: error.message,
|
|
5070
|
+
ok: false,
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
|
|
5075
|
+
function findPackageJsonForName(startDir, expectedName) {
|
|
5076
|
+
let current = path.resolve(startDir);
|
|
5077
|
+
while (current !== path.dirname(current)) {
|
|
5078
|
+
const candidate = path.join(current, "package.json");
|
|
5079
|
+
if (fs.existsSync(candidate)) {
|
|
5080
|
+
const packageJson = readJson(candidate);
|
|
5081
|
+
if (packageJson.name === expectedName) {
|
|
5082
|
+
return candidate;
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
current = path.dirname(current);
|
|
5086
|
+
}
|
|
5087
|
+
throw new Error(`Cannot locate package.json for ${expectedName} above ${startDir}.`);
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
function inspectTokamakCliRuntime() {
|
|
5091
|
+
const doctor = runCaptured(tokamakCliCommand, [...tokamakCliBaseArgs, "--doctor"], {
|
|
5092
|
+
cwd: resolveTokamakCliPackageRoot(),
|
|
5093
|
+
});
|
|
5094
|
+
const doctorOutput = stripAnsi(`${doctor.stdout}${doctor.stderr}`);
|
|
5095
|
+
const runtimeRoot = parseRuntimeRootFromTokamakDoctorOutput(doctorOutput);
|
|
5096
|
+
const cacheRoot = resolveTokamakCliCacheRoot();
|
|
5097
|
+
const installations = readTokamakCliInstallations(cacheRoot);
|
|
5098
|
+
const dockerModeInstalled = installations.some((entry) => entry.installMode === "docker" || entry.docker);
|
|
5099
|
+
const cudaCompatible = installations.some((entry) => entry.docker?.useGpus === true);
|
|
5100
|
+
|
|
5101
|
+
return {
|
|
5102
|
+
installed: doctor.status === 0 || installations.length > 0,
|
|
5103
|
+
packageRoot: resolveTokamakCliPackageRoot(),
|
|
5104
|
+
cacheRoot,
|
|
5105
|
+
runtimeRoot,
|
|
5106
|
+
packageVersion: readPackageReport({
|
|
5107
|
+
name: "@tokamak-zk-evm/cli",
|
|
5108
|
+
packageJsonPath: path.join(resolveTokamakCliPackageRoot(), "package.json"),
|
|
5109
|
+
}).version,
|
|
5110
|
+
dockerModeInstalled,
|
|
5111
|
+
cudaCompatible,
|
|
5112
|
+
doctor: {
|
|
5113
|
+
status: doctor.status,
|
|
5114
|
+
stdout: stripAnsi(doctor.stdout).trim(),
|
|
5115
|
+
stderr: stripAnsi(doctor.stderr).trim(),
|
|
5116
|
+
},
|
|
5117
|
+
installations,
|
|
5118
|
+
};
|
|
5119
|
+
}
|
|
5120
|
+
|
|
5121
|
+
function resolveTokamakCliCacheRoot() {
|
|
5122
|
+
return path.resolve(process.env.TOKAMAK_ZKEVM_CLI_CACHE_DIR ?? path.join(os.homedir(), ".tokamak-zk-evm"));
|
|
5123
|
+
}
|
|
5124
|
+
|
|
5125
|
+
function readTokamakCliInstallations(cacheRoot) {
|
|
5126
|
+
if (!fs.existsSync(cacheRoot)) {
|
|
5127
|
+
return [];
|
|
5128
|
+
}
|
|
5129
|
+
return fs.readdirSync(cacheRoot, { withFileTypes: true })
|
|
5130
|
+
.filter((entry) => entry.isDirectory())
|
|
5131
|
+
.map((entry) => {
|
|
5132
|
+
const platformDir = path.join(cacheRoot, entry.name);
|
|
5133
|
+
const statePath = path.join(platformDir, "installation.json");
|
|
5134
|
+
if (!fs.existsSync(statePath)) {
|
|
5135
|
+
return null;
|
|
5136
|
+
}
|
|
5137
|
+
const state = readJsonIfExists(statePath);
|
|
5138
|
+
const dockerBootstrapPath = path.join(platformDir, "docker", "bootstrap.json");
|
|
5139
|
+
const docker = readJsonIfExists(dockerBootstrapPath);
|
|
5140
|
+
return {
|
|
5141
|
+
platform: entry.name,
|
|
5142
|
+
statePath,
|
|
5143
|
+
runtimeRoot: path.join(platformDir, "runtime"),
|
|
5144
|
+
installMode: state?.installMode ?? (docker ? "docker" : null),
|
|
5145
|
+
packageVersion: state?.packageVersion ?? docker?.packageVersion ?? null,
|
|
5146
|
+
installedAt: state?.installedAt ?? null,
|
|
5147
|
+
dockerBootstrapPath,
|
|
5148
|
+
docker,
|
|
5149
|
+
};
|
|
5150
|
+
})
|
|
5151
|
+
.filter(Boolean);
|
|
5152
|
+
}
|
|
5153
|
+
|
|
5154
|
+
function parseRuntimeRootFromTokamakDoctorOutput(output) {
|
|
5155
|
+
const match = String(output ?? "").match(/^\[ ok \] Runtime workspace:\s*(.+)$/m);
|
|
5156
|
+
return match ? path.resolve(match[1].trim()) : null;
|
|
5157
|
+
}
|
|
5158
|
+
|
|
5159
|
+
function stripAnsi(value) {
|
|
5160
|
+
return String(value ?? "").replace(/\u001b\[[0-9;]*m/g, "");
|
|
5161
|
+
}
|
|
5162
|
+
|
|
4930
5163
|
async function installPrivateStateCliArtifacts({
|
|
4931
5164
|
dappName,
|
|
4932
5165
|
indexFileId = process.env.PRIVATE_STATE_DRIVE_ARTIFACT_INDEX_FILE_ID
|