@hardkas/cli 0.2.2-alpha → 0.3.0-alpha

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.
@@ -0,0 +1,50 @@
1
+ import {
2
+ UI
3
+ } from "./chunk-K7XPWWIO.js";
4
+
5
+ // src/runners/replay-verify-runner.ts
6
+ import { readTxPlanArtifact, readTxReceiptArtifact } from "@hardkas/artifacts";
7
+ import { loadOrCreateLocalnetState, verifyReplay } from "@hardkas/localnet";
8
+ import path from "path";
9
+ import pc from "picocolors";
10
+ async function runReplayVerify(options) {
11
+ const artifactDir = path.resolve(process.cwd(), options.path);
12
+ const planPath = path.join(artifactDir, "tx-plan.json");
13
+ const receiptPath = path.join(artifactDir, "tx-receipt.json");
14
+ UI.header(`Replay Verification: ${path.basename(artifactDir)}`);
15
+ try {
16
+ const plan = await readTxPlanArtifact(planPath);
17
+ const receipt = await readTxReceiptArtifact(receiptPath);
18
+ const state = await loadOrCreateLocalnetState();
19
+ const report = verifyReplay(state, plan, receipt);
20
+ console.log(` Workflow: ${report.checks.workflowDeterministic === "reproduced" ? pc.green("\u2713 REPRODUCED") : pc.red("\u2717 DIVERGED")}`);
21
+ console.log(` Consensus: ${pc.yellow("- UNIMPLEMENTED")} (HardKAS does not validate network consensus)`);
22
+ console.log(` L2 Logic: ${pc.yellow("- UNIMPLEMENTED")} (HardKAS does not validate bridge protocol)`);
23
+ console.log("");
24
+ if (report.invariantsOk) {
25
+ UI.success("Artifact workflow is deterministic.");
26
+ console.log(` Plan Hash: \u2713 MATCH`);
27
+ console.log(` Semantic Diff: \u2713 NO CHANGES`);
28
+ } else {
29
+ UI.error("Replay Verification Failed");
30
+ if (report.divergences.length > 0) {
31
+ console.log(pc.bold("\n Divergences found:"));
32
+ for (const div of report.divergences) {
33
+ console.log(` ${pc.cyan(div.path)}:`);
34
+ console.log(` Expected: ${pc.green(JSON.stringify(div.expected))}`);
35
+ console.log(` Actual: ${pc.red(JSON.stringify(div.actual))}`);
36
+ }
37
+ }
38
+ if (report.errors.length > 0 && report.divergences.length === 0) {
39
+ report.errors.forEach((err) => console.log(` [!] ${err}`));
40
+ }
41
+ process.exitCode = 1;
42
+ }
43
+ } catch (e) {
44
+ UI.error(`Failed to perform replay verification: ${e.message}`);
45
+ process.exitCode = 1;
46
+ }
47
+ }
48
+ export {
49
+ runReplayVerify
50
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  UI
3
- } from "./chunk-M54KNJEH.js";
3
+ } from "./chunk-K7XPWWIO.js";
4
4
 
5
5
  // src/runners/rpc-doctor-runner.ts
6
6
  import { KaspaJsonRpcClient } from "@hardkas/kaspa-rpc";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  UI
3
- } from "./chunk-M54KNJEH.js";
3
+ } from "./chunk-K7XPWWIO.js";
4
4
 
5
5
  // src/runners/snapshot-restore-runner.ts
6
6
  import {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  UI
3
- } from "./chunk-M54KNJEH.js";
3
+ } from "./chunk-K7XPWWIO.js";
4
4
 
5
5
  // src/runners/snapshot-verify-runner.ts
6
6
  import { loadOrCreateLocalnetState, verifySnapshot } from "@hardkas/localnet";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  UI
3
- } from "./chunk-M54KNJEH.js";
3
+ } from "./chunk-K7XPWWIO.js";
4
4
 
5
5
  // src/runners/tx-verify-runner.ts
6
- import { readTxPlanArtifact } from "@hardkas/artifacts";
6
+ import { readTxPlanArtifact, verifyArtifactIntegrity } from "@hardkas/artifacts";
7
7
  import { verifyTxPlanSemantics } from "@hardkas/tx-builder";
8
8
  import { formatSompi } from "@hardkas/core";
9
9
  import path from "path";
@@ -12,6 +12,15 @@ async function runTxVerify(options) {
12
12
  UI.header(`Transaction Verification: ${path.basename(options.path)}`);
13
13
  try {
14
14
  const artifact = await readTxPlanArtifact(absolutePath);
15
+ const integrityResult = await verifyArtifactIntegrity(absolutePath);
16
+ if (!integrityResult.ok) {
17
+ UI.error("INTEGRITY VERIFICATION FAILED");
18
+ integrityResult.issues.forEach((issue) => {
19
+ console.log(` [!] [${issue.code}] ${issue.message}`);
20
+ });
21
+ process.exitCode = 1;
22
+ return;
23
+ }
15
24
  const result = verifyTxPlanSemantics(artifact);
16
25
  if (options.json) {
17
26
  console.log(JSON.stringify(result, (key, value) => typeof value === "bigint" ? value.toString() : value, 2));
@@ -0,0 +1,10 @@
1
+ import {
2
+ UI,
3
+ handleError,
4
+ handleLockError
5
+ } from "./chunk-K7XPWWIO.js";
6
+ export {
7
+ UI,
8
+ handleError,
9
+ handleLockError
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/cli",
3
- "version": "0.2.2-alpha",
3
+ "version": "0.3.0-alpha",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -13,22 +13,24 @@
13
13
  "dependencies": {
14
14
  "commander": "^12.1.0",
15
15
  "enquirer": "^2.4.1",
16
+ "execa": "^9.5.2",
16
17
  "picocolors": "^1.1.1",
17
18
  "zod": "^3.23.8",
18
- "@hardkas/kaspa-rpc": "0.2.2-alpha",
19
- "@hardkas/accounts": "0.2.2-alpha",
20
- "@hardkas/artifacts": "0.2.2-alpha",
21
- "@hardkas/config": "0.2.2-alpha",
22
- "@hardkas/core": "0.2.2-alpha",
23
- "@hardkas/l2": "0.2.2-alpha",
24
- "@hardkas/localnet": "0.2.2-alpha",
25
- "@hardkas/query": "0.2.2-alpha",
26
- "@hardkas/node-orchestrator": "0.2.2-alpha",
27
- "@hardkas/node-runner": "0.2.2-alpha",
28
- "@hardkas/query-store": "0.2.2-alpha",
29
- "@hardkas/simulator": "0.2.2-alpha",
30
- "@hardkas/sdk": "0.2.2-alpha",
31
- "@hardkas/tx-builder": "0.2.2-alpha"
19
+ "@hardkas/accounts": "0.3.0-alpha",
20
+ "@hardkas/artifacts": "0.3.0-alpha",
21
+ "@hardkas/config": "0.3.0-alpha",
22
+ "@hardkas/core": "0.3.0-alpha",
23
+ "@hardkas/query": "0.3.0-alpha",
24
+ "@hardkas/l2": "0.3.0-alpha",
25
+ "@hardkas/kaspa-rpc": "0.3.0-alpha",
26
+ "@hardkas/node-orchestrator": "0.3.0-alpha",
27
+ "@hardkas/localnet": "0.3.0-alpha",
28
+ "@hardkas/node-runner": "0.3.0-alpha",
29
+ "@hardkas/query-store": "0.3.0-alpha",
30
+ "@hardkas/sdk": "0.3.0-alpha",
31
+ "@hardkas/simulator": "0.3.0-alpha",
32
+ "@hardkas/testing": "0.3.0-alpha",
33
+ "@hardkas/tx-builder": "0.3.0-alpha"
32
34
  },
33
35
  "devDependencies": {
34
36
  "tsup": "^8.3.5",
@@ -48,11 +50,15 @@
48
50
  },
49
51
  "homepage": "https://github.com/KasLabDevs/HardKas/tree/main/packages/cli#readme",
50
52
  "scripts": {
51
- "build": "tsup src/index.ts --format esm --clean",
53
+ "build": "tsup src/index.ts --format esm --clean --external vitest",
52
54
  "dev": "tsx src/index.ts",
53
55
  "hardkas": "tsx src/index.ts",
54
56
  "typecheck": "tsc --noEmit",
57
+ "docs:generate-cli": "tsx src/scripts/generate-cli-docs.ts",
58
+ "docs:check-cli": "tsx src/scripts/generate-cli-docs.ts --check",
55
59
  "test": "vitest run",
60
+ "test:docs": "tsx test/docs-drift.node.ts",
61
+ "test:examples": "node --import tsx --test test/examples-acceptance.node.ts",
56
62
  "lint": "eslint ."
57
63
  }
58
64
  }
@@ -1,112 +0,0 @@
1
- import {
2
- UI
3
- } from "./chunk-M54KNJEH.js";
4
-
5
- // src/runners/accounts-keystore-runners.ts
6
- import path from "path";
7
- import enquirer from "enquirer";
8
- import { KeystoreManager } from "@hardkas/accounts";
9
- var { Password } = enquirer;
10
- async function runAccountsKeystoreImport(options) {
11
- const name = options.name || "default";
12
- const address = options.address;
13
- const privateKey = options.privateKey;
14
- if (!options.encrypted) {
15
- throw new Error("Plaintext import is handled by runAccountsRealImport. Use --encrypted for this runner.");
16
- }
17
- UI.warning("HardKAS encrypted keystore is for local developer workflows, not institutional custody.");
18
- UI.info("Do not import mainnet keys unless you fully understand the risks.");
19
- if (!address || !privateKey) {
20
- throw new Error("Address and private key are required for import.");
21
- }
22
- const passwordPrompt = new Password({
23
- name: "password",
24
- message: "Enter keystore password:"
25
- });
26
- const password = await passwordPrompt.run();
27
- if (!password) {
28
- throw new Error("Password cannot be empty.");
29
- }
30
- const confirmPrompt = new Password({
31
- name: "confirm",
32
- message: "Confirm keystore password:"
33
- });
34
- const confirm = await confirmPrompt.run();
35
- if (password !== confirm) {
36
- throw new Error("Passwords do not match.");
37
- }
38
- const keystore = await KeystoreManager.createEncryptedKeystore(
39
- {
40
- address,
41
- privateKey,
42
- network: "devnet"
43
- // Default for now
44
- },
45
- password,
46
- {
47
- label: name,
48
- network: "devnet"
49
- }
50
- );
51
- const keystoreDir = path.join(process.cwd(), ".hardkas", "keystore");
52
- const filePath = path.join(keystoreDir, `${name}.json`);
53
- await KeystoreManager.saveEncryptedKeystore(filePath, keystore);
54
- return {
55
- success: true,
56
- name,
57
- path: filePath,
58
- formatted: `Successfully imported and encrypted account '${name}' to ${filePath}`
59
- };
60
- }
61
- async function runAccountsKeystoreUnlock(options) {
62
- const { name } = options;
63
- const filePath = path.join(process.cwd(), ".hardkas", "keystore", `${name}.json`);
64
- const keystore = await KeystoreManager.loadEncryptedKeystore(filePath);
65
- const passwordPrompt = new Password({
66
- name: "password",
67
- message: `Enter password for account '${name}':`
68
- });
69
- const password = await passwordPrompt.run();
70
- const result = await KeystoreManager.decryptEncryptedKeystore(keystore, password);
71
- if (result.success) {
72
- UI.success(`Password verified for account '${name}'.`);
73
- UI.info("Unlock is session-only. Decrypted key is not persisted.");
74
- } else {
75
- throw new Error(result.error || "Failed to unlock keystore.");
76
- }
77
- }
78
- async function runAccountsKeystoreChangePassword(options) {
79
- const { name } = options;
80
- const filePath = path.join(process.cwd(), ".hardkas", "keystore", `${name}.json`);
81
- const keystore = await KeystoreManager.loadEncryptedKeystore(filePath);
82
- const oldPasswordPrompt = new Password({
83
- name: "old",
84
- message: "Enter current password:"
85
- });
86
- const oldPassword = await oldPasswordPrompt.run();
87
- const newPasswordPrompt = new Password({
88
- name: "new",
89
- message: "Enter new password:"
90
- });
91
- const newPassword = await newPasswordPrompt.run();
92
- const confirmPrompt = new Password({
93
- name: "confirm",
94
- message: "Confirm new password:"
95
- });
96
- const confirm = await confirmPrompt.run();
97
- if (newPassword !== confirm) {
98
- throw new Error("New passwords do not match.");
99
- }
100
- const updatedKeystore = await KeystoreManager.changeKeystorePassword(
101
- keystore,
102
- oldPassword,
103
- newPassword
104
- );
105
- await KeystoreManager.saveEncryptedKeystore(filePath, updatedKeystore);
106
- UI.success(`Successfully changed password for account '${name}'.`);
107
- }
108
- export {
109
- runAccountsKeystoreChangePassword,
110
- runAccountsKeystoreImport,
111
- runAccountsKeystoreUnlock
112
- };
@@ -1,98 +0,0 @@
1
- // src/ui.ts
2
- import pc from "picocolors";
3
- import { formatSompi } from "@hardkas/core";
4
- var UI = {
5
- header(text) {
6
- console.log(pc.bold(pc.magenta(`
7
- \u2550\u2550\u2550 ${text} \u2550\u2550\u2550`)));
8
- },
9
- divider() {
10
- console.log(pc.dim(" " + "\u2500".repeat(50)));
11
- },
12
- info(text) {
13
- console.log(` ${pc.blue("\u2139")} ${text}`);
14
- },
15
- success(text) {
16
- console.log(` ${pc.green("\u2714")} ${text}`);
17
- },
18
- box(title, subtitle) {
19
- const width = 40;
20
- console.log(pc.magenta(` \u2554${"\u2550".repeat(width - 2)}\u2557`));
21
- console.log(pc.magenta(` \u2551${pc.bold(pc.white(title.padStart((width - 2 + title.length) / 2).padEnd(width - 2)))}\u2551`));
22
- if (subtitle) {
23
- console.log(pc.magenta(` \u2551${pc.italic(pc.dim(subtitle.padStart((width - 2 + subtitle.length) / 2).padEnd(width - 2)))}\u2551`));
24
- }
25
- console.log(pc.magenta(` \u255A${"\u2550".repeat(width - 2)}\u255D`));
26
- console.log("");
27
- },
28
- warning(text) {
29
- console.log(pc.yellow(`
30
- \u26A0\uFE0F WARNING:`));
31
- console.log(pc.yellow(` ${text}`));
32
- },
33
- error(msg, suggestion) {
34
- console.error(pc.red(`
35
- \u2717 Error:`));
36
- console.error(pc.red(` ${msg}`));
37
- if (suggestion) {
38
- console.error(pc.cyan(`
39
- \u{1F4A1} Suggestion:`));
40
- console.error(pc.cyan(` ${suggestion}`));
41
- }
42
- },
43
- field(label, value) {
44
- const val = value === void 0 || value === null ? pc.dim("none") : String(value);
45
- console.log(` ${pc.dim(label.padEnd(16))} ${pc.white(val)}`);
46
- },
47
- kas(label, sompi) {
48
- this.field(label, pc.cyan(formatSompi(BigInt(sompi))));
49
- },
50
- footer(hint) {
51
- if (hint) {
52
- console.log(pc.dim(`
53
- Hint: ${hint}`));
54
- }
55
- console.log("");
56
- }
57
- };
58
- function handleError(e, context) {
59
- const msg = e instanceof Error ? e.message : String(e);
60
- const errorObj = e;
61
- let reason = errorObj.reason;
62
- let suggestion = errorObj.suggestion;
63
- if (msg === "Real transaction signing is not available") {
64
- console.error(`
65
- ${msg}`);
66
- if (reason) console.error(`
67
- Reason:
68
- ${reason}`);
69
- if (suggestion) console.error(`
70
- Suggestion:
71
- ${suggestion}
72
- No artifact was written.`);
73
- return;
74
- }
75
- if (!suggestion) {
76
- if (msg.includes("Localnet state not found")) {
77
- suggestion = "Run 'hardkas localnet reset' to initialize the simulated environment.";
78
- } else if (msg.includes("Insufficient funds")) {
79
- suggestion = "Use 'hardkas faucet <address> <amount>' to add funds to your account.";
80
- } else if (msg.includes("Account not found")) {
81
- suggestion = "Check your 'hardkas.config.ts' or use a full Kaspa address.";
82
- } else if (msg.includes("Docker") || msg.includes("container")) {
83
- suggestion = "Ensure Docker is running and you have permissions to manage containers.";
84
- } else if (msg.includes("L2 RPC") || msg.includes("L2 profile")) {
85
- suggestion = "Check your L2 network configuration or pass a valid --url.";
86
- } else if (msg.includes("RPC") || msg.includes("Connection refused")) {
87
- suggestion = "The Kaspa node might still be starting. Try 'hardkas rpc health --wait'.";
88
- } else if (msg.includes("submitTransaction is not exposed")) {
89
- suggestion = "Ensure your node/RPC provider supports transaction submission and you are NOT on mainnet without --allow-mainnet-signing.";
90
- }
91
- }
92
- UI.error(context ? `${context}: ${msg}` : msg, suggestion);
93
- }
94
-
95
- export {
96
- UI,
97
- handleError
98
- };
@@ -1,37 +0,0 @@
1
- import {
2
- UI
3
- } from "./chunk-M54KNJEH.js";
4
-
5
- // src/runners/replay-verify-runner.ts
6
- import { readTxPlanArtifact, readTxReceiptArtifact } from "@hardkas/artifacts";
7
- import { loadOrCreateLocalnetState, verifyReplay } from "@hardkas/localnet";
8
- import path from "path";
9
- async function runReplayVerify(options) {
10
- const artifactDir = path.resolve(process.cwd(), options.path);
11
- const planPath = path.join(artifactDir, "tx-plan.json");
12
- const receiptPath = path.join(artifactDir, "tx-receipt.json");
13
- UI.header(`Replay Verification: ${path.basename(artifactDir)}`);
14
- try {
15
- const plan = await readTxPlanArtifact(planPath);
16
- const receipt = await readTxReceiptArtifact(receiptPath);
17
- const state = await loadOrCreateLocalnetState();
18
- const report = verifyReplay(state, plan, receipt);
19
- if (report.invariantsOk) {
20
- UI.success("Replay Verified Successfully");
21
- console.log(` Plan Hash: \u2713 MATCH`);
22
- console.log(` State Hashes: \u2713 MATCH`);
23
- console.log(` Mass/Fees: \u2713 MATCH`);
24
- console.log(` Status: \u2713 MATCH`);
25
- } else {
26
- UI.error("Replay Verification Failed");
27
- report.errors.forEach((err) => console.log(` [!] ${err}`));
28
- process.exitCode = 1;
29
- }
30
- } catch (e) {
31
- UI.error(`Failed to perform replay verification: ${e.message}`);
32
- process.exitCode = 1;
33
- }
34
- }
35
- export {
36
- runReplayVerify
37
- };
@@ -1,8 +0,0 @@
1
- import {
2
- UI,
3
- handleError
4
- } from "./chunk-M54KNJEH.js";
5
- export {
6
- UI,
7
- handleError
8
- };