@hardkas/cli 0.2.1-alpha → 0.2.2-alpha.1
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/dist/{accounts-keystore-runners-CVRE6NVM.js → accounts-keystore-runners-MCJIAGZ4.js} +1 -1
- package/dist/{artifact-lineage-runner-EPT6ABS2.js → artifact-lineage-runner-RJWQ3R3X.js} +1 -1
- package/dist/{chunk-M54KNJEH.js → chunk-AQWQW5ZG.js} +33 -10
- package/dist/{dag-runners-BQAKJ6DM.js → dag-runners-YQDHD7U6.js} +1 -1
- package/dist/index.js +550 -175
- package/dist/{replay-verify-runner-WBK2FCWC.js → replay-verify-runner-UMYALHNT.js} +1 -1
- package/dist/{rpc-doctor-runner-RKGKFGMM.js → rpc-doctor-runner-ERWXOXSE.js} +1 -1
- package/dist/{snapshot-restore-runner-P26HDE74.js → snapshot-restore-runner-QNAADGBX.js} +1 -1
- package/dist/{snapshot-verify-runner-UYTXXQ7A.js → snapshot-verify-runner-BWRW3NUW.js} +1 -1
- package/dist/{tx-verify-runner-GPPVBQIF.js → tx-verify-runner-Z5M2JDRI.js} +1 -1
- package/dist/{ui-DXULTF7Q.js → ui-OVK5PX6H.js} +1 -1
- package/package.json +16 -16
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
UI,
|
|
4
4
|
handleError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AQWQW5ZG.js";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -70,19 +70,19 @@ async function runUp() {
|
|
|
70
70
|
|
|
71
71
|
// src/commands/init.ts
|
|
72
72
|
function registerInitCommands(program) {
|
|
73
|
-
program.command("init").description(
|
|
73
|
+
program.command("init").description(`Initialize a new HardKAS project ${UI.maturity("stable")}`).argument("[name]", "Project name or directory").option("--force", "Overwrite existing hardkas.config.ts", false).action(async (name, options) => {
|
|
74
74
|
try {
|
|
75
75
|
const fs10 = await import("fs");
|
|
76
|
-
const
|
|
76
|
+
const path12 = await import("path");
|
|
77
77
|
let targetDir = process.cwd();
|
|
78
78
|
if (name) {
|
|
79
|
-
targetDir =
|
|
79
|
+
targetDir = path12.join(process.cwd(), name);
|
|
80
80
|
if (!fs10.existsSync(targetDir)) {
|
|
81
81
|
fs10.mkdirSync(targetDir, { recursive: true });
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
const configFile =
|
|
85
|
-
const pkgFile =
|
|
84
|
+
const configFile = path12.join(targetDir, "hardkas.config.ts");
|
|
85
|
+
const pkgFile = path12.join(targetDir, "package.json");
|
|
86
86
|
if (fs10.existsSync(configFile) && !options.force) {
|
|
87
87
|
UI.warning(`hardkas.config.ts already exists in ${name || "current directory"}. Use --force to overwrite.`);
|
|
88
88
|
return;
|
|
@@ -102,7 +102,7 @@ function registerInitCommands(program) {
|
|
|
102
102
|
const template = `import { defineHardkasConfig } from "@hardkas/sdk";
|
|
103
103
|
|
|
104
104
|
export default defineHardkasConfig({
|
|
105
|
-
// HardKAS v0.2-alpha Configuration
|
|
105
|
+
// HardKAS v0.2.2-alpha Configuration
|
|
106
106
|
defaultNetwork: "simnet",
|
|
107
107
|
|
|
108
108
|
networks: {
|
|
@@ -120,16 +120,28 @@ export default defineHardkasConfig({
|
|
|
120
120
|
accounts: {
|
|
121
121
|
alice: {
|
|
122
122
|
kind: "simulated",
|
|
123
|
-
address: "
|
|
123
|
+
address: "kaspa:sim_alice"
|
|
124
124
|
},
|
|
125
125
|
bob: {
|
|
126
126
|
kind: "simulated",
|
|
127
|
-
address: "
|
|
127
|
+
address: "kaspa:sim_bob"
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
});
|
|
131
131
|
`;
|
|
132
132
|
fs10.writeFileSync(configFile, template, "utf-8");
|
|
133
|
+
const gitIgnoreFile = path12.join(targetDir, ".gitignore");
|
|
134
|
+
const gitIgnoreEntry = "\n# HardKAS local storage\n.hardkas/\n";
|
|
135
|
+
if (!fs10.existsSync(gitIgnoreFile)) {
|
|
136
|
+
fs10.writeFileSync(gitIgnoreFile, gitIgnoreEntry, "utf-8");
|
|
137
|
+
UI.info("Created: .gitignore");
|
|
138
|
+
} else {
|
|
139
|
+
const content = fs10.readFileSync(gitIgnoreFile, "utf-8");
|
|
140
|
+
if (!content.includes(".hardkas/")) {
|
|
141
|
+
fs10.appendFileSync(gitIgnoreFile, gitIgnoreEntry, "utf-8");
|
|
142
|
+
UI.info("Updated: .gitignore (added .hardkas/)");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
133
145
|
UI.success(`HardKAS project '${name || "current"}' initialized successfully.`);
|
|
134
146
|
if (name) UI.info(`Project folder: ${targetDir}`);
|
|
135
147
|
UI.info(`Created: hardkas.config.ts (v0.2-alpha)`);
|
|
@@ -139,7 +151,7 @@ export default defineHardkasConfig({
|
|
|
139
151
|
process.exitCode = 1;
|
|
140
152
|
}
|
|
141
153
|
});
|
|
142
|
-
program.command("up").description(
|
|
154
|
+
program.command("up").description(`Boot or validate the HardKAS developer runtime environment ${UI.maturity("stable")}`).action(async () => {
|
|
143
155
|
try {
|
|
144
156
|
await runUp();
|
|
145
157
|
} catch (e) {
|
|
@@ -234,8 +246,8 @@ async function runTxPlan(input) {
|
|
|
234
246
|
const { target, name } = resolveNetworkTarget({ config, network: networkId });
|
|
235
247
|
resolvedNetwork = name;
|
|
236
248
|
if (target.kind === "simulated") {
|
|
237
|
-
const { loadOrCreateLocalnetState:
|
|
238
|
-
const localState = await
|
|
249
|
+
const { loadOrCreateLocalnetState: loadOrCreateLocalnetState4, getSpendableUtxos } = await import("@hardkas/localnet");
|
|
250
|
+
const localState = await loadOrCreateLocalnetState4();
|
|
239
251
|
const unspent = getSpendableUtxos(localState, fromAddress);
|
|
240
252
|
availableUtxos = unspent.map((u) => ({
|
|
241
253
|
outpoint: {
|
|
@@ -248,17 +260,17 @@ async function runTxPlan(input) {
|
|
|
248
260
|
}));
|
|
249
261
|
mode = "simulated";
|
|
250
262
|
} else if (target.kind === "kaspa-node" || target.kind === "kaspa-rpc") {
|
|
251
|
-
const { JsonWrpcKaspaClient:
|
|
252
|
-
const { resolveRuntimeConfig } = await import("@hardkas/node-orchestrator");
|
|
263
|
+
const { JsonWrpcKaspaClient: JsonWrpcKaspaClient5 } = await import("@hardkas/kaspa-rpc");
|
|
264
|
+
const { resolveRuntimeConfig: resolveRuntimeConfig2 } = await import("@hardkas/node-orchestrator");
|
|
253
265
|
rpcUrl = url || target.rpcUrl;
|
|
254
266
|
if (!rpcUrl && target.kind === "kaspa-node") {
|
|
255
|
-
rpcUrl =
|
|
267
|
+
rpcUrl = resolveRuntimeConfig2({
|
|
256
268
|
network: target.network,
|
|
257
269
|
...target.dataDir ? { dataDir: target.dataDir } : {}
|
|
258
270
|
}).rpcUrl;
|
|
259
271
|
}
|
|
260
272
|
if (!rpcUrl) throw new Error("Could not resolve RPC URL");
|
|
261
|
-
const client = new
|
|
273
|
+
const client = new JsonWrpcKaspaClient5({ rpcUrl });
|
|
262
274
|
const rpcUtxos = await client.getUtxosByAddress(fromAddress);
|
|
263
275
|
await client.close();
|
|
264
276
|
availableUtxos = rpcUtxos.map((u) => ({
|
|
@@ -271,13 +283,13 @@ async function runTxPlan(input) {
|
|
|
271
283
|
}
|
|
272
284
|
} catch (e) {
|
|
273
285
|
if (url || networkId !== "simnet") {
|
|
274
|
-
const { JsonWrpcKaspaClient:
|
|
275
|
-
const { resolveRuntimeConfig } = await import("@hardkas/node-orchestrator");
|
|
286
|
+
const { JsonWrpcKaspaClient: JsonWrpcKaspaClient5 } = await import("@hardkas/kaspa-rpc");
|
|
287
|
+
const { resolveRuntimeConfig: resolveRuntimeConfig2 } = await import("@hardkas/node-orchestrator");
|
|
276
288
|
rpcUrl = url;
|
|
277
289
|
if (!rpcUrl) {
|
|
278
|
-
rpcUrl =
|
|
290
|
+
rpcUrl = resolveRuntimeConfig2({ network: networkId }).rpcUrl;
|
|
279
291
|
}
|
|
280
|
-
const client = new
|
|
292
|
+
const client = new JsonWrpcKaspaClient5({ rpcUrl });
|
|
281
293
|
const rpcUtxos = await client.getUtxosByAddress(fromAddress);
|
|
282
294
|
await client.close();
|
|
283
295
|
availableUtxos = rpcUtxos.map((u) => ({
|
|
@@ -413,6 +425,7 @@ async function runTxSend(input) {
|
|
|
413
425
|
schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
|
|
414
426
|
hardkasVersion: HARDKAS_VERSION,
|
|
415
427
|
version: ARTIFACT_VERSION,
|
|
428
|
+
hashVersion: "sha256-canonical",
|
|
416
429
|
networkId: resolvedName,
|
|
417
430
|
mode: "simulated",
|
|
418
431
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -432,6 +445,7 @@ async function runTxSend(input) {
|
|
|
432
445
|
schema: ARTIFACT_SCHEMAS.TX_TRACE,
|
|
433
446
|
hardkasVersion: HARDKAS_VERSION,
|
|
434
447
|
version: ARTIFACT_VERSION,
|
|
448
|
+
hashVersion: "sha256-canonical",
|
|
435
449
|
createdAt: receipt.createdAt,
|
|
436
450
|
txId: receipt.txId,
|
|
437
451
|
mode: "simulated",
|
|
@@ -471,6 +485,7 @@ Receipt: ${receiptPath}`
|
|
|
471
485
|
schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
|
|
472
486
|
hardkasVersion: HARDKAS_VERSION,
|
|
473
487
|
version: ARTIFACT_VERSION,
|
|
488
|
+
hashVersion: "sha256-canonical",
|
|
474
489
|
networkId: resolvedName,
|
|
475
490
|
mode: "real",
|
|
476
491
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -666,19 +681,19 @@ async function runTxReceipt(input) {
|
|
|
666
681
|
// src/commands/tx.ts
|
|
667
682
|
function registerTxCommands(program) {
|
|
668
683
|
const tx = program.command("tx").description("L1 Transaction commands");
|
|
669
|
-
tx.command("profile <path>").description("Show detailed mass and fee breakdown for a transaction plan").action(async (
|
|
684
|
+
tx.command("profile <path>").description("Show detailed mass and fee breakdown for a transaction plan").action(async (path12) => {
|
|
670
685
|
try {
|
|
671
|
-
await runTxProfile({ path:
|
|
686
|
+
await runTxProfile({ path: path12 });
|
|
672
687
|
} catch (e) {
|
|
673
688
|
handleError(e);
|
|
674
689
|
process.exitCode = 1;
|
|
675
690
|
}
|
|
676
691
|
});
|
|
677
|
-
tx.command("plan").description(
|
|
692
|
+
tx.command("plan").description(`Build a transaction plan artifact ${UI.maturity("stable")}`).option("--from <accountOrAddress>", "Sender account name or address").option("--to <address>", "Recipient address").option("--amount <kas>", "Amount in KAS").option("--network <name>", "Kaspa network name", "simnet").option("--fee-rate <sompiPerMass>", "Fee rate in sompi per mass", "1").option("--url <url>", "RPC URL (optional override)").option("--out <path>", "Save plan as artifact JSON").option("--json", "Output as JSON", false).action(async (options) => {
|
|
678
693
|
try {
|
|
679
|
-
const { loadHardkasConfig:
|
|
694
|
+
const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
|
|
680
695
|
const { writeArtifact: writeArtifact4, formatTxPlanArtifact } = await import("@hardkas/artifacts");
|
|
681
|
-
const loaded = await
|
|
696
|
+
const loaded = await loadHardkasConfig5();
|
|
682
697
|
const artifact = await runTxPlan({
|
|
683
698
|
from: options.from || "alice",
|
|
684
699
|
to: options.to || "bob",
|
|
@@ -700,12 +715,12 @@ Artifact saved to: ${options.out}`);
|
|
|
700
715
|
process.exitCode = 1;
|
|
701
716
|
}
|
|
702
717
|
});
|
|
703
|
-
tx.command("sign <planPath>").description(
|
|
718
|
+
tx.command("sign <planPath>").description(`Sign a transaction plan artifact ${UI.maturity("stable")}`).option("--account <name>", "Account name to sign with").option("--out <path>", "Save signed artifact JSON").option("--allow-mainnet-signing", "Allow signing for mainnet", false).option("--json", "Output as JSON", false).action(async (planPath, options) => {
|
|
704
719
|
try {
|
|
705
720
|
const { readTxPlanArtifact, writeArtifact: writeArtifact4, formatSignedTxArtifact } = await import("@hardkas/artifacts");
|
|
706
|
-
const { loadHardkasConfig:
|
|
721
|
+
const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
|
|
707
722
|
const planArtifact = await readTxPlanArtifact(planPath);
|
|
708
|
-
const loaded = await
|
|
723
|
+
const loaded = await loadHardkasConfig5();
|
|
709
724
|
const signedArtifact = await runTxSign({
|
|
710
725
|
planArtifact,
|
|
711
726
|
...options.account ? { accountName: options.account } : {},
|
|
@@ -724,10 +739,10 @@ Signed artifact saved to: ${options.out}`);
|
|
|
724
739
|
process.exitCode = 1;
|
|
725
740
|
}
|
|
726
741
|
});
|
|
727
|
-
tx.command("send [signedPath]").description(
|
|
742
|
+
tx.command("send [signedPath]").description(`Broadcast a signed transaction or send directly ${UI.maturity("stable")}`).option("--from <accountOrAddress>", "Sender (shortcut mode)").option("--to <address>", "Recipient (shortcut mode)").option("--amount <kas>", "Amount in KAS (shortcut mode)").option("--network <name>", "Network name", "simnet").option("--url <url>", "RPC URL (optional override)").option("--yes", "Confirm broadcast", false).option("--json", "Output as JSON", false).action(async (signedPath, options) => {
|
|
728
743
|
try {
|
|
729
|
-
const { loadHardkasConfig:
|
|
730
|
-
const loaded = await
|
|
744
|
+
const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
|
|
745
|
+
const loaded = await loadHardkasConfig5();
|
|
731
746
|
if (signedPath) {
|
|
732
747
|
const { readSignedTxArtifact } = await import("@hardkas/artifacts");
|
|
733
748
|
const signedArtifact = await readSignedTxArtifact(signedPath);
|
|
@@ -777,12 +792,12 @@ Signed artifact saved to: ${options.out}`);
|
|
|
777
792
|
process.exitCode = 1;
|
|
778
793
|
}
|
|
779
794
|
});
|
|
780
|
-
tx.command("verify <path>").description(
|
|
781
|
-
const { runTxVerify } = await import("./tx-verify-runner-
|
|
782
|
-
await runTxVerify({ path:
|
|
795
|
+
tx.command("verify <path>").description(`Perform deep semantic verification of a transaction plan ${UI.maturity("preview")}`).option("--json", "Output as JSON", false).action(async (path12, options) => {
|
|
796
|
+
const { runTxVerify } = await import("./tx-verify-runner-Z5M2JDRI.js");
|
|
797
|
+
await runTxVerify({ path: path12, ...options });
|
|
783
798
|
});
|
|
784
|
-
tx.command("trace <txId>").description(
|
|
785
|
-
const { UI: UI2 } = await import("./ui-
|
|
799
|
+
tx.command("trace <txId>").description(`Reconstruct the full operational trace of a transaction ${UI.maturity("research")}`).action(async (txId) => {
|
|
800
|
+
const { UI: UI2 } = await import("./ui-OVK5PX6H.js");
|
|
786
801
|
UI2.error("Tracing is temporarily disabled while the query API stabilizes.");
|
|
787
802
|
process.exitCode = 1;
|
|
788
803
|
});
|
|
@@ -984,26 +999,26 @@ async function runArtifactExplain(options) {
|
|
|
984
999
|
// src/commands/artifact.ts
|
|
985
1000
|
function registerArtifactCommands(program) {
|
|
986
1001
|
const artifactCmd = program.command("artifact").description("Manage HardKAS artifacts");
|
|
987
|
-
artifactCmd.command("verify <path>").description(
|
|
1002
|
+
artifactCmd.command("verify <path>").description(`Verify an artifact's integrity and schema ${UI.maturity("stable")}`).option("--json", "Output results as JSON", false).option("--recursive", "Recursively verify all artifacts in a directory", false).option("--strict", "Perform deep semantic and operational safety verification", false).action(async (path12, options) => {
|
|
988
1003
|
try {
|
|
989
|
-
await runArtifactVerify({ path:
|
|
1004
|
+
await runArtifactVerify({ path: path12, ...options });
|
|
990
1005
|
} catch (e) {
|
|
991
1006
|
handleError(e);
|
|
992
1007
|
process.exitCode = 1;
|
|
993
1008
|
}
|
|
994
1009
|
});
|
|
995
|
-
artifactCmd.command("explain <path>").description(
|
|
1010
|
+
artifactCmd.command("explain <path>").description(`Provide a human-readable operational summary of an artifact ${UI.maturity("preview")}`).action(async (path12) => {
|
|
996
1011
|
try {
|
|
997
|
-
await runArtifactExplain({ path:
|
|
1012
|
+
await runArtifactExplain({ path: path12 });
|
|
998
1013
|
} catch (e) {
|
|
999
1014
|
handleError(e);
|
|
1000
1015
|
process.exitCode = 1;
|
|
1001
1016
|
}
|
|
1002
1017
|
});
|
|
1003
|
-
artifactCmd.command("lineage <path>").description(
|
|
1018
|
+
artifactCmd.command("lineage <path>").description(`Show the provenance and operational history of an artifact ${UI.maturity("preview")}`).action(async (path12) => {
|
|
1004
1019
|
try {
|
|
1005
|
-
const { runArtifactLineage } = await import("./artifact-lineage-runner-
|
|
1006
|
-
await runArtifactLineage({ path:
|
|
1020
|
+
const { runArtifactLineage } = await import("./artifact-lineage-runner-RJWQ3R3X.js");
|
|
1021
|
+
await runArtifactLineage({ path: path12 });
|
|
1007
1022
|
} catch (e) {
|
|
1008
1023
|
handleError(e);
|
|
1009
1024
|
process.exitCode = 1;
|
|
@@ -1014,9 +1029,9 @@ function registerArtifactCommands(program) {
|
|
|
1014
1029
|
// src/commands/replay.ts
|
|
1015
1030
|
function registerReplayCommands(program) {
|
|
1016
1031
|
const replayCmd = program.command("replay").description("Manage HardKAS transaction replays");
|
|
1017
|
-
replayCmd.command("verify <path>").description("Verify replay invariants for a directory of artifacts").action(async (
|
|
1018
|
-
const { runReplayVerify } = await import("./replay-verify-runner-
|
|
1019
|
-
await runReplayVerify({ path:
|
|
1032
|
+
replayCmd.command("verify <path>").description("Verify replay invariants for a directory of artifacts").action(async (path12) => {
|
|
1033
|
+
const { runReplayVerify } = await import("./replay-verify-runner-UMYALHNT.js");
|
|
1034
|
+
await runReplayVerify({ path: path12 });
|
|
1020
1035
|
});
|
|
1021
1036
|
}
|
|
1022
1037
|
|
|
@@ -1024,11 +1039,11 @@ function registerReplayCommands(program) {
|
|
|
1024
1039
|
function registerSnapshotCommands(program) {
|
|
1025
1040
|
const snapshotCmd = program.command("snapshot").description("Manage HardKAS localnet snapshots");
|
|
1026
1041
|
snapshotCmd.command("verify <idOrName>").description("Verify the integrity of a snapshot").action(async (idOrName) => {
|
|
1027
|
-
const { runSnapshotVerify } = await import("./snapshot-verify-runner-
|
|
1042
|
+
const { runSnapshotVerify } = await import("./snapshot-verify-runner-BWRW3NUW.js");
|
|
1028
1043
|
await runSnapshotVerify({ idOrName });
|
|
1029
1044
|
});
|
|
1030
1045
|
snapshotCmd.command("restore <idOrName>").description("Restore localnet state from a snapshot").action(async (idOrName) => {
|
|
1031
|
-
const { runSnapshotRestore } = await import("./snapshot-restore-runner-
|
|
1046
|
+
const { runSnapshotRestore } = await import("./snapshot-restore-runner-QNAADGBX.js");
|
|
1032
1047
|
await runSnapshotRestore({ idOrName });
|
|
1033
1048
|
});
|
|
1034
1049
|
}
|
|
@@ -1215,7 +1230,7 @@ function registerRpcCommands(program) {
|
|
|
1215
1230
|
}
|
|
1216
1231
|
});
|
|
1217
1232
|
rpcCmd.command("doctor").description("Run comprehensive RPC diagnostics").option("--endpoints <urls...>", "Specific endpoints to audit").action(async (options) => {
|
|
1218
|
-
const { runRpcDoctor } = await import("./rpc-doctor-runner-
|
|
1233
|
+
const { runRpcDoctor } = await import("./rpc-doctor-runner-ERWXOXSE.js");
|
|
1219
1234
|
try {
|
|
1220
1235
|
await runRpcDoctor(options);
|
|
1221
1236
|
} catch (e) {
|
|
@@ -1250,7 +1265,7 @@ function registerDagCommands(program) {
|
|
|
1250
1265
|
const dagCmd = program.command("dag").description("Simulate blockDAG operations (Localnet only)");
|
|
1251
1266
|
dagCmd.command("status").description("View current DAG status").action(async () => {
|
|
1252
1267
|
try {
|
|
1253
|
-
const { runDagStatus } = await import("./dag-runners-
|
|
1268
|
+
const { runDagStatus } = await import("./dag-runners-YQDHD7U6.js");
|
|
1254
1269
|
await runDagStatus();
|
|
1255
1270
|
} catch (e) {
|
|
1256
1271
|
handleError(e);
|
|
@@ -1259,7 +1274,7 @@ function registerDagCommands(program) {
|
|
|
1259
1274
|
});
|
|
1260
1275
|
dagCmd.command("simulate-reorg").description("Simulate a DAG reorg").option("--depth <n>", "Reorg depth", "1").action(async (options) => {
|
|
1261
1276
|
try {
|
|
1262
|
-
const { runDagSimulateReorg } = await import("./dag-runners-
|
|
1277
|
+
const { runDagSimulateReorg } = await import("./dag-runners-YQDHD7U6.js");
|
|
1263
1278
|
await runDagSimulateReorg({ depth: parseInt(options.depth) });
|
|
1264
1279
|
} catch (e) {
|
|
1265
1280
|
handleError(e);
|
|
@@ -1340,14 +1355,99 @@ async function runAccountsRealGenerate(options = {}) {
|
|
|
1340
1355
|
};
|
|
1341
1356
|
}
|
|
1342
1357
|
|
|
1358
|
+
// src/runners/accounts-balance-runner.ts
|
|
1359
|
+
import {
|
|
1360
|
+
loadHardkasConfig as loadHardkasConfig2
|
|
1361
|
+
} from "@hardkas/config";
|
|
1362
|
+
import {
|
|
1363
|
+
loadRealAccountStore as loadRealAccountStore2,
|
|
1364
|
+
getRealDevAccount
|
|
1365
|
+
} from "@hardkas/accounts";
|
|
1366
|
+
import { JsonWrpcKaspaClient as JsonWrpcKaspaClient3 } from "@hardkas/kaspa-rpc";
|
|
1367
|
+
import { resolveRuntimeConfig } from "@hardkas/node-orchestrator";
|
|
1368
|
+
async function runAccountsBalance(options) {
|
|
1369
|
+
let address = options.identifier;
|
|
1370
|
+
let name = "Unknown";
|
|
1371
|
+
const loadedConfig = await loadHardkasConfig2({});
|
|
1372
|
+
const projectAccount = loadedConfig.config.accounts?.[options.identifier];
|
|
1373
|
+
if (projectAccount) {
|
|
1374
|
+
address = projectAccount.address ?? "";
|
|
1375
|
+
name = options.identifier;
|
|
1376
|
+
} else {
|
|
1377
|
+
const store = await loadRealAccountStore2();
|
|
1378
|
+
const realAccount = store ? getRealDevAccount(store, options.identifier) : null;
|
|
1379
|
+
if (realAccount) {
|
|
1380
|
+
address = realAccount.address ?? "";
|
|
1381
|
+
name = realAccount.name;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
const network = options.network ?? loadedConfig.config.defaultNetwork ?? "simnet";
|
|
1385
|
+
let rpcUrl = options.url;
|
|
1386
|
+
if (!rpcUrl) {
|
|
1387
|
+
rpcUrl = resolveRuntimeConfig({ network }).rpcUrl;
|
|
1388
|
+
}
|
|
1389
|
+
const client = new JsonWrpcKaspaClient3({ rpcUrl });
|
|
1390
|
+
try {
|
|
1391
|
+
const balance = await client.getBalanceByAddress(address);
|
|
1392
|
+
const utxos = await client.getUtxosByAddress(address);
|
|
1393
|
+
return {
|
|
1394
|
+
name,
|
|
1395
|
+
address,
|
|
1396
|
+
balanceSompi: balance.balanceSompi,
|
|
1397
|
+
utxoCount: utxos.length,
|
|
1398
|
+
network
|
|
1399
|
+
};
|
|
1400
|
+
} finally {
|
|
1401
|
+
await client.close();
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/runners/accounts-fund-runner.ts
|
|
1406
|
+
import { loadHardkasConfig as loadHardkasConfig3 } from "@hardkas/config";
|
|
1407
|
+
import { resolveHardkasAccountAddress as resolveHardkasAccountAddress2 } from "@hardkas/accounts";
|
|
1408
|
+
import {
|
|
1409
|
+
loadOrCreateLocalnetState as loadOrCreateLocalnetState2,
|
|
1410
|
+
saveLocalnetState as saveLocalnetState2,
|
|
1411
|
+
fundAddress
|
|
1412
|
+
} from "@hardkas/localnet";
|
|
1413
|
+
async function runAccountsFund(options) {
|
|
1414
|
+
const loadedConfig = await loadHardkasConfig3({});
|
|
1415
|
+
const address = resolveHardkasAccountAddress2(options.identifier, loadedConfig.config);
|
|
1416
|
+
const networkId = loadedConfig.config.defaultNetwork || "simnet";
|
|
1417
|
+
const allowedNetworks = ["simnet", "localnet", "dev", "simulated"];
|
|
1418
|
+
if (!allowedNetworks.includes(networkId)) {
|
|
1419
|
+
throw new Error(`Faucet/Funding is only allowed on development networks (${allowedNetworks.join(", ")}). Current network is: ${networkId}`);
|
|
1420
|
+
}
|
|
1421
|
+
if (networkId === "simulated" || networkId === "localnet") {
|
|
1422
|
+
const state = await loadOrCreateLocalnetState2();
|
|
1423
|
+
const amount = options.amountSompi || 1000n * 100000000n;
|
|
1424
|
+
const newState = fundAddress(state, { address, amountSompi: amount });
|
|
1425
|
+
await saveLocalnetState2(newState);
|
|
1426
|
+
return {
|
|
1427
|
+
success: true,
|
|
1428
|
+
address,
|
|
1429
|
+
amountSompi: amount,
|
|
1430
|
+
mode: "simulated",
|
|
1431
|
+
formatted: `Successfully funded ${options.identifier} (${address}) with ${Number(amount) / 1e8} KAS (Simulated)`
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
if (networkId === "simnet" || networkId === "dev") {
|
|
1435
|
+
throw new Error(
|
|
1436
|
+
`Funding for real simnet (Docker) via faucet requires a miner account.
|
|
1437
|
+
Hint: Start your node with 'hardkas node start --miningaddr ${address}' to mine coins directly to this account.`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
throw new Error(`Unsupported network for funding: ${networkId}`);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1343
1443
|
// src/commands/accounts.ts
|
|
1344
1444
|
function registerAccountsCommands(program) {
|
|
1345
1445
|
const accountsCmd = program.command("accounts").description("Manage HardKAS accounts");
|
|
1346
1446
|
accountsCmd.command("list").description("List available HardKAS accounts").option("--config <path>", "Path to config file").option("--json", "Output as JSON", false).action(async (options) => {
|
|
1347
|
-
const { loadHardkasConfig:
|
|
1447
|
+
const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
|
|
1348
1448
|
const { listHardkasAccounts: listHardkasAccounts2, describeAccount } = await import("@hardkas/accounts");
|
|
1349
1449
|
try {
|
|
1350
|
-
const loaded = await
|
|
1450
|
+
const loaded = await loadHardkasConfig5(options.config ? { configPath: options.config } : {});
|
|
1351
1451
|
const accounts = listHardkasAccounts2(loaded.config);
|
|
1352
1452
|
if (options.json) {
|
|
1353
1453
|
console.log(JSON.stringify(accounts.map((a) => describeAccount(a)), null, 2));
|
|
@@ -1377,7 +1477,7 @@ function registerAccountsCommands(program) {
|
|
|
1377
1477
|
});
|
|
1378
1478
|
realAccountsCmd.command("import").description("Import an account into the persistent store").option("--name <name>", "Account name").option("--address <address>", "Kaspa address").option("--private-key <hex>", "Private key (plaintext, discouraged)").option("--encrypted", "Import as encrypted keystore (recommended)", false).option("--json", "Output as JSON", false).action(async (options) => {
|
|
1379
1479
|
try {
|
|
1380
|
-
const { runAccountsKeystoreImport } = await import("./accounts-keystore-runners-
|
|
1480
|
+
const { runAccountsKeystoreImport } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
|
|
1381
1481
|
const result = await runAccountsKeystoreImport(options);
|
|
1382
1482
|
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
1383
1483
|
else console.log(result.formatted);
|
|
@@ -1386,26 +1486,28 @@ function registerAccountsCommands(program) {
|
|
|
1386
1486
|
process.exitCode = 1;
|
|
1387
1487
|
}
|
|
1388
1488
|
});
|
|
1389
|
-
realAccountsCmd.command("unlock <name>").description(
|
|
1489
|
+
realAccountsCmd.command("unlock <name>").description(`Verify keystore access ${UI.maturity("internal")}`).action(async (name) => {
|
|
1390
1490
|
try {
|
|
1391
|
-
const { runAccountsKeystoreUnlock } = await import("./accounts-keystore-runners-
|
|
1491
|
+
const { runAccountsKeystoreUnlock } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
|
|
1392
1492
|
await runAccountsKeystoreUnlock({ name });
|
|
1493
|
+
console.log(`
|
|
1494
|
+
\u2713 Access to account '${name}' verified.`);
|
|
1495
|
+
console.log(` (Note: HardKAS CLI is stateless. Password will be required again for signing operations.)
|
|
1496
|
+
`);
|
|
1393
1497
|
} catch (e) {
|
|
1394
1498
|
handleError(e);
|
|
1395
1499
|
process.exitCode = 1;
|
|
1396
1500
|
}
|
|
1397
1501
|
});
|
|
1398
|
-
realAccountsCmd.command("lock <name>").description(
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
process.exitCode = 1;
|
|
1404
|
-
}
|
|
1502
|
+
realAccountsCmd.command("lock <name>").description(`[DEPRECATED] Lock an account ${UI.maturity("internal")}`).action(async (name) => {
|
|
1503
|
+
console.log(`
|
|
1504
|
+
\u2139 Account '${name}' session clear (redundant).`);
|
|
1505
|
+
console.log(` The CLI is already stateless. No secrets are stored in memory between commands.
|
|
1506
|
+
`);
|
|
1405
1507
|
});
|
|
1406
1508
|
realAccountsCmd.command("change-password <name>").description("Change password for an encrypted account").action(async (name) => {
|
|
1407
1509
|
try {
|
|
1408
|
-
const { runAccountsKeystoreChangePassword } = await import("./accounts-keystore-runners-
|
|
1510
|
+
const { runAccountsKeystoreChangePassword } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
|
|
1409
1511
|
await runAccountsKeystoreChangePassword({ name });
|
|
1410
1512
|
} catch (e) {
|
|
1411
1513
|
handleError(e);
|
|
@@ -1426,6 +1528,34 @@ function registerAccountsCommands(program) {
|
|
|
1426
1528
|
process.exitCode = 1;
|
|
1427
1529
|
}
|
|
1428
1530
|
});
|
|
1531
|
+
accountsCmd.command("balance <identifier>").description("Show account balance").option("--network <name>", "Network name (simnet, localnet, etc.)").option("--url <rpc-url>", "Explicit RPC URL").option("--json", "Output as JSON", false).action(async (identifier, options) => {
|
|
1532
|
+
try {
|
|
1533
|
+
const result = await runAccountsBalance({ identifier, network: options.network ?? "simnet", url: options.url ?? "" });
|
|
1534
|
+
if (options.json) {
|
|
1535
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1536
|
+
} else {
|
|
1537
|
+
console.log(`
|
|
1538
|
+
Account: ${result.name}`);
|
|
1539
|
+
console.log(`Address: ${result.address}`);
|
|
1540
|
+
console.log(`Balance: ${Number(result.balanceSompi) / 1e8} KAS`);
|
|
1541
|
+
console.log(`UTXOs: ${result.utxoCount}`);
|
|
1542
|
+
console.log(`Network: ${result.network}`);
|
|
1543
|
+
}
|
|
1544
|
+
} catch (e) {
|
|
1545
|
+
handleError(e);
|
|
1546
|
+
process.exitCode = 1;
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
accountsCmd.command("fund <identifier>").description("Fund an account (Faucet)").option("--amount <kas>", "Amount in KAS to fund", "1000").action(async (identifier, options) => {
|
|
1550
|
+
try {
|
|
1551
|
+
const amountSompi = BigInt(parseFloat(options.amount) * 1e8);
|
|
1552
|
+
const result = await runAccountsFund({ identifier, amountSompi });
|
|
1553
|
+
console.log(result.formatted);
|
|
1554
|
+
} catch (e) {
|
|
1555
|
+
handleError(e);
|
|
1556
|
+
process.exitCode = 1;
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1429
1559
|
}
|
|
1430
1560
|
|
|
1431
1561
|
// src/runners/l2-networks-runner.ts
|
|
@@ -1523,10 +1653,11 @@ import {
|
|
|
1523
1653
|
createIgraSignedId,
|
|
1524
1654
|
assertValidIgraTxReceiptArtifact,
|
|
1525
1655
|
listIgraTxReceiptArtifacts,
|
|
1526
|
-
loadIgraTxReceiptArtifact
|
|
1656
|
+
loadIgraTxReceiptArtifact,
|
|
1657
|
+
calculateContentHash
|
|
1527
1658
|
} from "@hardkas/artifacts";
|
|
1528
1659
|
import {
|
|
1529
|
-
loadRealAccountStore as
|
|
1660
|
+
loadRealAccountStore as loadRealAccountStore3,
|
|
1530
1661
|
resolveRealAccountOrAddress
|
|
1531
1662
|
} from "@hardkas/accounts";
|
|
1532
1663
|
async function runL2TxBuild(options) {
|
|
@@ -1569,14 +1700,14 @@ async function runL2TxBuild(options) {
|
|
|
1569
1700
|
gasLimit = g.toString();
|
|
1570
1701
|
}
|
|
1571
1702
|
const estimatedFeeWei = (BigInt(gasLimit) * BigInt(gasPrice)).toString();
|
|
1572
|
-
const planId = createIgraPlanId();
|
|
1573
1703
|
const artifact = {
|
|
1574
1704
|
schema: ARTIFACT_SCHEMAS2.IGRA_TX_PLAN,
|
|
1575
1705
|
hardkasVersion: HARDKAS_VERSION2,
|
|
1576
1706
|
networkId: profile.name,
|
|
1577
1707
|
mode: "l2-rpc",
|
|
1578
1708
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1579
|
-
planId,
|
|
1709
|
+
planId: "",
|
|
1710
|
+
// Placeholder
|
|
1580
1711
|
l2Network: profile.name,
|
|
1581
1712
|
chainId,
|
|
1582
1713
|
request: {
|
|
@@ -1592,6 +1723,10 @@ async function runL2TxBuild(options) {
|
|
|
1592
1723
|
estimatedFeeWei,
|
|
1593
1724
|
status: "built"
|
|
1594
1725
|
};
|
|
1726
|
+
const hash = calculateContentHash(artifact);
|
|
1727
|
+
const planId = createIgraPlanId(hash);
|
|
1728
|
+
artifact.planId = planId;
|
|
1729
|
+
artifact.contentHash = hash;
|
|
1595
1730
|
assertValidIgraTxPlanArtifact(artifact);
|
|
1596
1731
|
const outDir = options.outDir || "plans";
|
|
1597
1732
|
const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
|
|
@@ -1647,7 +1782,7 @@ async function runL2TxSign(options) {
|
|
|
1647
1782
|
}
|
|
1648
1783
|
let accountInfo;
|
|
1649
1784
|
if (options.account) {
|
|
1650
|
-
const store = await
|
|
1785
|
+
const store = await loadRealAccountStore3();
|
|
1651
1786
|
const accountData = resolveRealAccountOrAddress(store, options.account);
|
|
1652
1787
|
if (plan.request.from && plan.request.from.toLowerCase() !== accountData.address.toLowerCase()) {
|
|
1653
1788
|
throw new Error(`Account address mismatch: plan specifies '${plan.request.from}' but resolved account '${accountData.name ?? accountData.address}' is '${accountData.address}'`);
|
|
@@ -1686,14 +1821,14 @@ async function runL2TxSign(options) {
|
|
|
1686
1821
|
}
|
|
1687
1822
|
throw e;
|
|
1688
1823
|
}
|
|
1689
|
-
const signedId = createIgraSignedId();
|
|
1690
1824
|
const artifact = {
|
|
1691
1825
|
schema: ARTIFACT_SCHEMAS2.IGRA_SIGNED_TX,
|
|
1692
1826
|
hardkasVersion: HARDKAS_VERSION2,
|
|
1693
1827
|
networkId: plan.networkId,
|
|
1694
1828
|
mode: "l2-rpc",
|
|
1695
1829
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1696
|
-
signedId,
|
|
1830
|
+
signedId: "",
|
|
1831
|
+
// Placeholder
|
|
1697
1832
|
sourcePlanId: plan.planId,
|
|
1698
1833
|
sourcePlanPath: options.planPath,
|
|
1699
1834
|
l2Network: plan.l2Network,
|
|
@@ -1702,6 +1837,10 @@ async function runL2TxSign(options) {
|
|
|
1702
1837
|
txHash: result.txHash || "unknown",
|
|
1703
1838
|
status: "signed"
|
|
1704
1839
|
};
|
|
1840
|
+
const hash = calculateContentHash(artifact);
|
|
1841
|
+
const signedId = createIgraSignedId(hash);
|
|
1842
|
+
artifact.signedId = signedId;
|
|
1843
|
+
artifact.contentHash = hash;
|
|
1705
1844
|
assertValidIgraSignedTxArtifact(artifact);
|
|
1706
1845
|
const outDir = options.outDir || "signed";
|
|
1707
1846
|
const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
|
|
@@ -1954,7 +2093,8 @@ import {
|
|
|
1954
2093
|
writeArtifact as writeArtifact3,
|
|
1955
2094
|
HARDKAS_VERSION as HARDKAS_VERSION3,
|
|
1956
2095
|
ARTIFACT_SCHEMAS as ARTIFACT_SCHEMAS3,
|
|
1957
|
-
createIgraDeployPlanId
|
|
2096
|
+
createIgraDeployPlanId,
|
|
2097
|
+
calculateContentHash as calculateContentHash2
|
|
1958
2098
|
} from "@hardkas/artifacts";
|
|
1959
2099
|
async function runL2ContractDeployPlan(options) {
|
|
1960
2100
|
const networkName = options.network ?? "igra";
|
|
@@ -1999,14 +2139,14 @@ async function runL2ContractDeployPlan(options) {
|
|
|
1999
2139
|
gasLimit = g.toString();
|
|
2000
2140
|
}
|
|
2001
2141
|
const estimatedFeeWei = (BigInt(gasLimit) * BigInt(gasPrice)).toString();
|
|
2002
|
-
const planId = createIgraDeployPlanId();
|
|
2003
2142
|
const artifact = {
|
|
2004
2143
|
schema: ARTIFACT_SCHEMAS3.IGRA_TX_PLAN,
|
|
2005
2144
|
hardkasVersion: HARDKAS_VERSION3,
|
|
2006
2145
|
networkId: profile.name,
|
|
2007
2146
|
mode: "l2-rpc",
|
|
2008
2147
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2009
|
-
planId,
|
|
2148
|
+
planId: "",
|
|
2149
|
+
// Placeholder
|
|
2010
2150
|
l2Network: profile.name,
|
|
2011
2151
|
chainId,
|
|
2012
2152
|
txType: "contract-deploy",
|
|
@@ -2022,6 +2162,10 @@ async function runL2ContractDeployPlan(options) {
|
|
|
2022
2162
|
estimatedFeeWei,
|
|
2023
2163
|
status: "built"
|
|
2024
2164
|
};
|
|
2165
|
+
const hash = calculateContentHash2(artifact);
|
|
2166
|
+
const planId = createIgraDeployPlanId(hash);
|
|
2167
|
+
artifact.planId = planId;
|
|
2168
|
+
artifact.contentHash = hash;
|
|
2025
2169
|
assertValidIgraTxPlanArtifact2(artifact);
|
|
2026
2170
|
const outDir = options.outDir || "plans";
|
|
2027
2171
|
const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
|
|
@@ -2402,10 +2546,49 @@ async function runNodeStatus(input) {
|
|
|
2402
2546
|
};
|
|
2403
2547
|
}
|
|
2404
2548
|
|
|
2549
|
+
// src/runners/node-stop-runner.ts
|
|
2550
|
+
import { DockerKaspadRunner as DockerKaspadRunner3 } from "@hardkas/node-runner";
|
|
2551
|
+
async function runNodeStop(input) {
|
|
2552
|
+
const runner = new DockerKaspadRunner3(input.containerName ? { containerName: input.containerName } : {});
|
|
2553
|
+
return runner.stop();
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
// src/runners/node-restart-runner.ts
|
|
2557
|
+
import { DockerKaspadRunner as DockerKaspadRunner4 } from "@hardkas/node-runner";
|
|
2558
|
+
async function runNodeRestart(input) {
|
|
2559
|
+
const runner = new DockerKaspadRunner4({
|
|
2560
|
+
...input.containerName ? { containerName: input.containerName } : {},
|
|
2561
|
+
...input.image ? { image: input.image } : {}
|
|
2562
|
+
});
|
|
2563
|
+
const status = await runner.restart();
|
|
2564
|
+
return {
|
|
2565
|
+
status,
|
|
2566
|
+
formatted: `Kaspa node restarted (Container: ${status.containerName})`
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/runners/node-reset-runner.ts
|
|
2571
|
+
import { DockerKaspadRunner as DockerKaspadRunner5 } from "@hardkas/node-runner";
|
|
2572
|
+
async function runNodeReset(input) {
|
|
2573
|
+
const runner = new DockerKaspadRunner5(input.containerName ? { containerName: input.containerName } : {});
|
|
2574
|
+
const status = await runner.reset({ removeData: input.removeData !== false });
|
|
2575
|
+
return {
|
|
2576
|
+
status,
|
|
2577
|
+
formatted: `Kaspa node reset complete. Data removed: ${input.removeData !== false}. Node is currently ${status.running ? "running" : "stopped"}.`
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
// src/runners/node-logs-runner.ts
|
|
2582
|
+
import { DockerKaspadRunner as DockerKaspadRunner6 } from "@hardkas/node-runner";
|
|
2583
|
+
async function runNodeLogs(input) {
|
|
2584
|
+
const runner = new DockerKaspadRunner6(input.containerName ? { containerName: input.containerName } : {});
|
|
2585
|
+
return await runner.logs(input.tail ? { tail: input.tail } : {}) ?? "";
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2405
2588
|
// src/commands/node.ts
|
|
2406
2589
|
function registerNodeCommands(program) {
|
|
2407
2590
|
const nodeCmd = program.command("node").description("Kaspa node management (Docker)");
|
|
2408
|
-
nodeCmd.command("start").description(
|
|
2591
|
+
nodeCmd.command("start").description(`Start local node ${UI.maturity("stable")}`).option("--image <image>", "Docker image").action(async (options) => {
|
|
2409
2592
|
try {
|
|
2410
2593
|
const result = await runNodeStart(options);
|
|
2411
2594
|
console.log(result.formatted);
|
|
@@ -2413,6 +2596,42 @@ function registerNodeCommands(program) {
|
|
|
2413
2596
|
handleError(e);
|
|
2414
2597
|
}
|
|
2415
2598
|
});
|
|
2599
|
+
nodeCmd.command("stop").description(`Stop local node ${UI.maturity("stable")}`).action(async () => {
|
|
2600
|
+
try {
|
|
2601
|
+
const result = await runNodeStop({});
|
|
2602
|
+
UI.success(`Node stopped (Container: ${result.containerName})`);
|
|
2603
|
+
} catch (e) {
|
|
2604
|
+
handleError(e);
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
nodeCmd.command("restart").description(`Restart local node ${UI.maturity("stable")}`).action(async () => {
|
|
2608
|
+
try {
|
|
2609
|
+
const result = await runNodeRestart({});
|
|
2610
|
+
console.log(result.formatted);
|
|
2611
|
+
} catch (e) {
|
|
2612
|
+
handleError(e);
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
nodeCmd.command("reset").description(`Stop node and remove all local chain data ${UI.maturity("preview")}`).option("--start", "Restart the node after reset", false).option("--yes", "Skip confirmation prompt", false).action(async (options) => {
|
|
2616
|
+
try {
|
|
2617
|
+
if (!options.yes) {
|
|
2618
|
+
const confirmed = await UI.confirm("This will delete all local chain data. Are you sure?");
|
|
2619
|
+
if (!confirmed) {
|
|
2620
|
+
console.log(" Aborted.");
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
const result = await runNodeReset({ removeData: true });
|
|
2625
|
+
UI.success(result.formatted);
|
|
2626
|
+
if (options.start) {
|
|
2627
|
+
UI.info("Starting node...");
|
|
2628
|
+
const startResult = await runNodeStart({});
|
|
2629
|
+
console.log(startResult.formatted);
|
|
2630
|
+
}
|
|
2631
|
+
} catch (e) {
|
|
2632
|
+
handleError(e);
|
|
2633
|
+
}
|
|
2634
|
+
});
|
|
2416
2635
|
nodeCmd.command("status").description("Check node status").action(async () => {
|
|
2417
2636
|
try {
|
|
2418
2637
|
const result = await runNodeStatus({});
|
|
@@ -2421,15 +2640,25 @@ function registerNodeCommands(program) {
|
|
|
2421
2640
|
handleError(e);
|
|
2422
2641
|
}
|
|
2423
2642
|
});
|
|
2643
|
+
nodeCmd.command("logs").description(`View node logs ${UI.maturity("preview")}`).option("--tail <n>", "Number of lines to show", "100").option("--follow", "Follow log output", false).action(async (options) => {
|
|
2644
|
+
try {
|
|
2645
|
+
const result = await runNodeLogs({
|
|
2646
|
+
tail: parseInt(options.tail, 10)
|
|
2647
|
+
});
|
|
2648
|
+
if (result) console.log(result);
|
|
2649
|
+
} catch (e) {
|
|
2650
|
+
handleError(e);
|
|
2651
|
+
}
|
|
2652
|
+
});
|
|
2424
2653
|
}
|
|
2425
2654
|
|
|
2426
2655
|
// src/commands/config.ts
|
|
2427
2656
|
function registerConfigCommands(program) {
|
|
2428
2657
|
const configCmd = program.command("config").description("Manage HardKAS configuration");
|
|
2429
2658
|
configCmd.command("show").description("Show the current HardKAS configuration").option("--config <path>", "Path to config file").option("--json", "Output as JSON", false).action(async (options) => {
|
|
2430
|
-
const { loadHardkasConfig:
|
|
2659
|
+
const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
|
|
2431
2660
|
try {
|
|
2432
|
-
const loaded = await
|
|
2661
|
+
const loaded = await loadHardkasConfig5(options.config ? { configPath: options.config } : {});
|
|
2433
2662
|
if (options.json) {
|
|
2434
2663
|
console.log(JSON.stringify(loaded, null, 2));
|
|
2435
2664
|
return;
|
|
@@ -2459,7 +2688,7 @@ function registerConfigCommands(program) {
|
|
|
2459
2688
|
}
|
|
2460
2689
|
|
|
2461
2690
|
// src/commands/misc.ts
|
|
2462
|
-
import { loadOrCreateLocalnetState as
|
|
2691
|
+
import { loadOrCreateLocalnetState as loadOrCreateLocalnetState3 } from "@hardkas/localnet";
|
|
2463
2692
|
|
|
2464
2693
|
// src/runners/example-list-runner.ts
|
|
2465
2694
|
import fs7 from "fs/promises";
|
|
@@ -2566,7 +2795,7 @@ function registerMiscCommands(program) {
|
|
|
2566
2795
|
});
|
|
2567
2796
|
program.command("dev").description("Start development environment").option("--mode <mode>", "simulated or node", "simulated").action(async (options) => {
|
|
2568
2797
|
if (options.mode === "simulated") {
|
|
2569
|
-
const state = await
|
|
2798
|
+
const state = await loadOrCreateLocalnetState3();
|
|
2570
2799
|
UI.success("Local HardKAS devnet (simulated) is ready.");
|
|
2571
2800
|
UI.info(`Network: ${state.networkId}`);
|
|
2572
2801
|
UI.info(`Accounts: ${state.accounts.length}`);
|
|
@@ -2579,11 +2808,50 @@ function registerMiscCommands(program) {
|
|
|
2579
2808
|
// src/commands/query.ts
|
|
2580
2809
|
function registerQueryCommands(program) {
|
|
2581
2810
|
const queryCmd = program.command("query").description("Query and introspect HardKAS artifacts, lineage, and workflows");
|
|
2582
|
-
const artifactsCmd = queryCmd.command("artifacts").description(
|
|
2811
|
+
const artifactsCmd = queryCmd.command("artifacts").description(`Query artifact store ${UI.maturity("stable")}`);
|
|
2812
|
+
const storeCmd = queryCmd.command("store").description(`Manage query store index ${UI.maturity("alpha")}`);
|
|
2813
|
+
storeCmd.command("doctor").description("Integrity and freshness check of the query store index").action(async () => {
|
|
2814
|
+
try {
|
|
2815
|
+
const engine = await getQueryEngine();
|
|
2816
|
+
const report = await engine.backend.doctor();
|
|
2817
|
+
console.log("\n \u2550\u2550\u2550 Query Store Doctor \u2550\u2550\u2550\n");
|
|
2818
|
+
console.log(` Backend: ${engine.backend.kind()}`);
|
|
2819
|
+
console.log(` Overall: ${report.ok ? "\u2713 HEALTHY" : "\u2717 STALE / ISSUES"}`);
|
|
2820
|
+
console.log(` Last Indexed: ${report.lastIndexedAt || "never"}`);
|
|
2821
|
+
console.log(` Stale Rows: ${report.staleArtifacts || 0}`);
|
|
2822
|
+
console.log(` Zombie Rows: ${report.zombieArtifacts || 0}`);
|
|
2823
|
+
console.log(` Orphan Edges: ${report.orphanEdges || 0}`);
|
|
2824
|
+
if (report.corruptedFiles?.length > 0) {
|
|
2825
|
+
console.log("\n Corrupted Files:");
|
|
2826
|
+
for (const f of report.corruptedFiles) console.log(` \u2717 ${f}`);
|
|
2827
|
+
}
|
|
2828
|
+
if (!report.ok) {
|
|
2829
|
+
console.log("\n Recommendation: Run 'hardkas query store rebuild' to fix issues.\n");
|
|
2830
|
+
} else {
|
|
2831
|
+
console.log("\n Everything looks good.\n");
|
|
2832
|
+
}
|
|
2833
|
+
} catch (e) {
|
|
2834
|
+
handleError(e);
|
|
2835
|
+
process.exitCode = 1;
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2838
|
+
storeCmd.command("rebuild").description("Force a complete rebuild of the query store index").action(async () => {
|
|
2839
|
+
try {
|
|
2840
|
+
console.log("\n Rebuilding query store index...");
|
|
2841
|
+
const engine = await getQueryEngine();
|
|
2842
|
+
const start = Date.now();
|
|
2843
|
+
await engine.backend.rebuild();
|
|
2844
|
+
console.log(` \u2713 Index rebuilt successfully in ${Date.now() - start}ms.
|
|
2845
|
+
`);
|
|
2846
|
+
} catch (e) {
|
|
2847
|
+
handleError(e);
|
|
2848
|
+
process.exitCode = 1;
|
|
2849
|
+
}
|
|
2850
|
+
});
|
|
2583
2851
|
artifactsCmd.command("list").description("List artifacts matching filters").option("--schema <schema>", "Filter by artifact schema (e.g. txPlan, signedTx)").option("--network <network>", "Filter by network ID").option("--mode <mode>", "Filter by mode (simulated/real)").option("--from <address>", "Filter by sender address").option("--to <address>", "Filter by recipient address").option("--sort <field:dir>", "Sort field and direction (e.g. createdAt:desc)").option("--limit <n>", "Max results", "100").option("--json", "Output as deterministic JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
|
|
2584
2852
|
try {
|
|
2585
|
-
const {
|
|
2586
|
-
const engine =
|
|
2853
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2854
|
+
const engine = await getQueryEngine();
|
|
2587
2855
|
const filters = [];
|
|
2588
2856
|
if (options.schema) filters.push({ field: "schema", op: "eq", value: `hardkas.${options.schema}` });
|
|
2589
2857
|
if (options.network) filters.push({ field: "networkId", op: "eq", value: options.network });
|
|
@@ -2617,8 +2885,8 @@ function registerQueryCommands(program) {
|
|
|
2617
2885
|
});
|
|
2618
2886
|
artifactsCmd.command("inspect <target>").description("Deep structural analysis of an artifact (path or contentHash)").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (target, options) => {
|
|
2619
2887
|
try {
|
|
2620
|
-
const {
|
|
2621
|
-
const engine =
|
|
2888
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2889
|
+
const engine = await getQueryEngine();
|
|
2622
2890
|
const request = createQueryRequest({
|
|
2623
2891
|
domain: "artifacts",
|
|
2624
2892
|
op: "inspect",
|
|
@@ -2639,8 +2907,8 @@ function registerQueryCommands(program) {
|
|
|
2639
2907
|
});
|
|
2640
2908
|
artifactsCmd.command("diff <left> <right>").description("Semantic diff between two artifacts").option("--json", "Output as JSON", false).action(async (left, right, options) => {
|
|
2641
2909
|
try {
|
|
2642
|
-
const {
|
|
2643
|
-
const engine =
|
|
2910
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2911
|
+
const engine = await getQueryEngine();
|
|
2644
2912
|
const request = createQueryRequest({
|
|
2645
2913
|
domain: "artifacts",
|
|
2646
2914
|
op: "diff",
|
|
@@ -2658,11 +2926,11 @@ function registerQueryCommands(program) {
|
|
|
2658
2926
|
process.exitCode = 1;
|
|
2659
2927
|
}
|
|
2660
2928
|
});
|
|
2661
|
-
const lineageCmd = queryCmd.command("lineage").description(
|
|
2929
|
+
const lineageCmd = queryCmd.command("lineage").description(`Traverse artifact lineage ${UI.maturity("preview")}`);
|
|
2662
2930
|
lineageCmd.command("chain <anchor>").description("Reconstruct lineage chain from an artifact (contentHash or artifactId)").option("--direction <dir>", "Traversal direction: ancestors or descendants", "ancestors").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").option("--why", "Shorthand for --explain full").action(async (anchor, options) => {
|
|
2663
2931
|
try {
|
|
2664
|
-
const {
|
|
2665
|
-
const engine =
|
|
2932
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2933
|
+
const engine = await getQueryEngine();
|
|
2666
2934
|
const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
|
|
2667
2935
|
const request = createQueryRequest({
|
|
2668
2936
|
domain: "lineage",
|
|
@@ -2684,8 +2952,8 @@ function registerQueryCommands(program) {
|
|
|
2684
2952
|
});
|
|
2685
2953
|
lineageCmd.command("transitions").description("List all lineage transitions").option("--root <hash>", "Filter by root artifact ID").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").option("--why", "Shorthand for --explain full").action(async (options) => {
|
|
2686
2954
|
try {
|
|
2687
|
-
const {
|
|
2688
|
-
const engine =
|
|
2955
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2956
|
+
const engine = await getQueryEngine();
|
|
2689
2957
|
const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
|
|
2690
2958
|
const request = createQueryRequest({
|
|
2691
2959
|
domain: "lineage",
|
|
@@ -2707,8 +2975,8 @@ function registerQueryCommands(program) {
|
|
|
2707
2975
|
});
|
|
2708
2976
|
lineageCmd.command("orphans").description("Find artifacts with broken lineage references").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
|
|
2709
2977
|
try {
|
|
2710
|
-
const {
|
|
2711
|
-
const engine =
|
|
2978
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
2979
|
+
const engine = await getQueryEngine();
|
|
2712
2980
|
const request = createQueryRequest({
|
|
2713
2981
|
domain: "lineage",
|
|
2714
2982
|
op: "orphans",
|
|
@@ -2726,11 +2994,11 @@ function registerQueryCommands(program) {
|
|
|
2726
2994
|
process.exitCode = 1;
|
|
2727
2995
|
}
|
|
2728
2996
|
});
|
|
2729
|
-
const replayCmd = queryCmd.command("replay").description(
|
|
2997
|
+
const replayCmd = queryCmd.command("replay").description(`Inspect replay history and divergence ${UI.maturity("preview")}`);
|
|
2730
2998
|
replayCmd.command("list").description("List all stored receipts").option("--status <status>", "Filter by status").option("--json", "Output as JSON", false).option("--limit <n>", "Max results", "100").action(async (options) => {
|
|
2731
2999
|
try {
|
|
2732
|
-
const {
|
|
2733
|
-
const engine =
|
|
3000
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3001
|
+
const engine = await getQueryEngine();
|
|
2734
3002
|
const filters = [];
|
|
2735
3003
|
if (options.status) filters.push({ field: "status", op: "eq", value: options.status });
|
|
2736
3004
|
const request = createQueryRequest({ domain: "replay", op: "list", filters, limit: parseInt(options.limit, 10) });
|
|
@@ -2748,8 +3016,8 @@ function registerQueryCommands(program) {
|
|
|
2748
3016
|
});
|
|
2749
3017
|
replayCmd.command("summary <txId>").description("Detailed receipt + trace summary for a transaction").option("--json", "Output as JSON", false).action(async (txId, options) => {
|
|
2750
3018
|
try {
|
|
2751
|
-
const {
|
|
2752
|
-
const engine =
|
|
3019
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3020
|
+
const engine = await getQueryEngine();
|
|
2753
3021
|
const request = createQueryRequest({ domain: "replay", op: "summary", params: { txId } });
|
|
2754
3022
|
const result = await engine.execute(request);
|
|
2755
3023
|
if (options.json) {
|
|
@@ -2765,8 +3033,8 @@ function registerQueryCommands(program) {
|
|
|
2765
3033
|
});
|
|
2766
3034
|
replayCmd.command("divergences").description("Detect receipts with replay divergence indicators").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
|
|
2767
3035
|
try {
|
|
2768
|
-
const {
|
|
2769
|
-
const engine =
|
|
3036
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3037
|
+
const engine = await getQueryEngine();
|
|
2770
3038
|
const request = createQueryRequest({
|
|
2771
3039
|
domain: "replay",
|
|
2772
3040
|
op: "divergences",
|
|
@@ -2786,8 +3054,8 @@ function registerQueryCommands(program) {
|
|
|
2786
3054
|
});
|
|
2787
3055
|
replayCmd.command("invariants <txId>").description("Check replay invariants for a specific transaction").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (txId, options) => {
|
|
2788
3056
|
try {
|
|
2789
|
-
const {
|
|
2790
|
-
const engine =
|
|
3057
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3058
|
+
const engine = await getQueryEngine();
|
|
2791
3059
|
const request = createQueryRequest({
|
|
2792
3060
|
domain: "replay",
|
|
2793
3061
|
op: "invariants",
|
|
@@ -2806,11 +3074,11 @@ function registerQueryCommands(program) {
|
|
|
2806
3074
|
process.exitCode = 1;
|
|
2807
3075
|
}
|
|
2808
3076
|
});
|
|
2809
|
-
const dagCmd = queryCmd.command("dag").description(
|
|
3077
|
+
const dagCmd = queryCmd.command("dag").description(`Query simulated DAG state ${UI.maturity("research")}`);
|
|
2810
3078
|
dagCmd.command("conflicts").description("Show double-spend conflict analysis").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").option("--why", "Shorthand for --explain full").action(async (options) => {
|
|
2811
3079
|
try {
|
|
2812
|
-
const {
|
|
2813
|
-
const engine =
|
|
3080
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3081
|
+
const engine = await getQueryEngine();
|
|
2814
3082
|
const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
|
|
2815
3083
|
const request = createQueryRequest({ domain: "dag", op: "conflicts", explain });
|
|
2816
3084
|
const result = await engine.execute(request);
|
|
@@ -2827,8 +3095,8 @@ function registerQueryCommands(program) {
|
|
|
2827
3095
|
});
|
|
2828
3096
|
dagCmd.command("displaced").description("Show displaced transactions").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
|
|
2829
3097
|
try {
|
|
2830
|
-
const {
|
|
2831
|
-
const engine =
|
|
3098
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3099
|
+
const engine = await getQueryEngine();
|
|
2832
3100
|
const explain = options.explain === true ? "brief" : options.explain || false;
|
|
2833
3101
|
const request = createQueryRequest({ domain: "dag", op: "displaced", explain });
|
|
2834
3102
|
const result = await engine.execute(request);
|
|
@@ -2845,8 +3113,8 @@ function registerQueryCommands(program) {
|
|
|
2845
3113
|
});
|
|
2846
3114
|
dagCmd.command("history <txId>").description("Full lifecycle of a transaction through the DAG").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").option("--why", "Shorthand for --explain full").action(async (txId, options) => {
|
|
2847
3115
|
try {
|
|
2848
|
-
const {
|
|
2849
|
-
const engine =
|
|
3116
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3117
|
+
const engine = await getQueryEngine();
|
|
2850
3118
|
const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
|
|
2851
3119
|
const request = createQueryRequest({ domain: "dag", op: "history", params: { txId }, explain });
|
|
2852
3120
|
const result = await engine.execute(request);
|
|
@@ -2863,8 +3131,8 @@ function registerQueryCommands(program) {
|
|
|
2863
3131
|
});
|
|
2864
3132
|
dagCmd.command("sink-path").description("Show current selected path from genesis to sink").option("--json", "Output as JSON", false).action(async (options) => {
|
|
2865
3133
|
try {
|
|
2866
|
-
const {
|
|
2867
|
-
const engine =
|
|
3134
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3135
|
+
const engine = await getQueryEngine();
|
|
2868
3136
|
const request = createQueryRequest({ domain: "dag", op: "sink-path" });
|
|
2869
3137
|
const result = await engine.execute(request);
|
|
2870
3138
|
if (options.json) {
|
|
@@ -2880,8 +3148,8 @@ function registerQueryCommands(program) {
|
|
|
2880
3148
|
});
|
|
2881
3149
|
dagCmd.command("anomalies").description("Find transactions or blocks in unexpected states").option("--json", "Output as JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
|
|
2882
3150
|
try {
|
|
2883
|
-
const {
|
|
2884
|
-
const engine =
|
|
3151
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3152
|
+
const engine = await getQueryEngine();
|
|
2885
3153
|
const explain = options.explain === true ? "brief" : options.explain || false;
|
|
2886
3154
|
const request = createQueryRequest({ domain: "dag", op: "anomalies", explain });
|
|
2887
3155
|
const result = await engine.execute(request);
|
|
@@ -2898,8 +3166,8 @@ function registerQueryCommands(program) {
|
|
|
2898
3166
|
});
|
|
2899
3167
|
queryCmd.command("events").description("Query event log").option("--tx <txId>", "Filter events by transaction ID").option("--domain <domain>", "Filter by event domain").option("--kind <kind>", "Filter by event kind").option("--workflow <workflowId>", "Filter by workflow ID").option("--limit <n>", "Max results", "100").option("--json", "Output as deterministic JSON", false).option("--explain [level]", "Attach explain metadata (brief|full)").action(async (options) => {
|
|
2900
3168
|
try {
|
|
2901
|
-
const {
|
|
2902
|
-
const engine =
|
|
3169
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3170
|
+
const engine = await getQueryEngine();
|
|
2903
3171
|
const filters = [];
|
|
2904
3172
|
if (options.domain) filters.push({ field: "domain", op: "eq", value: options.domain });
|
|
2905
3173
|
if (options.kind) filters.push({ field: "kind", op: "eq", value: options.kind });
|
|
@@ -2926,10 +3194,10 @@ function registerQueryCommands(program) {
|
|
|
2926
3194
|
process.exitCode = 1;
|
|
2927
3195
|
}
|
|
2928
3196
|
});
|
|
2929
|
-
queryCmd.command("tx <txId>").description(
|
|
3197
|
+
queryCmd.command("tx <txId>").description(`Aggregate all data for a transaction ${UI.maturity("stable")}`).option("--json", "Output as deterministic JSON", false).option("--explain [level]", "Attach explain metadata (brief|full)").action(async (txId, options) => {
|
|
2930
3198
|
try {
|
|
2931
|
-
const {
|
|
2932
|
-
const engine =
|
|
3199
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
3200
|
+
const engine = await getQueryEngine();
|
|
2933
3201
|
const request = createQueryRequest({
|
|
2934
3202
|
domain: "tx",
|
|
2935
3203
|
op: "aggregate",
|
|
@@ -2960,9 +3228,12 @@ function printArtifactList(result) {
|
|
|
2960
3228
|
}
|
|
2961
3229
|
console.log(`
|
|
2962
3230
|
queryHash: ${result.queryHash.slice(0, 16)}...`);
|
|
2963
|
-
|
|
3231
|
+
const backend = result.annotations.backendUsed || "unknown";
|
|
3232
|
+
const freshness = result.annotations.freshness ? ` | ${result.annotations.freshness}` : "";
|
|
3233
|
+
console.log(` ${result.annotations.executionMs}ms | backend:${backend}${freshness} | ${result.annotations.filesScanned ?? 0} files scanned
|
|
2964
3234
|
`);
|
|
2965
|
-
|
|
3235
|
+
printExplain(result.explain);
|
|
3236
|
+
printWhy(result.why);
|
|
2966
3237
|
}
|
|
2967
3238
|
function printInspectResult(result) {
|
|
2968
3239
|
const item = result.items[0];
|
|
@@ -2988,7 +3259,8 @@ function printInspectResult(result) {
|
|
|
2988
3259
|
for (const err of item.integrity.errors) console.log(` \u2717 ${err}`);
|
|
2989
3260
|
}
|
|
2990
3261
|
console.log("");
|
|
2991
|
-
|
|
3262
|
+
printExplain(result.explain);
|
|
3263
|
+
printWhy(result.why);
|
|
2992
3264
|
}
|
|
2993
3265
|
function printDiffResult(result) {
|
|
2994
3266
|
const diff = result.items[0];
|
|
@@ -3029,7 +3301,8 @@ function printLineageChain(result) {
|
|
|
3029
3301
|
console.log(`${prefix} ${node.schema} [${node.contentHash.slice(0, 12)}...] ${node.networkId}/${node.mode}`);
|
|
3030
3302
|
}
|
|
3031
3303
|
console.log("");
|
|
3032
|
-
|
|
3304
|
+
printExplain(result.explain);
|
|
3305
|
+
printWhy(result.why);
|
|
3033
3306
|
}
|
|
3034
3307
|
function printTransitions(result) {
|
|
3035
3308
|
console.log(`
|
|
@@ -3040,7 +3313,8 @@ function printTransitions(result) {
|
|
|
3040
3313
|
console.log(` ${marker} ${t.from.schema} \u2192 ${t.to.schema} [${t.rule}]`);
|
|
3041
3314
|
}
|
|
3042
3315
|
console.log("");
|
|
3043
|
-
|
|
3316
|
+
printExplain(result.explain);
|
|
3317
|
+
printWhy(result.why);
|
|
3044
3318
|
}
|
|
3045
3319
|
function printOrphans(result) {
|
|
3046
3320
|
if (result.total === 0) {
|
|
@@ -3056,7 +3330,8 @@ function printOrphans(result) {
|
|
|
3056
3330
|
console.log(` Reason: ${o.reason}
|
|
3057
3331
|
`);
|
|
3058
3332
|
}
|
|
3059
|
-
|
|
3333
|
+
printExplain(result.explain);
|
|
3334
|
+
printWhy(result.why);
|
|
3060
3335
|
}
|
|
3061
3336
|
function printReplayList(result) {
|
|
3062
3337
|
console.log(`
|
|
@@ -3101,7 +3376,8 @@ function printDivergences(result) {
|
|
|
3101
3376
|
console.log(` Actual: ${d.actual.slice(0, 60)}
|
|
3102
3377
|
`);
|
|
3103
3378
|
}
|
|
3104
|
-
|
|
3379
|
+
printExplain(result.explain);
|
|
3380
|
+
printWhy(result.why);
|
|
3105
3381
|
}
|
|
3106
3382
|
function printInvariants(result) {
|
|
3107
3383
|
const inv = result.items[0];
|
|
@@ -3121,7 +3397,8 @@ function printInvariants(result) {
|
|
|
3121
3397
|
for (const i of inv.issues) console.log(` \u2717 ${i}`);
|
|
3122
3398
|
}
|
|
3123
3399
|
console.log("");
|
|
3124
|
-
|
|
3400
|
+
printExplain(result.explain);
|
|
3401
|
+
printWhy(result.why);
|
|
3125
3402
|
}
|
|
3126
3403
|
function printDagConflicts(result) {
|
|
3127
3404
|
console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
|
|
@@ -3137,7 +3414,8 @@ function printDagConflicts(result) {
|
|
|
3137
3414
|
for (const l of c.loserTxIds) console.log(` \u2514\u2500 LOSER: ${l.slice(0, 24)}...`);
|
|
3138
3415
|
console.log("");
|
|
3139
3416
|
}
|
|
3140
|
-
|
|
3417
|
+
printExplain(result.explain);
|
|
3418
|
+
printWhy(result.why);
|
|
3141
3419
|
}
|
|
3142
3420
|
function printDagDisplaced(result) {
|
|
3143
3421
|
console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
|
|
@@ -3153,7 +3431,8 @@ function printDagDisplaced(result) {
|
|
|
3153
3431
|
console.log(` ${d.reason}
|
|
3154
3432
|
`);
|
|
3155
3433
|
}
|
|
3156
|
-
|
|
3434
|
+
printExplain(result.explain);
|
|
3435
|
+
printWhy(result.why);
|
|
3157
3436
|
}
|
|
3158
3437
|
function printDagHistory(result) {
|
|
3159
3438
|
console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
|
|
@@ -3169,7 +3448,8 @@ function printDagHistory(result) {
|
|
|
3169
3448
|
console.log(` ${status.padEnd(10)} block:${e.blockId.slice(0, 12)}... daa:${e.daaScore} ${sinkPath}`);
|
|
3170
3449
|
}
|
|
3171
3450
|
console.log("");
|
|
3172
|
-
|
|
3451
|
+
printExplain(result.explain);
|
|
3452
|
+
printWhy(result.why);
|
|
3173
3453
|
}
|
|
3174
3454
|
function printSinkPath(result) {
|
|
3175
3455
|
console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
|
|
@@ -3202,19 +3482,39 @@ function printDagAnomalies(result) {
|
|
|
3202
3482
|
console.log(` \u2717 [${a.kind}] ${a.description}
|
|
3203
3483
|
`);
|
|
3204
3484
|
}
|
|
3205
|
-
if (result.explain)
|
|
3485
|
+
if (result.explain) printExplain(result.explain);
|
|
3486
|
+
}
|
|
3487
|
+
function printExplain(explain) {
|
|
3488
|
+
if (!explain) return;
|
|
3489
|
+
console.log(" \u2500\u2500\u2500 Explain: Technical Diagnostics \u2500\u2500\u2500\n");
|
|
3490
|
+
console.log(` Backend: ${explain.backend}`);
|
|
3491
|
+
console.log(` Freshness: ${explain.freshness}`);
|
|
3492
|
+
console.log(` Rows Read: ${explain.rowsRead}`);
|
|
3493
|
+
console.log(` Files Scan: ${explain.scannedFiles}`);
|
|
3494
|
+
if (explain.executionPlan && explain.executionPlan.length > 0) {
|
|
3495
|
+
console.log(` Plan: ${explain.executionPlan.join(" \u2192 ")}`);
|
|
3496
|
+
}
|
|
3497
|
+
if (explain.warnings && explain.warnings.length > 0) {
|
|
3498
|
+
console.log(` Warnings:`);
|
|
3499
|
+
for (const w of explain.warnings) console.log(` \u26A0 ${w}`);
|
|
3500
|
+
}
|
|
3501
|
+
console.log("");
|
|
3206
3502
|
}
|
|
3207
|
-
function
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3503
|
+
function printWhy(why) {
|
|
3504
|
+
if (!why || why.length === 0) return;
|
|
3505
|
+
console.log(" \u2500\u2500\u2500 Why: Causal Analysis \u2500\u2500\u2500\n");
|
|
3506
|
+
for (const block of why) {
|
|
3507
|
+
console.log(` Q: ${block.question}`);
|
|
3508
|
+
console.log(` A: ${block.answer}`);
|
|
3509
|
+
for (const step of block.causalChain) {
|
|
3212
3510
|
console.log(` ${step.order}. ${step.assertion}`);
|
|
3213
|
-
|
|
3511
|
+
console.log(` Evidence: ${step.evidence}`);
|
|
3512
|
+
if (step.rule) console.log(` Rule: ${step.rule}`);
|
|
3214
3513
|
}
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3514
|
+
if (block.evidence && block.evidence.length > 0) {
|
|
3515
|
+
console.log(` Evidence Refs: ${block.evidence.map((e) => `${e.type}:${e.value.slice(0, 12)}...`).join(", ")}`);
|
|
3516
|
+
}
|
|
3517
|
+
console.log("");
|
|
3218
3518
|
}
|
|
3219
3519
|
}
|
|
3220
3520
|
function printEventList(result) {
|
|
@@ -3229,7 +3529,8 @@ function printEventList(result) {
|
|
|
3229
3529
|
queryHash: ${result.queryHash.slice(0, 16)}...`);
|
|
3230
3530
|
console.log(` ${result.annotations.executionMs}ms
|
|
3231
3531
|
`);
|
|
3232
|
-
|
|
3532
|
+
printExplain(result.explain);
|
|
3533
|
+
printWhy(result.why);
|
|
3233
3534
|
}
|
|
3234
3535
|
function printTxAggregate(result) {
|
|
3235
3536
|
const agg = result.items[0];
|
|
@@ -3264,32 +3565,79 @@ function printTxAggregate(result) {
|
|
|
3264
3565
|
}
|
|
3265
3566
|
}
|
|
3266
3567
|
console.log("");
|
|
3267
|
-
if (result.explain)
|
|
3568
|
+
if (result.explain) printExplain(result.explain);
|
|
3569
|
+
}
|
|
3570
|
+
async function getQueryEngine() {
|
|
3571
|
+
const { QueryEngine } = await import("@hardkas/query");
|
|
3572
|
+
return QueryEngine.create({
|
|
3573
|
+
artifactDir: process.cwd()
|
|
3574
|
+
});
|
|
3268
3575
|
}
|
|
3269
3576
|
|
|
3270
|
-
// src/
|
|
3577
|
+
// src/runners/test-runner.ts
|
|
3271
3578
|
import { Hardkas } from "@hardkas/sdk";
|
|
3272
|
-
function
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3579
|
+
async function runTest(options) {
|
|
3580
|
+
const { files, network, watch, json, reporter } = options;
|
|
3581
|
+
if (!json) {
|
|
3582
|
+
UI.header("HardKAS Test Runner");
|
|
3583
|
+
UI.info(`Network: ${network}`);
|
|
3584
|
+
}
|
|
3585
|
+
let hardkas;
|
|
3586
|
+
try {
|
|
3587
|
+
hardkas = await Hardkas.open(".");
|
|
3588
|
+
} catch (e) {
|
|
3589
|
+
throw new Error("Could not find a valid HardKAS project in this directory.");
|
|
3590
|
+
}
|
|
3591
|
+
const searchPatterns = files.length > 0 ? files : ["test/**/*.test.ts", "tests/**/*.test.ts"];
|
|
3592
|
+
if (!json) {
|
|
3593
|
+
UI.info(`Searching for tests: ${searchPatterns.join(", ")}`);
|
|
3594
|
+
}
|
|
3595
|
+
try {
|
|
3596
|
+
const { startVitest } = await import("vitest/node");
|
|
3597
|
+
const vitestOptions = {
|
|
3598
|
+
run: !watch,
|
|
3599
|
+
watch: !!watch,
|
|
3600
|
+
reporter: json ? "json" : reporter || "default",
|
|
3601
|
+
globals: true,
|
|
3602
|
+
environment: "node",
|
|
3603
|
+
include: searchPatterns,
|
|
3604
|
+
exclude: ["**/node_modules/**", "**/dist/**", "**/.hardkas/**"],
|
|
3605
|
+
// Injected environment variables for tests to consume
|
|
3606
|
+
env: {
|
|
3607
|
+
HARDKAS_NETWORK: network,
|
|
3608
|
+
HARDKAS_CWD: process.cwd()
|
|
3281
3609
|
}
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3610
|
+
};
|
|
3611
|
+
const vitest = await startVitest("test", searchPatterns, vitestOptions);
|
|
3612
|
+
if (!vitest) {
|
|
3613
|
+
throw new Error("Failed to initialize test engine.");
|
|
3614
|
+
}
|
|
3615
|
+
} catch (e) {
|
|
3616
|
+
const error = e;
|
|
3617
|
+
if (error.code === "ERR_MODULE_NOT_FOUND" || error.message?.includes("vitest")) {
|
|
3618
|
+
UI.warning("Vitest is not installed in this project.");
|
|
3619
|
+
UI.info("Run 'pnpm add -D vitest' to enable real test execution.");
|
|
3620
|
+
UI.divider();
|
|
3621
|
+
UI.info("Fallback: No real tests were executed because the engine is missing.");
|
|
3622
|
+
process.exit(1);
|
|
3623
|
+
}
|
|
3624
|
+
throw e;
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
// src/commands/test.ts
|
|
3629
|
+
function registerTestCommands(program) {
|
|
3630
|
+
program.command("test [files...]").description(`Run HardKAS tests against localnet ${UI.maturity("stable")}`).option("--network <network>", "Network to test against", "simnet").option("--watch", "Watch for changes", false).option("--json", "Output results as JSON", false).option("--reporter <reporter>", "Reporter to use", "default").action(async (files, options) => {
|
|
3631
|
+
try {
|
|
3632
|
+
await runTest({
|
|
3633
|
+
files,
|
|
3634
|
+
network: options.network,
|
|
3635
|
+
watch: options.watch,
|
|
3636
|
+
json: options.json,
|
|
3637
|
+
reporter: options.reporter
|
|
3638
|
+
});
|
|
3291
3639
|
} catch (e) {
|
|
3292
|
-
|
|
3640
|
+
handleError(e, "Test execution failed");
|
|
3293
3641
|
process.exit(1);
|
|
3294
3642
|
}
|
|
3295
3643
|
});
|
|
@@ -3300,8 +3648,8 @@ import os from "os";
|
|
|
3300
3648
|
import path10 from "path";
|
|
3301
3649
|
import fs9 from "fs/promises";
|
|
3302
3650
|
import pc from "picocolors";
|
|
3303
|
-
import { loadHardkasConfig as
|
|
3304
|
-
import { JsonWrpcKaspaClient as
|
|
3651
|
+
import { loadHardkasConfig as loadHardkasConfig4 } from "@hardkas/config";
|
|
3652
|
+
import { JsonWrpcKaspaClient as JsonWrpcKaspaClient4 } from "@hardkas/kaspa-rpc";
|
|
3305
3653
|
import { HardkasStore } from "@hardkas/query-store";
|
|
3306
3654
|
function registerDoctorCommand(program) {
|
|
3307
3655
|
program.command("doctor").description("Perform a full system diagnostic and health report").action(async () => {
|
|
@@ -3321,7 +3669,7 @@ async function runDoctor() {
|
|
|
3321
3669
|
UI.divider();
|
|
3322
3670
|
UI.header("Configuration Analysis");
|
|
3323
3671
|
try {
|
|
3324
|
-
const loaded = await
|
|
3672
|
+
const loaded = await loadHardkasConfig4({ cwd: process.cwd() });
|
|
3325
3673
|
UI.success(`Config found: ${pc.cyan(path10.basename(loaded.path || "unknown"))}`);
|
|
3326
3674
|
UI.field("Default Network", loaded.config.defaultNetwork || "simnet");
|
|
3327
3675
|
} catch (e) {
|
|
@@ -3330,15 +3678,15 @@ async function runDoctor() {
|
|
|
3330
3678
|
UI.divider();
|
|
3331
3679
|
UI.header("RPC Connectivity & Health");
|
|
3332
3680
|
try {
|
|
3333
|
-
const loaded = await
|
|
3681
|
+
const loaded = await loadHardkasConfig4({ cwd: process.cwd() });
|
|
3334
3682
|
const networkId = loaded.config.defaultNetwork || "simnet";
|
|
3335
3683
|
const target = loaded.config.networks?.[networkId];
|
|
3336
3684
|
let rpcUrl = "ws://127.0.0.1:18210";
|
|
3337
3685
|
if (target?.rpcUrl) rpcUrl = target.rpcUrl;
|
|
3338
3686
|
UI.info(`Connecting to ${pc.cyan(rpcUrl)}...`);
|
|
3339
|
-
const rpc = new
|
|
3687
|
+
const rpc = new JsonWrpcKaspaClient4({ rpcUrl });
|
|
3340
3688
|
const info = await rpc.getInfo();
|
|
3341
|
-
UI.success(`RPC Alive: ${pc.bold(info.networkId)}`);
|
|
3689
|
+
UI.success(`RPC Alive: ${pc.bold(info.networkId || "active")}`);
|
|
3342
3690
|
UI.field("Synced", info.isSynced ? pc.green("YES") : pc.yellow("NO"));
|
|
3343
3691
|
if (info.serverVersion) UI.field("Version", info.serverVersion);
|
|
3344
3692
|
} catch (e) {
|
|
@@ -3366,28 +3714,49 @@ async function runDoctor() {
|
|
|
3366
3714
|
}
|
|
3367
3715
|
UI.divider();
|
|
3368
3716
|
UI.header("Query Store (SQLite) Status");
|
|
3369
|
-
const dbPath = path10.join(hardkasDir, "
|
|
3717
|
+
const dbPath = path10.join(hardkasDir, "store.db");
|
|
3370
3718
|
try {
|
|
3371
3719
|
const store = new HardkasStore({ dbPath });
|
|
3372
3720
|
store.connect();
|
|
3373
3721
|
const db = store.getDatabase();
|
|
3374
3722
|
const artCount = db.prepare("SELECT COUNT(*) as count FROM artifacts").get().count;
|
|
3375
3723
|
const eventCount = db.prepare("SELECT COUNT(*) as count FROM events").get().count;
|
|
3376
|
-
UI.success("Relational index (
|
|
3724
|
+
UI.success("Relational index (store.db) is healthy");
|
|
3377
3725
|
UI.field("Indexed Artifacts", artCount);
|
|
3378
3726
|
UI.field("Indexed Events", eventCount);
|
|
3379
3727
|
if (artCount === 0 && eventCount === 0) {
|
|
3380
|
-
UI.warning("Database is empty. Run 'hardkas query store
|
|
3728
|
+
UI.warning("Database is empty. Run 'hardkas query store rebuild' to populate.");
|
|
3381
3729
|
}
|
|
3382
3730
|
store.disconnect();
|
|
3383
3731
|
} catch (e) {
|
|
3384
|
-
UI.error("Query Store Issues", "The SQLite database might be corrupt or inaccessible.");
|
|
3732
|
+
UI.error("Query Store Issues", "The SQLite database might be corrupt or inaccessible. Run 'hardkas query store rebuild' to repair.");
|
|
3385
3733
|
}
|
|
3386
3734
|
UI.footer("Use 'hardkas query' for deep operational introspection.");
|
|
3387
3735
|
}
|
|
3388
3736
|
|
|
3737
|
+
// src/commands/faucet.ts
|
|
3738
|
+
function registerFaucetCommand(program) {
|
|
3739
|
+
program.command("faucet <identifier>").description(`Fund an account with KAS (Local only) ${UI.maturity("stable")}`).option("--amount <kas>", "Amount in KAS to fund", "1000").action(async (identifier, options) => {
|
|
3740
|
+
try {
|
|
3741
|
+
const amountSompi = BigInt(parseFloat(options.amount) * 1e8);
|
|
3742
|
+
const result = await runAccountsFund({ identifier, amountSompi });
|
|
3743
|
+
console.log(result.formatted);
|
|
3744
|
+
} catch (e) {
|
|
3745
|
+
handleError(e);
|
|
3746
|
+
process.exitCode = 1;
|
|
3747
|
+
}
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3389
3751
|
// src/index.ts
|
|
3390
|
-
|
|
3752
|
+
import { readFileSync } from "fs";
|
|
3753
|
+
import { fileURLToPath } from "url";
|
|
3754
|
+
import path11 from "path";
|
|
3755
|
+
var packageJsonPath = path11.resolve(
|
|
3756
|
+
path11.dirname(fileURLToPath(import.meta.url)),
|
|
3757
|
+
"../package.json"
|
|
3758
|
+
);
|
|
3759
|
+
var { version: HARDKAS_VERSION4 } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
3391
3760
|
async function main() {
|
|
3392
3761
|
const program = new Command();
|
|
3393
3762
|
program.name("hardkas").description("HardKAS: Kaspa-native developer operating environment").version(HARDKAS_VERSION4);
|
|
@@ -3406,15 +3775,21 @@ async function main() {
|
|
|
3406
3775
|
registerQueryCommands(program);
|
|
3407
3776
|
registerTestCommands(program);
|
|
3408
3777
|
registerDoctorCommand(program);
|
|
3778
|
+
registerFaucetCommand(program);
|
|
3409
3779
|
try {
|
|
3410
3780
|
await program.parseAsync(process.argv);
|
|
3411
3781
|
} catch (err) {
|
|
3782
|
+
const { maskSecrets } = await import("@hardkas/core");
|
|
3412
3783
|
console.error(`
|
|
3413
|
-
Error: ${err.message}`);
|
|
3784
|
+
Error: ${maskSecrets(err.message || String(err))}`);
|
|
3414
3785
|
process.exit(1);
|
|
3415
3786
|
}
|
|
3416
3787
|
}
|
|
3417
|
-
main().catch((err) => {
|
|
3418
|
-
|
|
3788
|
+
main().catch(async (err) => {
|
|
3789
|
+
const { maskSecrets } = await import("@hardkas/core");
|
|
3790
|
+
console.error("Fatal error:", maskSecrets(err.message || String(err)));
|
|
3791
|
+
if (err.stack) {
|
|
3792
|
+
console.error(maskSecrets(err.stack));
|
|
3793
|
+
}
|
|
3419
3794
|
process.exit(1);
|
|
3420
3795
|
});
|