@hardkas/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/accounts-keystore-runners-CVRE6NVM.js +112 -0
- package/dist/artifact-lineage-runner-EPT6ABS2.js +60 -0
- package/dist/chunk-M54KNJEH.js +98 -0
- package/dist/dag-runners-BQAKJ6DM.js +70 -0
- package/dist/index.js +3420 -0
- package/dist/replay-verify-runner-WBK2FCWC.js +37 -0
- package/dist/rpc-doctor-runner-RKGKFGMM.js +74 -0
- package/dist/snapshot-restore-runner-P26HDE74.js +31 -0
- package/dist/snapshot-verify-runner-UYTXXQ7A.js +38 -0
- package/dist/tx-verify-runner-GPPVBQIF.js +66 -0
- package/dist/ui-DXULTF7Q.js +8 -0
- package/package.json +58 -0
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI
|
|
3
|
+
} from "./chunk-M54KNJEH.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/rpc-doctor-runner.ts
|
|
6
|
+
import { KaspaJsonRpcClient } from "@hardkas/kaspa-rpc";
|
|
7
|
+
import { loadHardkasConfig } from "@hardkas/config";
|
|
8
|
+
async function runRpcDoctor(options) {
|
|
9
|
+
let endpoints = options.endpoints || [];
|
|
10
|
+
if (endpoints.length === 0) {
|
|
11
|
+
const loaded = await loadHardkasConfig(options.config ? { configPath: options.config } : {});
|
|
12
|
+
const networks = loaded.config.networks || {};
|
|
13
|
+
const defaultNetwork = loaded.config.defaultNetwork || "simnet";
|
|
14
|
+
const network = networks[defaultNetwork];
|
|
15
|
+
if (network?.rpcUrl) {
|
|
16
|
+
endpoints = [network.rpcUrl];
|
|
17
|
+
} else {
|
|
18
|
+
endpoints = ["http://127.0.0.1:18210"];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
UI.header("HardKAS RPC Doctor");
|
|
22
|
+
console.log(`Auditing ${endpoints.length} endpoint(s)...
|
|
23
|
+
`);
|
|
24
|
+
const results = [];
|
|
25
|
+
for (const url of endpoints) {
|
|
26
|
+
const client = new KaspaJsonRpcClient({ url, timeoutMs: 5e3 });
|
|
27
|
+
const health = await client.healthCheck();
|
|
28
|
+
results.push({ url, health });
|
|
29
|
+
const statusIcon = health.status === "healthy" ? "\u2713" : health.status === "stale" ? "\u26A0" : "\u2717";
|
|
30
|
+
console.log("\u250C\u2500\u2500 RPC HEALTH \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
31
|
+
console.log(`\u2502 ENDPOINT: ${url.padEnd(48)} \u2502`);
|
|
32
|
+
console.log(`\u2502 STATE: ${health.status.toUpperCase().padEnd(48)} \u2502`);
|
|
33
|
+
console.log(`\u2502 CONFIDENCE: ${health.confidence?.toUpperCase().padEnd(36)} [${(health.score ?? 0).toString().padStart(3)}%] \u2502`);
|
|
34
|
+
console.log(`\u2502 LATENCY: ${(health.latencyMs + "ms").padEnd(48)} \u2502`);
|
|
35
|
+
console.log(`\u2502 NETWORK: ${(health.info?.networkId || "unknown").padEnd(48)} \u2502`);
|
|
36
|
+
console.log(`\u2502 DAA SCORE: ${(health.info?.virtualDaaScore?.toString() || "0").padEnd(48)} \u2502`);
|
|
37
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
38
|
+
const { calculateConfidence } = await import("@hardkas/kaspa-rpc");
|
|
39
|
+
const resilience = calculateConfidence({
|
|
40
|
+
latencyMs: health.latencyMs || null,
|
|
41
|
+
successRate: health.successRate ?? 100,
|
|
42
|
+
retries: health.retries ?? 0,
|
|
43
|
+
stale: !!health.stale,
|
|
44
|
+
reachable: !!health.reachable,
|
|
45
|
+
circuitOpen: health.circuitState === "OPEN"
|
|
46
|
+
});
|
|
47
|
+
if (resilience.issues.length > 0) {
|
|
48
|
+
console.log("\n[ ISSUES ]");
|
|
49
|
+
resilience.issues.forEach((issue) => console.log(` \u2022 ${issue}`));
|
|
50
|
+
} else {
|
|
51
|
+
UI.success("\n \u2713 No operational issues detected.");
|
|
52
|
+
}
|
|
53
|
+
console.log("\n[ TRACE ]");
|
|
54
|
+
console.log(` - Retries: ${health.retries ?? 0}`);
|
|
55
|
+
console.log(` - Circuit: ${health.circuitState || "CLOSED"}`);
|
|
56
|
+
console.log(` - Sync Status: ${health.info?.isSynced ? "SYNCED" : "STALE"}`);
|
|
57
|
+
console.log(` - Version: ${health.info?.serverVersion || "unknown"}`);
|
|
58
|
+
console.log("");
|
|
59
|
+
}
|
|
60
|
+
if (endpoints.length > 1) {
|
|
61
|
+
UI.divider();
|
|
62
|
+
const healthy = results.filter((r) => r.health.reachable);
|
|
63
|
+
if (healthy.length === endpoints.length) {
|
|
64
|
+
UI.success("All endpoints are healthy and ready for failover.");
|
|
65
|
+
} else if (healthy.length > 0) {
|
|
66
|
+
UI.warning(`${healthy.length}/${endpoints.length} endpoints are healthy. Load balancing will be degraded.`);
|
|
67
|
+
} else {
|
|
68
|
+
UI.error("CRITICAL: All endpoints are unreachable.");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
runRpcDoctor
|
|
74
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI
|
|
3
|
+
} from "./chunk-M54KNJEH.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/snapshot-restore-runner.ts
|
|
6
|
+
import {
|
|
7
|
+
loadOrCreateLocalnetState,
|
|
8
|
+
restoreLocalnetSnapshot,
|
|
9
|
+
saveLocalnetState,
|
|
10
|
+
calculateStateHash
|
|
11
|
+
} from "@hardkas/localnet";
|
|
12
|
+
async function runSnapshotRestore(options) {
|
|
13
|
+
try {
|
|
14
|
+
const state = await loadOrCreateLocalnetState();
|
|
15
|
+
const preHash = calculateStateHash(state);
|
|
16
|
+
UI.header(`Restoring Snapshot: ${options.idOrName}`);
|
|
17
|
+
const nextState = restoreLocalnetSnapshot(state, options.idOrName);
|
|
18
|
+
const postHash = calculateStateHash(nextState);
|
|
19
|
+
await saveLocalnetState(nextState);
|
|
20
|
+
UI.success("Snapshot Restored Successfully");
|
|
21
|
+
console.log(` Previous State Hash: ${preHash.slice(0, 16)}...`);
|
|
22
|
+
console.log(` New State Hash: ${postHash.slice(0, 16)}...`);
|
|
23
|
+
console.log(` DAA Score: ${nextState.daaScore}`);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
UI.error(`Restore failed: ${e.message}`);
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
runSnapshotRestore
|
|
31
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI
|
|
3
|
+
} from "./chunk-M54KNJEH.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/snapshot-verify-runner.ts
|
|
6
|
+
import { loadOrCreateLocalnetState, verifySnapshot } from "@hardkas/localnet";
|
|
7
|
+
async function runSnapshotVerify(options) {
|
|
8
|
+
try {
|
|
9
|
+
const state = await loadOrCreateLocalnetState();
|
|
10
|
+
const snapshot = state.snapshots?.find(
|
|
11
|
+
(s) => s.id === options.idOrName || s.name === options.idOrName || s.contentHash === options.idOrName
|
|
12
|
+
);
|
|
13
|
+
if (!snapshot) {
|
|
14
|
+
UI.error(`Snapshot not found: ${options.idOrName}`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
UI.header(`Snapshot Verification: ${snapshot.name || snapshot.contentHash}`);
|
|
19
|
+
const result = verifySnapshot(snapshot);
|
|
20
|
+
if (result.ok) {
|
|
21
|
+
UI.success("Snapshot Integrity Verified");
|
|
22
|
+
console.log(` Accounts Hash: \u2713 MATCH (${snapshot.accountsHash?.slice(0, 8)}...)`);
|
|
23
|
+
console.log(` UTXO Set Hash: \u2713 MATCH (${snapshot.utxoSetHash?.slice(0, 8)}...)`);
|
|
24
|
+
console.log(` State Hash: \u2713 MATCH (${snapshot.stateHash?.slice(0, 8)}...)`);
|
|
25
|
+
console.log(` Content Hash: \u2713 MATCH (${snapshot.contentHash?.slice(0, 8)}...)`);
|
|
26
|
+
} else {
|
|
27
|
+
UI.error("Snapshot Integrity Compromised");
|
|
28
|
+
result.errors.forEach((err) => console.log(` [!] ${err}`));
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
UI.error(`Verification failed: ${e.message}`);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
runSnapshotVerify
|
|
38
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI
|
|
3
|
+
} from "./chunk-M54KNJEH.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/tx-verify-runner.ts
|
|
6
|
+
import { readTxPlanArtifact } from "@hardkas/artifacts";
|
|
7
|
+
import { verifyTxPlanSemantics } from "@hardkas/tx-builder";
|
|
8
|
+
import { formatSompi } from "@hardkas/core";
|
|
9
|
+
import path from "path";
|
|
10
|
+
async function runTxVerify(options) {
|
|
11
|
+
const absolutePath = path.resolve(process.cwd(), options.path);
|
|
12
|
+
UI.header(`Transaction Verification: ${path.basename(options.path)}`);
|
|
13
|
+
try {
|
|
14
|
+
const artifact = await readTxPlanArtifact(absolutePath);
|
|
15
|
+
const result = verifyTxPlanSemantics(artifact);
|
|
16
|
+
if (options.json) {
|
|
17
|
+
console.log(JSON.stringify(result, (key, value) => typeof value === "bigint" ? value.toString() : value, 2));
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
console.log(`Plan ID: ${artifact.planId}`);
|
|
21
|
+
console.log(`Network: ${artifact.networkId}`);
|
|
22
|
+
console.log(`Mode: ${artifact.mode}`);
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log("Economic Audit:");
|
|
25
|
+
console.log(` Inputs: ${formatSompi(result.inputTotalSompi)}`);
|
|
26
|
+
console.log(` Outputs: ${formatSompi(result.outputTotalSompi)}`);
|
|
27
|
+
console.log(` Change: ${formatSompi(result.changeAmountSompi)}`);
|
|
28
|
+
console.log(` Fee (calc): ${formatSompi(result.recomputedFeeSompi)}`);
|
|
29
|
+
console.log(` Mass: ${result.recomputedMass}`);
|
|
30
|
+
console.log("");
|
|
31
|
+
if (result.issues.length > 0) {
|
|
32
|
+
console.log("Issues Found:");
|
|
33
|
+
result.issues.forEach((issue) => {
|
|
34
|
+
const prefix = getSeverityPrefix(issue.severity);
|
|
35
|
+
console.log(` ${prefix} [${issue.code}] ${issue.message}`);
|
|
36
|
+
if (issue.path) console.log(` Path: ${issue.path}`);
|
|
37
|
+
});
|
|
38
|
+
console.log("");
|
|
39
|
+
}
|
|
40
|
+
if (result.ok) {
|
|
41
|
+
UI.success("SEMANTIC VERIFICATION PASSED");
|
|
42
|
+
} else {
|
|
43
|
+
UI.error("SEMANTIC VERIFICATION FAILED");
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
UI.error(`Verification error: ${e.message}`);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function getSeverityPrefix(severity) {
|
|
53
|
+
switch (severity) {
|
|
54
|
+
case "critical":
|
|
55
|
+
return "[!!!]";
|
|
56
|
+
case "error":
|
|
57
|
+
return "[!] ";
|
|
58
|
+
case "warning":
|
|
59
|
+
return "[?] ";
|
|
60
|
+
default:
|
|
61
|
+
return "[i] ";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
runTxVerify
|
|
66
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hardkas/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"README.md",
|
|
8
|
+
"LICENSE"
|
|
9
|
+
],
|
|
10
|
+
"bin": {
|
|
11
|
+
"hardkas": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"hardkas": "tsx src/index.ts",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"lint": "eslint ."
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@hardkas/accounts": "workspace:*",
|
|
23
|
+
"@hardkas/artifacts": "workspace:*",
|
|
24
|
+
"@hardkas/config": "workspace:*",
|
|
25
|
+
"@hardkas/core": "workspace:*",
|
|
26
|
+
"@hardkas/kaspa-rpc": "workspace:*",
|
|
27
|
+
"@hardkas/query": "workspace:*",
|
|
28
|
+
"@hardkas/l2": "workspace:*",
|
|
29
|
+
"@hardkas/localnet": "workspace:*",
|
|
30
|
+
"@hardkas/node-orchestrator": "workspace:*",
|
|
31
|
+
"@hardkas/node-runner": "workspace:*",
|
|
32
|
+
"@hardkas/query-store": "workspace:*",
|
|
33
|
+
"@hardkas/sdk": "workspace:*",
|
|
34
|
+
"@hardkas/simulator": "workspace:*",
|
|
35
|
+
"@hardkas/tx-builder": "workspace:*",
|
|
36
|
+
"commander": "^12.1.0",
|
|
37
|
+
"enquirer": "^2.4.1",
|
|
38
|
+
"picocolors": "^1.1.1",
|
|
39
|
+
"zod": "^3.23.8"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"tsup": "^8.3.5",
|
|
43
|
+
"tsx": "^4.19.2",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"vitest": "^2.1.8"
|
|
46
|
+
},
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"author": "Javier Rodriguez",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/KasLabDevs/HardKas.git",
|
|
52
|
+
"directory": "packages/cli"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/KasLabDevs/HardKas/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/KasLabDevs/HardKas/tree/main/packages/cli#readme"
|
|
58
|
+
}
|