@tokamak-private-dapps/private-state-cli 0.1.1 → 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 CHANGED
@@ -1,5 +1,10 @@
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
+
3
8
  ## 0.1.1 - 2026-04-28
4
9
 
5
10
  - Updated channel balance proof generation to use the fixed Groth16 runtime workspace proof paths.
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:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokamak-private-dapps/private-state-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Command-line client for the Tokamak private-state DApp.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "Tokamak Network",
@@ -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);
@@ -4217,6 +4242,10 @@ function parseArgs(argv) {
4217
4242
  parsed.command = "--install";
4218
4243
  parsed.positional = ["--install"];
4219
4244
  }
4245
+ if (!parsed.command && parsed.doctor === true) {
4246
+ parsed.command = "--doctor";
4247
+ parsed.positional = ["--doctor"];
4248
+ }
4220
4249
  return parsed;
4221
4250
  }
4222
4251
 
@@ -4421,6 +4450,10 @@ function assertUninstallZkEvmArgs(args) {
4421
4450
  assertAllowedCommandKeys(args, "uninstall-zk-evm", new Set(["command", "positional"]), "no options");
4422
4451
  }
4423
4452
 
4453
+ function assertDoctorArgs(args) {
4454
+ assertAllowedCommandKeys(args, "--doctor", new Set(["command", "positional", "doctor"]), "no options");
4455
+ }
4456
+
4424
4457
  function assertMintNotesArgs(args) {
4425
4458
  requireArg(args.amounts, "--amounts");
4426
4459
  assertWalletPasswordArgs(args, "mint-notes", ["amounts"], "--wallet, --password, --network, and --amounts");
@@ -4603,6 +4636,9 @@ Commands:
4603
4636
  uninstall-zk-evm
4604
4637
  Remove the Tokamak zk-EVM CLI runtime workspace
4605
4638
 
4639
+ --doctor
4640
+ Check private-state CLI package versions, runtime install state, Docker mode, CUDA mode, and deployment artifacts
4641
+
4606
4642
  create-channel --channel-name <NAME> --join-fee <TOKENS> --network <NAME> --private-key <HEX> --alchemy-api-key <KEY>
4607
4643
  Create a bridge channel and initialize its workspace
4608
4644
 
@@ -4882,6 +4918,248 @@ function privateStateCliArtifactPaths(cacheBaseRoot = resolveArtifactCacheBaseRo
4882
4918
  };
4883
4919
  }
4884
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
+
4885
5163
  async function installPrivateStateCliArtifacts({
4886
5164
  dappName,
4887
5165
  indexFileId = process.env.PRIVATE_STATE_DRIVE_ARTIFACT_INDEX_FILE_ID