@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.
package/dist/index.js CHANGED
@@ -1,10 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ acquirePassword
4
+ } from "./chunk-ZM2NBOAE.js";
5
+ import {
6
+ inspectDeployment,
7
+ listAllDeployments,
8
+ showDeploymentHistory,
9
+ trackDeployment,
10
+ verifyDeploymentStatus
11
+ } from "./chunk-KA5CAWI2.js";
2
12
  import {
3
13
  UI,
4
- handleError
5
- } from "./chunk-M54KNJEH.js";
14
+ handleError,
15
+ handleLockError
16
+ } from "./chunk-K7XPWWIO.js";
6
17
 
7
- // src/index.ts
18
+ // src/program.ts
8
19
  import { Command } from "commander";
9
20
 
10
21
  // src/runners/up-runner.ts
@@ -70,36 +81,44 @@ async function runUp() {
70
81
 
71
82
  // src/commands/init.ts
72
83
  function registerInitCommands(program) {
73
- program.command("init").description("Initialize a new HardKAS project").argument("[name]", "Project name or directory").option("--force", "Overwrite existing hardkas.config.ts", false).action(async (name, options) => {
84
+ 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) => {
85
+ let targetDir = process.cwd();
86
+ const path15 = await import("path");
87
+ const { withLock: withLock2 } = await import("@hardkas/core");
88
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
89
+ if (name) {
90
+ targetDir = path15.join(process.cwd(), name);
91
+ }
74
92
  try {
75
- const fs10 = await import("fs");
76
- const path11 = await import("path");
77
- let targetDir = process.cwd();
78
- if (name) {
79
- targetDir = path11.join(process.cwd(), name);
80
- if (!fs10.existsSync(targetDir)) {
81
- fs10.mkdirSync(targetDir, { recursive: true });
93
+ await withLock2({
94
+ rootDir: targetDir,
95
+ name: "workspace",
96
+ command: `hardkas init ${name || ""}`
97
+ }, async () => {
98
+ const fs12 = await import("fs");
99
+ const { writeFileAtomicSync } = await import("@hardkas/core");
100
+ if (name && !fs12.existsSync(targetDir)) {
101
+ fs12.mkdirSync(targetDir, { recursive: true });
82
102
  }
83
- }
84
- const configFile = path11.join(targetDir, "hardkas.config.ts");
85
- const pkgFile = path11.join(targetDir, "package.json");
86
- if (fs10.existsSync(configFile) && !options.force) {
87
- UI.warning(`hardkas.config.ts already exists in ${name || "current directory"}. Use --force to overwrite.`);
88
- return;
89
- }
90
- if (!fs10.existsSync(pkgFile)) {
91
- const pkgTemplate = {
92
- name: name || "hardkas-project",
93
- version: "1.0.0",
94
- type: "module",
95
- dependencies: {
96
- "@hardkas/sdk": "latest"
97
- }
98
- };
99
- fs10.writeFileSync(pkgFile, JSON.stringify(pkgTemplate, null, 2), "utf-8");
100
- UI.info("Created: package.json");
101
- }
102
- const template = `import { defineHardkasConfig } from "@hardkas/sdk";
103
+ const configFile = path15.join(targetDir, "hardkas.config.ts");
104
+ const pkgFile = path15.join(targetDir, "package.json");
105
+ if (fs12.existsSync(configFile) && !options.force) {
106
+ UI.warning(`hardkas.config.ts already exists in ${name || "current directory"}. Use --force to overwrite.`);
107
+ return;
108
+ }
109
+ if (!fs12.existsSync(pkgFile)) {
110
+ const pkgTemplate = {
111
+ name: name || "hardkas-project",
112
+ version: "1.0.0",
113
+ type: "module",
114
+ dependencies: {
115
+ "@hardkas/sdk": "latest"
116
+ }
117
+ };
118
+ writeFileAtomicSync(pkgFile, JSON.stringify(pkgTemplate, null, 2), { encoding: "utf-8" });
119
+ UI.info("Created: package.json");
120
+ }
121
+ const template = `import { defineHardkasConfig } from "@hardkas/sdk";
103
122
 
104
123
  export default defineHardkasConfig({
105
124
  // HardKAS v0.2.2-alpha Configuration
@@ -120,26 +139,39 @@ export default defineHardkasConfig({
120
139
  accounts: {
121
140
  alice: {
122
141
  kind: "simulated",
123
- address: "kaspa:sim_alice"
142
+ address: "kaspasim:sim_alice"
124
143
  },
125
144
  bob: {
126
145
  kind: "simulated",
127
- address: "kaspa:sim_bob"
146
+ address: "kaspasim:sim_bob"
128
147
  }
129
148
  }
130
149
  });
131
150
  `;
132
- fs10.writeFileSync(configFile, template, "utf-8");
133
- UI.success(`HardKAS project '${name || "current"}' initialized successfully.`);
134
- if (name) UI.info(`Project folder: ${targetDir}`);
135
- UI.info(`Created: hardkas.config.ts (v0.2-alpha)`);
136
- UI.footer(`Run 'cd ${name || "."}' and then 'hardkas up' to start.`);
151
+ writeFileAtomicSync(configFile, template, { encoding: "utf-8" });
152
+ const gitIgnoreFile = path15.join(targetDir, ".gitignore");
153
+ const gitIgnoreEntry = "\n# HardKAS local storage\n.hardkas/\n";
154
+ if (!fs12.existsSync(gitIgnoreFile)) {
155
+ writeFileAtomicSync(gitIgnoreFile, gitIgnoreEntry, { encoding: "utf-8" });
156
+ UI.info("Created: .gitignore");
157
+ } else {
158
+ const content = fs12.readFileSync(gitIgnoreFile, "utf-8");
159
+ if (!content.includes(".hardkas/")) {
160
+ fs12.appendFileSync(gitIgnoreFile, gitIgnoreEntry, "utf-8");
161
+ UI.info("Updated: .gitignore (added .hardkas/)");
162
+ }
163
+ }
164
+ UI.success(`HardKAS project '${name || "current"}' initialized successfully.`);
165
+ if (name) UI.info(`Project folder: ${targetDir}`);
166
+ UI.info(`Created: hardkas.config.ts (v0.2-alpha)`);
167
+ UI.footer(`Run 'cd ${name || "."}' and then 'hardkas up' to start.`);
168
+ });
137
169
  } catch (e) {
138
- handleError(e, "Initialization failed");
170
+ handleLockError2(e);
139
171
  process.exitCode = 1;
140
172
  }
141
173
  });
142
- program.command("up").description("Boot or validate the HardKAS developer runtime environment").action(async () => {
174
+ program.command("up").description(`Boot or validate the HardKAS developer runtime environment ${UI.maturity("stable")}`).action(async () => {
143
175
  try {
144
176
  await runUp();
145
177
  } catch (e) {
@@ -234,8 +266,8 @@ async function runTxPlan(input) {
234
266
  const { target, name } = resolveNetworkTarget({ config, network: networkId });
235
267
  resolvedNetwork = name;
236
268
  if (target.kind === "simulated") {
237
- const { loadOrCreateLocalnetState: loadOrCreateLocalnetState3, getSpendableUtxos } = await import("@hardkas/localnet");
238
- const localState = await loadOrCreateLocalnetState3();
269
+ const { loadOrCreateLocalnetState: loadOrCreateLocalnetState4, getSpendableUtxos } = await import("@hardkas/localnet");
270
+ const localState = await loadOrCreateLocalnetState4();
239
271
  const unspent = getSpendableUtxos(localState, fromAddress);
240
272
  availableUtxos = unspent.map((u) => ({
241
273
  outpoint: {
@@ -248,17 +280,17 @@ async function runTxPlan(input) {
248
280
  }));
249
281
  mode = "simulated";
250
282
  } else if (target.kind === "kaspa-node" || target.kind === "kaspa-rpc") {
251
- const { JsonWrpcKaspaClient: JsonWrpcKaspaClient4 } = await import("@hardkas/kaspa-rpc");
252
- const { resolveRuntimeConfig } = await import("@hardkas/node-orchestrator");
283
+ const { JsonWrpcKaspaClient: JsonWrpcKaspaClient5 } = await import("@hardkas/kaspa-rpc");
284
+ const { resolveRuntimeConfig: resolveRuntimeConfig2 } = await import("@hardkas/node-orchestrator");
253
285
  rpcUrl = url || target.rpcUrl;
254
286
  if (!rpcUrl && target.kind === "kaspa-node") {
255
- rpcUrl = resolveRuntimeConfig({
287
+ rpcUrl = resolveRuntimeConfig2({
256
288
  network: target.network,
257
289
  ...target.dataDir ? { dataDir: target.dataDir } : {}
258
290
  }).rpcUrl;
259
291
  }
260
292
  if (!rpcUrl) throw new Error("Could not resolve RPC URL");
261
- const client = new JsonWrpcKaspaClient4({ rpcUrl });
293
+ const client = new JsonWrpcKaspaClient5({ rpcUrl });
262
294
  const rpcUtxos = await client.getUtxosByAddress(fromAddress);
263
295
  await client.close();
264
296
  availableUtxos = rpcUtxos.map((u) => ({
@@ -271,13 +303,13 @@ async function runTxPlan(input) {
271
303
  }
272
304
  } catch (e) {
273
305
  if (url || networkId !== "simnet") {
274
- const { JsonWrpcKaspaClient: JsonWrpcKaspaClient4 } = await import("@hardkas/kaspa-rpc");
275
- const { resolveRuntimeConfig } = await import("@hardkas/node-orchestrator");
306
+ const { JsonWrpcKaspaClient: JsonWrpcKaspaClient5 } = await import("@hardkas/kaspa-rpc");
307
+ const { resolveRuntimeConfig: resolveRuntimeConfig2 } = await import("@hardkas/node-orchestrator");
276
308
  rpcUrl = url;
277
309
  if (!rpcUrl) {
278
- rpcUrl = resolveRuntimeConfig({ network: networkId }).rpcUrl;
310
+ rpcUrl = resolveRuntimeConfig2({ network: networkId }).rpcUrl;
279
311
  }
280
- const client = new JsonWrpcKaspaClient4({ rpcUrl });
312
+ const client = new JsonWrpcKaspaClient5({ rpcUrl });
281
313
  const rpcUtxos = await client.getUtxosByAddress(fromAddress);
282
314
  await client.close();
283
315
  availableUtxos = rpcUtxos.map((u) => ({
@@ -328,6 +360,22 @@ async function runTxSign(input) {
328
360
  const { planArtifact, accountName, config, allowMainnetSigning } = input;
329
361
  const targetAccountName = accountName || planArtifact.from.accountName || planArtifact.from.input || planArtifact.from.address;
330
362
  const account = resolveHardkasAccount({ nameOrAddress: targetAccountName, config });
363
+ const artifactNetwork = planArtifact.networkId;
364
+ const accountAddressNetwork = getNetworkFromAddress(account.address || "");
365
+ const activeProfileNetwork = config.defaultNetwork;
366
+ if (artifactNetwork === "mainnet") {
367
+ UI.warning("CRITICAL: You are signing a transaction for MAINNET.");
368
+ UI.info("HardKAS is developer infrastructure, not production custody software.");
369
+ UI.info("Do not use high-value mainnet keys in this environment.");
370
+ if (!allowMainnetSigning) {
371
+ throw new Error("Mainnet signing is blocked. Use --allow-mainnet-signing if you understand the risks.");
372
+ }
373
+ }
374
+ if (artifactNetwork !== accountAddressNetwork && accountAddressNetwork !== "unknown") {
375
+ if (artifactNetwork === "mainnet" || accountAddressNetwork === "mainnet") {
376
+ throw new Error(`Network mismatch: Plan is for '${artifactNetwork}' but account is for '${accountAddressNetwork}'. Refusing to sign.`);
377
+ }
378
+ }
331
379
  const signedArtifact = await signTxPlanArtifact({
332
380
  planArtifact,
333
381
  account,
@@ -336,6 +384,12 @@ async function runTxSign(input) {
336
384
  });
337
385
  return signedArtifact;
338
386
  }
387
+ function getNetworkFromAddress(address) {
388
+ if (address.startsWith("kaspa:")) return "mainnet";
389
+ if (address.startsWith("kaspatest:")) return "testnet-10";
390
+ if (address.startsWith("kaspasim:")) return "simnet";
391
+ return "unknown";
392
+ }
339
393
 
340
394
  // src/runners/tx-send-runner.ts
341
395
  import {
@@ -413,6 +467,7 @@ async function runTxSend(input) {
413
467
  schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
414
468
  hardkasVersion: HARDKAS_VERSION,
415
469
  version: ARTIFACT_VERSION,
470
+ hashVersion: "sha256-canonical",
416
471
  networkId: resolvedName,
417
472
  mode: "simulated",
418
473
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -423,7 +478,7 @@ async function runTxSend(input) {
423
478
  to: { address: signedArtifact.to.address },
424
479
  amountSompi: signedArtifact.amountSompi,
425
480
  feeSompi: simResult.receipt.feeSompi,
426
- daaScore: simResult.receipt.daaScore.toString(),
481
+ daaScore: simResult.receipt.daaScore?.toString() || "0",
427
482
  submittedAt: simResult.receipt.createdAt,
428
483
  confirmedAt: simResult.receipt.createdAt,
429
484
  rpcUrl: "simulated://local"
@@ -432,6 +487,7 @@ async function runTxSend(input) {
432
487
  schema: ARTIFACT_SCHEMAS.TX_TRACE,
433
488
  hardkasVersion: HARDKAS_VERSION,
434
489
  version: ARTIFACT_VERSION,
490
+ hashVersion: "sha256-canonical",
435
491
  createdAt: receipt.createdAt,
436
492
  txId: receipt.txId,
437
493
  mode: "simulated",
@@ -471,6 +527,7 @@ Receipt: ${receiptPath}`
471
527
  schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
472
528
  hardkasVersion: HARDKAS_VERSION,
473
529
  version: ARTIFACT_VERSION,
530
+ hashVersion: "sha256-canonical",
474
531
  networkId: resolvedName,
475
532
  mode: "real",
476
533
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -482,7 +539,7 @@ Receipt: ${receiptPath}`
482
539
  amountSompi: signedArtifact.amountSompi,
483
540
  feeSompi: signedArtifact.metadata?.estimatedFeeSompi || "0",
484
541
  submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
485
- rpcUrl
542
+ ...rpcUrl ? { rpcUrl } : {}
486
543
  };
487
544
  return {
488
545
  accepted: !!result.accepted,
@@ -666,108 +723,148 @@ async function runTxReceipt(input) {
666
723
  // src/commands/tx.ts
667
724
  function registerTxCommands(program) {
668
725
  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 (path11) => {
726
+ tx.command("profile <path>").description(`Show detailed mass and fee breakdown for a transaction plan ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (path15, options) => {
670
727
  try {
671
- await runTxProfile({ path: path11 });
728
+ await runTxProfile({ path: path15, ...options });
672
729
  } catch (e) {
673
730
  handleError(e);
674
731
  process.exitCode = 1;
675
732
  }
676
733
  });
677
- tx.command("plan").description("Build a transaction plan artifact").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) => {
734
+ 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("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
735
+ const { withLock: withLock2 } = await import("@hardkas/core");
736
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
678
737
  try {
679
- const { loadHardkasConfig: loadHardkasConfig3 } = await import("@hardkas/config");
680
- const { writeArtifact: writeArtifact4, formatTxPlanArtifact } = await import("@hardkas/artifacts");
681
- const loaded = await loadHardkasConfig3();
682
- const artifact = await runTxPlan({
683
- from: options.from || "alice",
684
- to: options.to || "bob",
685
- amount: options.amount || "1",
686
- networkId: options.network,
687
- feeRate: options.feeRate,
688
- config: loaded.config,
689
- ...options.url ? { url: options.url } : {}
690
- });
691
- if (options.out) await writeArtifact4(options.out, artifact);
692
- if (options.json) console.log(JSON.stringify(artifact, bigIntReplacer, 2));
693
- else {
694
- console.log(formatTxPlanArtifact(artifact));
695
- if (options.out) console.log(`
738
+ await withLock2({
739
+ rootDir: process.cwd(),
740
+ name: "artifacts",
741
+ command: "hardkas tx plan",
742
+ wait: options.waitLock,
743
+ timeoutMs: parseInt(options.lockTimeout)
744
+ }, async () => {
745
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
746
+ const { writeArtifact: writeArtifact4, formatTxPlanArtifact } = await import("@hardkas/artifacts");
747
+ const loaded = await loadHardkasConfig15();
748
+ const artifact = await runTxPlan({
749
+ from: options.from || "alice",
750
+ to: options.to || "bob",
751
+ amount: options.amount || "1",
752
+ networkId: options.network,
753
+ feeRate: options.feeRate,
754
+ config: loaded.config,
755
+ ...options.url ? { url: options.url } : {}
756
+ });
757
+ if (options.out) await writeArtifact4(options.out, artifact);
758
+ if (options.json) console.log(JSON.stringify(artifact, bigIntReplacer, 2));
759
+ else {
760
+ console.log(formatTxPlanArtifact(artifact));
761
+ if (options.out) console.log(`
696
762
  Artifact saved to: ${options.out}`);
697
- }
763
+ }
764
+ });
698
765
  } catch (e) {
699
- handleError(e);
766
+ handleLockError2(e);
700
767
  process.exitCode = 1;
701
768
  }
702
769
  });
703
- tx.command("sign <planPath>").description("Sign a transaction plan artifact").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) => {
770
+ 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("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (planPath, options) => {
771
+ const { withLock: withLock2 } = await import("@hardkas/core");
772
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
704
773
  try {
705
- const { readTxPlanArtifact, writeArtifact: writeArtifact4, formatSignedTxArtifact } = await import("@hardkas/artifacts");
706
- const { loadHardkasConfig: loadHardkasConfig3 } = await import("@hardkas/config");
707
- const planArtifact = await readTxPlanArtifact(planPath);
708
- const loaded = await loadHardkasConfig3();
709
- const signedArtifact = await runTxSign({
710
- planArtifact,
711
- ...options.account ? { accountName: options.account } : {},
712
- config: loaded.config,
713
- allowMainnetSigning: options.allowMainnetSigning
714
- });
715
- if (options.out) await writeArtifact4(options.out, signedArtifact);
716
- if (options.json) console.log(JSON.stringify(signedArtifact, bigIntReplacer, 2));
717
- else {
718
- console.log(formatSignedTxArtifact(signedArtifact));
719
- if (options.out) console.log(`
774
+ await withLock2({
775
+ rootDir: process.cwd(),
776
+ name: "artifacts",
777
+ command: "hardkas tx sign",
778
+ wait: options.waitLock,
779
+ timeoutMs: parseInt(options.lockTimeout)
780
+ }, async () => {
781
+ const { readTxPlanArtifact, writeArtifact: writeArtifact4, formatSignedTxArtifact } = await import("@hardkas/artifacts");
782
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
783
+ const planArtifact = await readTxPlanArtifact(planPath);
784
+ const loaded = await loadHardkasConfig15();
785
+ const signedArtifact = await runTxSign({
786
+ planArtifact,
787
+ ...options.account ? { accountName: options.account } : {},
788
+ config: loaded.config,
789
+ allowMainnetSigning: options.allowMainnetSigning
790
+ });
791
+ if (options.out) await writeArtifact4(options.out, signedArtifact);
792
+ if (options.json) console.log(JSON.stringify(signedArtifact, bigIntReplacer, 2));
793
+ else {
794
+ console.log(formatSignedTxArtifact(signedArtifact));
795
+ if (options.out) console.log(`
720
796
  Signed artifact saved to: ${options.out}`);
721
- }
797
+ }
798
+ });
722
799
  } catch (e) {
723
- handleError(e);
800
+ handleLockError2(e);
724
801
  process.exitCode = 1;
725
802
  }
726
803
  });
727
- tx.command("send [signedPath]").description("Broadcast a signed transaction or send directly (simulated)").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) => {
804
+ 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("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).option("--track <label>", "Auto-track deployment with this label").action(async (signedPath, options) => {
805
+ const { withLock: withLock2 } = await import("@hardkas/core");
806
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
728
807
  try {
729
- const { loadHardkasConfig: loadHardkasConfig3 } = await import("@hardkas/config");
730
- const loaded = await loadHardkasConfig3();
731
- if (signedPath) {
732
- const { readSignedTxArtifact } = await import("@hardkas/artifacts");
733
- const signedArtifact = await readSignedTxArtifact(signedPath);
734
- if (!options.yes && signedArtifact.networkId !== "simnet") {
735
- console.log(`Transaction is for network: ${signedArtifact.networkId}`);
736
- console.log("Run with --yes to broadcast.");
737
- return;
808
+ await withLock2({
809
+ rootDir: process.cwd(),
810
+ name: "artifacts",
811
+ command: "hardkas tx send",
812
+ wait: options.waitLock,
813
+ timeoutMs: parseInt(options.lockTimeout)
814
+ }, async () => {
815
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
816
+ const loaded = await loadHardkasConfig15();
817
+ if (signedPath) {
818
+ const { readSignedTxArtifact } = await import("@hardkas/artifacts");
819
+ const signedArtifact = await readSignedTxArtifact(signedPath);
820
+ if (!options.yes && signedArtifact.networkId !== "simnet") {
821
+ console.log(`Transaction is for network: ${signedArtifact.networkId}`);
822
+ console.log("Run with --yes to broadcast.");
823
+ return;
824
+ }
825
+ const result = await runTxSend({
826
+ signedArtifact,
827
+ network: options.network,
828
+ config: loaded.config,
829
+ ...options.url ? { url: options.url } : {}
830
+ });
831
+ if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
832
+ else console.log(result.formatted);
833
+ if (options.track && result.accepted) {
834
+ const { trackDeployment: trackDeployment2 } = await import("./deployment-runners-GEICABEH.js");
835
+ await trackDeployment2({
836
+ label: options.track,
837
+ network: result.networkName,
838
+ txId: result.txId,
839
+ plan: signedArtifact.sourcePlanId,
840
+ status: result.receipt.status === "confirmed" ? "confirmed" : "sent"
841
+ });
842
+ }
843
+ } else if (options.from && options.to && options.amount) {
844
+ const result = await runTxFlow({
845
+ ...options,
846
+ amount: options.amount,
847
+ from: options.from,
848
+ to: options.to,
849
+ send: true,
850
+ feeRate: "1",
851
+ // Default fee rate for shortcut
852
+ config: loaded.config,
853
+ ...options.url ? { url: options.url } : {}
854
+ });
855
+ if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
856
+ else console.log(result.steps.send.artifact?.formatted || "Flow completed");
857
+ } else {
858
+ console.error("Provide a path to a signed artifact or use --from, --to, --amount.");
859
+ process.exitCode = 1;
738
860
  }
739
- const result = await runTxSend({
740
- signedArtifact,
741
- network: options.network,
742
- config: loaded.config,
743
- ...options.url ? { url: options.url } : {}
744
- });
745
- if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
746
- else console.log(result.formatted);
747
- } else if (options.from && options.to && options.amount) {
748
- const result = await runTxFlow({
749
- ...options,
750
- amount: options.amount,
751
- from: options.from,
752
- to: options.to,
753
- send: true,
754
- feeRate: "1",
755
- // Default fee rate for shortcut
756
- config: loaded.config,
757
- ...options.url ? { url: options.url } : {}
758
- });
759
- if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
760
- else console.log(result.steps.send.artifact?.formatted || "Flow completed");
761
- } else {
762
- console.error("Provide a path to a signed artifact or use --from, --to, --amount.");
763
- process.exitCode = 1;
764
- }
861
+ });
765
862
  } catch (e) {
766
- handleError(e);
863
+ handleLockError2(e);
767
864
  process.exitCode = 1;
768
865
  }
769
866
  });
770
- tx.command("receipt <txId>").description("Show transaction receipt").option("--json", "Output as JSON", false).action(async (txId, options) => {
867
+ tx.command("receipt <txId>").description(`Show transaction receipt ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (txId, options) => {
771
868
  try {
772
869
  const result = await runTxReceipt({ txId });
773
870
  if (options.json) console.log(JSON.stringify(result.receipt, bigIntReplacer, 2));
@@ -777,19 +874,19 @@ Signed artifact saved to: ${options.out}`);
777
874
  process.exitCode = 1;
778
875
  }
779
876
  });
780
- tx.command("verify <path>").description("Perform deep semantic verification of a transaction plan").option("--json", "Output as JSON", false).action(async (path11, options) => {
781
- const { runTxVerify } = await import("./tx-verify-runner-GPPVBQIF.js");
782
- await runTxVerify({ path: path11, ...options });
877
+ tx.command("verify <path>").description(`Perform deep semantic verification of a transaction plan ${UI.maturity("preview")}`).option("--json", "Output as JSON", false).action(async (path15, options) => {
878
+ const { runTxVerify } = await import("./tx-verify-runner-6EGY5ZN4.js");
879
+ await runTxVerify({ path: path15, ...options });
783
880
  });
784
- tx.command("trace <txId>").description("Reconstruct the full operational trace of a transaction").action(async (txId) => {
785
- const { UI: UI2 } = await import("./ui-DXULTF7Q.js");
881
+ tx.command("trace <txId>").description(`Reconstruct the full operational trace of a transaction ${UI.maturity("research")}`).action(async (txId) => {
882
+ const { UI: UI2 } = await import("./ui-SUYOHGGP.js");
786
883
  UI2.error("Tracing is temporarily disabled while the query API stabilizes.");
787
884
  process.exitCode = 1;
788
885
  });
789
886
  }
790
887
 
791
888
  // src/runners/artifact-verify-runner.ts
792
- import { verifyArtifactIntegrity, verifyArtifactSemantics } from "@hardkas/artifacts";
889
+ import { verifyArtifactIntegrity, verifyArtifactSemantics, verifyArtifactReplay } from "@hardkas/artifacts";
793
890
  import path4 from "path";
794
891
  import fs3 from "fs";
795
892
  async function runArtifactVerify(options) {
@@ -813,8 +910,11 @@ async function runArtifactVerify(options) {
813
910
  let result = await verifyArtifactIntegrity(absolutePath);
814
911
  const artifact = JSON.parse(fs3.readFileSync(absolutePath, "utf-8"));
815
912
  const semanticResult = verifyArtifactSemantics(artifact, { strict: options.strict ?? false });
913
+ const replayResult = await verifyArtifactReplay(artifact, { strict: options.strict ?? false });
816
914
  result.issues.push(...semanticResult.issues);
915
+ result.issues.push(...replayResult.issues);
817
916
  result.errors.push(...semanticResult.errors);
917
+ result.errors.push(...replayResult.errors);
818
918
  result.ok = result.ok && semanticResult.ok;
819
919
  if (options.json) {
820
920
  console.log(JSON.stringify(result, null, 2));
@@ -829,11 +929,22 @@ async function runArtifactVerify(options) {
829
929
  if (options.strict) {
830
930
  console.log(`
831
931
  Operational Audit (STRICT):`);
832
- const feeAudit = verifyArtifactSemantics(artifact, { strict: true });
833
- if (feeAudit.ok) {
834
- UI.success(" \u2713 Economic invariants verified.");
932
+ if (semanticResult.ok) {
933
+ UI.success(" \u2713 Economic & Lineage invariants verified.");
934
+ } else {
935
+ UI.error(" \u2717 Semantic invariants VIOLATED.");
936
+ }
937
+ }
938
+ console.log(`
939
+ Replay Verification:`);
940
+ if (replayResult.ok) {
941
+ UI.success(" \u2713 Replay verified.");
942
+ } else {
943
+ const replayIssue = replayResult.issues.find((i) => i.code === "REPLAY_UNSUPPORTED_CHECK");
944
+ if (replayIssue) {
945
+ UI.warning(" \u26A0 REPLAY UNSUPPORTED (Consensus simulation skipped)");
835
946
  } else {
836
- UI.error(" \u2717 Economic invariants VIOLATED.");
947
+ UI.error(" \u2717 Replay verification FAILED.");
837
948
  }
838
949
  }
839
950
  } else {
@@ -970,7 +1081,8 @@ async function runArtifactExplain(options) {
970
1081
  if (explanation.security.strictOk) {
971
1082
  UI.success(" \u2713 No critical integrity violations detected.");
972
1083
  } else {
973
- UI.error(" \u2717 SECUIRTY WARNINGS DETECTED.");
1084
+ UI.error(" \u2717 SECURITY WARNINGS DETECTED.");
1085
+ process.exitCode = 1;
974
1086
  }
975
1087
  if (explanation.security.issues.length > 0) {
976
1088
  explanation.security.issues.forEach((issue) => {
@@ -984,26 +1096,26 @@ async function runArtifactExplain(options) {
984
1096
  // src/commands/artifact.ts
985
1097
  function registerArtifactCommands(program) {
986
1098
  const artifactCmd = program.command("artifact").description("Manage HardKAS artifacts");
987
- artifactCmd.command("verify <path>").description("Verify an artifact's integrity and schema").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 (path11, options) => {
1099
+ 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 (path15, options) => {
988
1100
  try {
989
- await runArtifactVerify({ path: path11, ...options });
1101
+ await runArtifactVerify({ path: path15, ...options });
990
1102
  } catch (e) {
991
1103
  handleError(e);
992
1104
  process.exitCode = 1;
993
1105
  }
994
1106
  });
995
- artifactCmd.command("explain <path>").description("Provide a human-readable operational summary of an artifact").action(async (path11) => {
1107
+ artifactCmd.command("explain <path>").description(`Provide a human-readable operational summary of an artifact ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (path15, options) => {
996
1108
  try {
997
- await runArtifactExplain({ path: path11 });
1109
+ await runArtifactExplain({ path: path15, ...options });
998
1110
  } catch (e) {
999
1111
  handleError(e);
1000
1112
  process.exitCode = 1;
1001
1113
  }
1002
1114
  });
1003
- artifactCmd.command("lineage <path>").description("Show the provenance and operational history of an artifact").action(async (path11) => {
1115
+ artifactCmd.command("lineage <path>").description(`Show the provenance and operational history of an artifact ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (path15, options) => {
1004
1116
  try {
1005
- const { runArtifactLineage } = await import("./artifact-lineage-runner-EPT6ABS2.js");
1006
- await runArtifactLineage({ path: path11 });
1117
+ const { runArtifactLineage } = await import("./artifact-lineage-runner-TY4YTE6H.js");
1118
+ await runArtifactLineage({ path: path15 });
1007
1119
  } catch (e) {
1008
1120
  handleError(e);
1009
1121
  process.exitCode = 1;
@@ -1014,22 +1126,22 @@ function registerArtifactCommands(program) {
1014
1126
  // src/commands/replay.ts
1015
1127
  function registerReplayCommands(program) {
1016
1128
  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 (path11) => {
1018
- const { runReplayVerify } = await import("./replay-verify-runner-WBK2FCWC.js");
1019
- await runReplayVerify({ path: path11 });
1129
+ replayCmd.command("verify <path>").description(`Verify replay invariants for a directory of artifacts ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (path15, options) => {
1130
+ const { runReplayVerify } = await import("./replay-verify-runner-Y3RARVD7.js");
1131
+ await runReplayVerify({ path: path15, ...options });
1020
1132
  });
1021
1133
  }
1022
1134
 
1023
1135
  // src/commands/snapshot.ts
1024
1136
  function registerSnapshotCommands(program) {
1025
1137
  const snapshotCmd = program.command("snapshot").description("Manage HardKAS localnet snapshots");
1026
- snapshotCmd.command("verify <idOrName>").description("Verify the integrity of a snapshot").action(async (idOrName) => {
1027
- const { runSnapshotVerify } = await import("./snapshot-verify-runner-UYTXXQ7A.js");
1028
- await runSnapshotVerify({ idOrName });
1138
+ snapshotCmd.command("verify <idOrName>").description(`Verify the integrity of a snapshot ${UI.maturity("preview")}`).option("--json", "Output as JSON", false).action(async (idOrName, options) => {
1139
+ const { runSnapshotVerify } = await import("./snapshot-verify-runner-PGMADWLN.js");
1140
+ await runSnapshotVerify({ idOrName, ...options });
1029
1141
  });
1030
- snapshotCmd.command("restore <idOrName>").description("Restore localnet state from a snapshot").action(async (idOrName) => {
1031
- const { runSnapshotRestore } = await import("./snapshot-restore-runner-P26HDE74.js");
1032
- await runSnapshotRestore({ idOrName });
1142
+ snapshotCmd.command("restore <idOrName>").description(`Restore localnet state from a snapshot ${UI.maturity("preview")}`).option("--json", "Output as JSON", false).action(async (idOrName, options) => {
1143
+ const { runSnapshotRestore } = await import("./snapshot-restore-runner-KIJNPLDV.js");
1144
+ await runSnapshotRestore({ idOrName, ...options });
1033
1145
  });
1034
1146
  }
1035
1147
 
@@ -1215,7 +1327,7 @@ function registerRpcCommands(program) {
1215
1327
  }
1216
1328
  });
1217
1329
  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-RKGKFGMM.js");
1330
+ const { runRpcDoctor } = await import("./rpc-doctor-runner-V7H7AAX4.js");
1219
1331
  try {
1220
1332
  await runRpcDoctor(options);
1221
1333
  } catch (e) {
@@ -1250,7 +1362,7 @@ function registerDagCommands(program) {
1250
1362
  const dagCmd = program.command("dag").description("Simulate blockDAG operations (Localnet only)");
1251
1363
  dagCmd.command("status").description("View current DAG status").action(async () => {
1252
1364
  try {
1253
- const { runDagStatus } = await import("./dag-runners-BQAKJ6DM.js");
1365
+ const { runDagStatus } = await import("./dag-runners-NLBYR7I7.js");
1254
1366
  await runDagStatus();
1255
1367
  } catch (e) {
1256
1368
  handleError(e);
@@ -1259,7 +1371,7 @@ function registerDagCommands(program) {
1259
1371
  });
1260
1372
  dagCmd.command("simulate-reorg").description("Simulate a DAG reorg").option("--depth <n>", "Reorg depth", "1").action(async (options) => {
1261
1373
  try {
1262
- const { runDagSimulateReorg } = await import("./dag-runners-BQAKJ6DM.js");
1374
+ const { runDagSimulateReorg } = await import("./dag-runners-NLBYR7I7.js");
1263
1375
  await runDagSimulateReorg({ depth: parseInt(options.depth) });
1264
1376
  } catch (e) {
1265
1377
  handleError(e);
@@ -1303,21 +1415,58 @@ import {
1303
1415
  loadOrCreateRealAccountStore,
1304
1416
  saveRealAccountStore as saveRealAccountStore2,
1305
1417
  importRealDevAccount,
1306
- KaspaSdkKeyGenerator
1418
+ KaspaSdkKeyGenerator,
1419
+ KeystoreManager
1307
1420
  } from "@hardkas/accounts";
1421
+ import path6 from "path";
1308
1422
  async function runAccountsRealGenerate(options = {}) {
1309
1423
  const generator = new KaspaSdkKeyGenerator(options.networkId ? { networkId: options.networkId } : {});
1310
1424
  const count = options.count || 1;
1311
1425
  let store = await loadOrCreateRealAccountStore();
1312
1426
  const generatedAccounts = [];
1427
+ let password = "";
1428
+ if (!options.unsafePlaintext) {
1429
+ password = await acquirePassword({
1430
+ stdin: options.passwordStdin,
1431
+ env: options.passwordEnv,
1432
+ message: `Enter password to encrypt ${count} new account(s):`
1433
+ });
1434
+ if (!password) throw new Error("Password is required for encrypted storage.");
1435
+ } else {
1436
+ UI.warning("LEGACY MODE: Generating accounts in plaintext is unsafe.");
1437
+ if (!options.yes) {
1438
+ const confirmed = await UI.confirm("Are you sure you want to store these keys in plaintext?");
1439
+ if (!confirmed) throw new Error("Generation cancelled.");
1440
+ }
1441
+ }
1313
1442
  for (let i = 0; i < count; i++) {
1314
1443
  const name = count === 1 && options.name ? options.name : options.name ? `${options.name}${i + 1}` : `account${i}`;
1315
1444
  const generated = await generator.generateAccount(options.networkId ? { networkId: options.networkId } : {});
1445
+ let keystoreRef;
1446
+ if (!options.unsafePlaintext) {
1447
+ const keystore = await KeystoreManager.createEncryptedKeystore(
1448
+ {
1449
+ address: generated.address,
1450
+ privateKey: generated.privateKey,
1451
+ network: options.networkId || "simnet"
1452
+ },
1453
+ password,
1454
+ {
1455
+ label: name,
1456
+ network: options.networkId || "simnet"
1457
+ }
1458
+ );
1459
+ const keystoreDir = path6.join(process.cwd(), ".hardkas", "keystore");
1460
+ const filePath = path6.join(keystoreDir, `${name}.json`);
1461
+ await KeystoreManager.saveEncryptedKeystore(filePath, keystore);
1462
+ keystoreRef = `.hardkas/keystore/${name}.json`;
1463
+ }
1316
1464
  store = importRealDevAccount(store, {
1317
1465
  name,
1318
1466
  address: generated.address,
1319
1467
  ...generated.publicKey ? { publicKey: generated.publicKey } : {},
1320
- ...generated.privateKey ? { privateKey: generated.privateKey } : {}
1468
+ ...options.unsafePlaintext && generated.privateKey ? { privateKey: generated.privateKey } : {},
1469
+ ...keystoreRef ? { keystoreRef } : {}
1321
1470
  });
1322
1471
  generatedAccounts.push(store.accounts[store.accounts.length - 1]);
1323
1472
  }
@@ -1340,14 +1489,101 @@ async function runAccountsRealGenerate(options = {}) {
1340
1489
  };
1341
1490
  }
1342
1491
 
1492
+ // src/runners/accounts-balance-runner.ts
1493
+ import {
1494
+ loadHardkasConfig as loadHardkasConfig2
1495
+ } from "@hardkas/config";
1496
+ import {
1497
+ loadRealAccountStore as loadRealAccountStore2,
1498
+ getRealDevAccount
1499
+ } from "@hardkas/accounts";
1500
+ import { JsonWrpcKaspaClient as JsonWrpcKaspaClient3 } from "@hardkas/kaspa-rpc";
1501
+ import { resolveRuntimeConfig } from "@hardkas/node-orchestrator";
1502
+ async function runAccountsBalance(options) {
1503
+ let address = options.identifier;
1504
+ let name = "Unknown";
1505
+ const loadedConfig = await loadHardkasConfig2({});
1506
+ const projectAccount = loadedConfig.config.accounts?.[options.identifier];
1507
+ if (projectAccount) {
1508
+ address = projectAccount.address ?? "";
1509
+ name = options.identifier;
1510
+ } else {
1511
+ const store = await loadRealAccountStore2();
1512
+ const realAccount = store ? getRealDevAccount(store, options.identifier) : null;
1513
+ if (realAccount) {
1514
+ address = realAccount.address ?? "";
1515
+ name = realAccount.name;
1516
+ }
1517
+ }
1518
+ const network = options.network ?? loadedConfig.config.defaultNetwork ?? "simnet";
1519
+ let rpcUrl = options.url;
1520
+ if (!rpcUrl) {
1521
+ rpcUrl = resolveRuntimeConfig({ network }).rpcUrl;
1522
+ }
1523
+ const client = new JsonWrpcKaspaClient3({ rpcUrl });
1524
+ try {
1525
+ const balance = await client.getBalanceByAddress(address);
1526
+ const utxos = await client.getUtxosByAddress(address);
1527
+ return {
1528
+ name,
1529
+ address,
1530
+ balanceSompi: balance.balanceSompi,
1531
+ utxoCount: utxos.length,
1532
+ network
1533
+ };
1534
+ } finally {
1535
+ await client.close();
1536
+ }
1537
+ }
1538
+
1539
+ // src/runners/accounts-fund-runner.ts
1540
+ import { loadHardkasConfig as loadHardkasConfig3 } from "@hardkas/config";
1541
+ import { resolveHardkasAccountAddress as resolveHardkasAccountAddress2 } from "@hardkas/accounts";
1542
+ import {
1543
+ loadOrCreateLocalnetState as loadOrCreateLocalnetState2,
1544
+ saveLocalnetState as saveLocalnetState2,
1545
+ fundAddress
1546
+ } from "@hardkas/localnet";
1547
+ async function runAccountsFund(options) {
1548
+ const loadedConfig = await loadHardkasConfig3({});
1549
+ const address = resolveHardkasAccountAddress2(options.identifier, loadedConfig.config);
1550
+ const networkId = loadedConfig.config.defaultNetwork || "simnet";
1551
+ const networkConfig = loadedConfig.config.networks?.[networkId];
1552
+ const isSimulated = networkId === "simulated" || networkId === "localnet" || networkConfig?.kind === "simulated";
1553
+ const allowedNetworks = ["simnet", "localnet", "dev", "simulated"];
1554
+ if (!allowedNetworks.includes(networkId) && !isSimulated) {
1555
+ throw new Error(`Faucet/Funding is only allowed on development networks (${allowedNetworks.join(", ")}). Current network is: ${networkId}`);
1556
+ }
1557
+ if (isSimulated) {
1558
+ const state = await loadOrCreateLocalnetState2();
1559
+ const amount = options.amountSompi || 1000n * 100000000n;
1560
+ const newState = fundAddress(state, { address, amountSompi: amount });
1561
+ await saveLocalnetState2(newState);
1562
+ return {
1563
+ success: true,
1564
+ address,
1565
+ amountSompi: amount,
1566
+ mode: "simulated",
1567
+ formatted: `Successfully funded ${options.identifier} (${address}) with ${Number(amount) / 1e8} KAS (Simulated)`
1568
+ };
1569
+ }
1570
+ if (networkId === "simnet" || networkId === "dev") {
1571
+ throw new Error(
1572
+ `Funding for real simnet (Docker) via faucet requires a miner account.
1573
+ Hint: Start your node with 'hardkas node start --miningaddr ${address}' to mine coins directly to this account.`
1574
+ );
1575
+ }
1576
+ throw new Error(`Unsupported network for funding: ${networkId}`);
1577
+ }
1578
+
1343
1579
  // src/commands/accounts.ts
1344
1580
  function registerAccountsCommands(program) {
1345
1581
  const accountsCmd = program.command("accounts").description("Manage HardKAS accounts");
1346
- 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: loadHardkasConfig3 } = await import("@hardkas/config");
1582
+ accountsCmd.command("list").description(`List available HardKAS accounts ${UI.maturity("stable")}`).option("--config <path>", "Path to config file").option("--json", "Output as JSON", false).action(async (options) => {
1583
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
1348
1584
  const { listHardkasAccounts: listHardkasAccounts2, describeAccount } = await import("@hardkas/accounts");
1349
1585
  try {
1350
- const loaded = await loadHardkasConfig3(options.config ? { configPath: options.config } : {});
1586
+ const loaded = await loadHardkasConfig15(options.config ? { configPath: options.config } : {});
1351
1587
  const accounts = listHardkasAccounts2(loaded.config);
1352
1588
  if (options.json) {
1353
1589
  console.log(JSON.stringify(accounts.map((a) => describeAccount(a)), null, 2));
@@ -1365,62 +1601,139 @@ function registerAccountsCommands(program) {
1365
1601
  }
1366
1602
  });
1367
1603
  const realAccountsCmd = accountsCmd.command("real").description("Persistent dev account store (L1)");
1368
- realAccountsCmd.command("init").description("Initialize real dev account store").option("--force", "Overwrite existing store", false).option("--json", "Output as JSON", false).action(async (options) => {
1604
+ realAccountsCmd.command("init").description(`Initialize real dev account store ${UI.maturity("stable")}`).option("--force", "Overwrite existing store", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
1605
+ const { withLock: withLock2 } = await import("@hardkas/core");
1606
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
1369
1607
  try {
1370
- const result = await runAccountsRealInit({ force: options.force });
1371
- if (options.json) console.log(JSON.stringify(result, null, 2));
1372
- else console.log(result.formatted);
1608
+ await withLock2({
1609
+ rootDir: process.cwd(),
1610
+ name: "accounts",
1611
+ command: "hardkas accounts real init",
1612
+ wait: options.waitLock,
1613
+ timeoutMs: parseInt(options.lockTimeout)
1614
+ }, async () => {
1615
+ const result = await runAccountsRealInit({ force: options.force });
1616
+ if (options.json) console.log(JSON.stringify(result, null, 2));
1617
+ else console.log(result.formatted);
1618
+ });
1373
1619
  } catch (e) {
1374
- handleError(e);
1620
+ handleLockError2(e);
1375
1621
  process.exitCode = 1;
1376
1622
  }
1377
1623
  });
1378
- 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) => {
1624
+ realAccountsCmd.command("import").description(`Import an account into the persistent store ${UI.maturity("stable")}`).option("--name <name>", "Account name").option("--address <address>", "Kaspa address").option("--private-key <hex>", "Deprecated. Unsafe: may leak through shell history. Prefer --private-key-stdin or --private-key-env.").option("--private-key-stdin", "Read private key from stdin", false).option("--private-key-env <env>", "Read private key from environment variable").option("--password-stdin", "Read keystore password from stdin (safe)", false).option("--password-env <env>", "Read keystore password from environment variable (safe)").option("--unsafe-plaintext", "Store private key in plaintext (legacy/discouraged)", false).option("--yes", "Skip confirmation for unsafe operations", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
1625
+ const { withLock: withLock2 } = await import("@hardkas/core");
1626
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
1379
1627
  try {
1380
- const { runAccountsKeystoreImport } = await import("./accounts-keystore-runners-CVRE6NVM.js");
1381
- const result = await runAccountsKeystoreImport(options);
1382
- if (options.json) console.log(JSON.stringify(result, null, 2));
1383
- else console.log(result.formatted);
1628
+ await withLock2({
1629
+ rootDir: process.cwd(),
1630
+ name: "accounts",
1631
+ command: "hardkas accounts real import",
1632
+ wait: options.waitLock,
1633
+ timeoutMs: parseInt(options.lockTimeout)
1634
+ }, async () => {
1635
+ const { runAccountsKeystoreImport } = await import("./accounts-keystore-runners-QYSNZMZV.js");
1636
+ const result = await runAccountsKeystoreImport(options);
1637
+ if (options.json) console.log(JSON.stringify(result, null, 2));
1638
+ else console.log(result.formatted);
1639
+ });
1384
1640
  } catch (e) {
1385
- handleError(e);
1641
+ handleLockError2(e);
1386
1642
  process.exitCode = 1;
1387
1643
  }
1388
1644
  });
1389
- realAccountsCmd.command("unlock <name>").description("Verify password for an encrypted account").action(async (name) => {
1645
+ realAccountsCmd.command("session-open <name>").alias("unlock").description(`Verify keystore access and record signing intent ${UI.maturity("internal")}`).option("--password-stdin", "Read password from stdin", false).option("--password-env <env>", "Read password from environment variable").option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (name, options) => {
1646
+ const { withLock: withLock2 } = await import("@hardkas/core");
1647
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
1390
1648
  try {
1391
- const { runAccountsKeystoreUnlock } = await import("./accounts-keystore-runners-CVRE6NVM.js");
1392
- await runAccountsKeystoreUnlock({ name });
1649
+ await withLock2({
1650
+ rootDir: process.cwd(),
1651
+ name: "accounts",
1652
+ command: `hardkas accounts real session-open ${name}`,
1653
+ wait: options.waitLock,
1654
+ timeoutMs: parseInt(options.lockTimeout)
1655
+ }, async () => {
1656
+ const { runAccountsSessionOpen } = await import("./accounts-keystore-runners-QYSNZMZV.js");
1657
+ await runAccountsSessionOpen({ name, ...options });
1658
+ });
1393
1659
  } catch (e) {
1394
- handleError(e);
1660
+ handleLockError2(e);
1395
1661
  process.exitCode = 1;
1396
1662
  }
1397
1663
  });
1398
- realAccountsCmd.command("lock <name>").description("Lock an account (clear session)").action(async (name) => {
1664
+ realAccountsCmd.command("session-close <name>").alias("lock").description(`Clear the local dev signing session marker ${UI.maturity("internal")}`).action(async (name) => {
1665
+ console.log(`
1666
+ \u2139 Account '${name}' session clear (redundant).`);
1667
+ console.log(` The CLI is already stateless. No secrets are stored in memory between commands.
1668
+ `);
1669
+ });
1670
+ realAccountsCmd.command("change-password <name>").description(`Change password for an encrypted account ${UI.maturity("stable")}`).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (name, options) => {
1671
+ const { withLock: withLock2 } = await import("@hardkas/core");
1672
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
1399
1673
  try {
1400
- console.log(`Account '${name}' is now locked. (Session cleared)`);
1674
+ await withLock2({
1675
+ rootDir: process.cwd(),
1676
+ name: "accounts",
1677
+ command: `hardkas accounts real change-password ${name}`,
1678
+ wait: options.waitLock,
1679
+ timeoutMs: parseInt(options.lockTimeout)
1680
+ }, async () => {
1681
+ const { runAccountsKeystoreChangePassword } = await import("./accounts-keystore-runners-QYSNZMZV.js");
1682
+ await runAccountsKeystoreChangePassword({ name });
1683
+ });
1401
1684
  } catch (e) {
1402
- handleError(e);
1685
+ handleLockError2(e);
1686
+ process.exitCode = 1;
1687
+ }
1688
+ });
1689
+ realAccountsCmd.command("generate").description(`Generate new real dev account(s) using Kaspa SDK ${UI.maturity("stable")}`).option("--name <name>", "Base name for account(s)").option("--count <number>", "Number of accounts to generate", "1").option("--network <network>", "Kaspa network (simnet, testnet-10, mainnet)", "simnet").option("--password-stdin", "Read keystore password from stdin", false).option("--password-env <env>", "Read keystore password from environment variable").option("--unsafe-plaintext", "Generate accounts in plaintext (legacy/discouraged)", false).option("--yes", "Skip confirmation for unsafe operations", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
1690
+ const { withLock: withLock2 } = await import("@hardkas/core");
1691
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
1692
+ try {
1693
+ await withLock2({
1694
+ rootDir: process.cwd(),
1695
+ name: "accounts",
1696
+ command: "hardkas accounts real generate",
1697
+ wait: options.waitLock,
1698
+ timeoutMs: parseInt(options.lockTimeout)
1699
+ }, async () => {
1700
+ const result = await runAccountsRealGenerate({
1701
+ ...options.name ? { name: options.name } : {},
1702
+ count: parseInt(options.count, 10),
1703
+ networkId: options.network,
1704
+ ...options
1705
+ });
1706
+ if (options.json) console.log(JSON.stringify(result.accounts, null, 2));
1707
+ else console.log(result.formatted);
1708
+ });
1709
+ } catch (e) {
1710
+ handleLockError2(e);
1403
1711
  process.exitCode = 1;
1404
1712
  }
1405
1713
  });
1406
- realAccountsCmd.command("change-password <name>").description("Change password for an encrypted account").action(async (name) => {
1714
+ accountsCmd.command("balance <identifier>").description(`Show account balance ${UI.maturity("stable")}`).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) => {
1407
1715
  try {
1408
- const { runAccountsKeystoreChangePassword } = await import("./accounts-keystore-runners-CVRE6NVM.js");
1409
- await runAccountsKeystoreChangePassword({ name });
1716
+ const result = await runAccountsBalance({ identifier, network: options.network ?? "simnet", url: options.url ?? "" });
1717
+ if (options.json) {
1718
+ console.log(JSON.stringify(result, null, 2));
1719
+ } else {
1720
+ console.log(`
1721
+ Account: ${result.name}`);
1722
+ console.log(`Address: ${result.address}`);
1723
+ console.log(`Balance: ${Number(result.balanceSompi) / 1e8} KAS`);
1724
+ console.log(`UTXOs: ${result.utxoCount}`);
1725
+ console.log(`Network: ${result.network}`);
1726
+ }
1410
1727
  } catch (e) {
1411
1728
  handleError(e);
1412
1729
  process.exitCode = 1;
1413
1730
  }
1414
1731
  });
1415
- realAccountsCmd.command("generate").description("Generate new real dev account(s) using Kaspa SDK").option("--name <name>", "Base name for account(s)").option("--count <number>", "Number of accounts to generate", "1").option("--network <network>", "Kaspa network (simnet, testnet-10, mainnet)", "simnet").option("--json", "Output as JSON", false).action(async (options) => {
1732
+ accountsCmd.command("fund <identifier>").description("Fund an account (Faucet)").option("--amount <kas>", "Amount in KAS to fund", "1000").action(async (identifier, options) => {
1416
1733
  try {
1417
- const result = await runAccountsRealGenerate({
1418
- ...options.name ? { name: options.name } : {},
1419
- count: parseInt(options.count, 10),
1420
- networkId: options.network
1421
- });
1422
- if (options.json) console.log(JSON.stringify(result.accounts, null, 2));
1423
- else console.log(result.formatted);
1734
+ const amountSompi = BigInt(parseFloat(options.amount) * 1e8);
1735
+ const result = await runAccountsFund({ identifier, amountSompi });
1736
+ console.log(result.formatted);
1424
1737
  } catch (e) {
1425
1738
  handleError(e);
1426
1739
  process.exitCode = 1;
@@ -1430,8 +1743,10 @@ function registerAccountsCommands(program) {
1430
1743
 
1431
1744
  // src/runners/l2-networks-runner.ts
1432
1745
  import { listL2Profiles } from "@hardkas/l2";
1746
+ import { loadHardkasConfig as loadHardkasConfig4 } from "@hardkas/config";
1433
1747
  async function runL2Networks(options = {}) {
1434
- const profiles = listL2Profiles();
1748
+ const loaded = await loadHardkasConfig4();
1749
+ const profiles = listL2Profiles(loaded.config.l2?.networks);
1435
1750
  if (options.json) {
1436
1751
  console.log(JSON.stringify(profiles, null, 2));
1437
1752
  return;
@@ -1442,20 +1757,30 @@ async function runL2Networks(options = {}) {
1442
1757
  console.log("No L2 profiles found.");
1443
1758
  return;
1444
1759
  }
1760
+ console.log(`${"name".padEnd(16)} ${"source".padEnd(14)} ${"type".padEnd(20)} ${"bridge".padEnd(10)} ${"exit"}`);
1761
+ console.log("\u2500".repeat(70));
1445
1762
  for (const p of profiles) {
1446
1763
  const bridge = p.security.bridgePhase;
1447
1764
  const exit = p.security.trustlessExit ? "yes" : "no";
1448
- console.log(`${p.name.padEnd(8)} ${p.displayName.padEnd(8)} ${p.type.padEnd(18)} gas: ${p.gasToken.padEnd(8)} bridge: ${bridge.padEnd(8)} trustless exit: ${exit}`);
1765
+ const source = p.source;
1766
+ console.log(`${p.name.padEnd(16)} ${source.padEnd(14)} ${p.type.padEnd(20)} ${bridge.padEnd(10)} ${exit}`);
1449
1767
  }
1450
1768
  }
1451
1769
 
1452
1770
  // src/runners/l2-profile-show-runner.ts
1453
- import { getL2Profile } from "@hardkas/l2";
1771
+ import { resolveL2Profile } from "@hardkas/l2";
1772
+ import { loadHardkasConfig as loadHardkasConfig5 } from "@hardkas/config";
1454
1773
  async function runL2ProfileShow(options) {
1455
- const profile = getL2Profile(options.name);
1456
- if (!profile) {
1457
- throw new Error(`L2 profile '${options.name}' not found.`);
1458
- }
1774
+ const loaded = await loadHardkasConfig5();
1775
+ const name = options.name || options.network;
1776
+ const profile = resolveL2Profile({
1777
+ name,
1778
+ userProfiles: loaded.config.l2?.networks,
1779
+ cliOverrides: {
1780
+ ...options.url !== void 0 ? { url: options.url } : {},
1781
+ ...options.chainId !== void 0 ? { chainId: options.chainId } : {}
1782
+ }
1783
+ });
1459
1784
  if (options.json) {
1460
1785
  console.log(JSON.stringify(profile, null, 2));
1461
1786
  return;
@@ -1463,10 +1788,13 @@ async function runL2ProfileShow(options) {
1463
1788
  console.log("L2 profile");
1464
1789
  console.log("");
1465
1790
  console.log(`Name: ${profile.name}`);
1791
+ console.log(`Source: ${profile.source}`);
1466
1792
  console.log(`Display: ${profile.displayName}`);
1467
1793
  console.log(`Type: ${profile.type}`);
1468
1794
  console.log(`Settlement: ${profile.settlementLayer === "kaspa" ? "Kaspa L1" : profile.settlementLayer}`);
1469
1795
  console.log(`Execution: ${profile.executionLayer === "evm" ? "EVM L2" : profile.executionLayer}`);
1796
+ console.log(`Chain ID: ${profile.chainId || "unknown"}`);
1797
+ console.log(`RPC URL: ${profile.rpcUrl || "unknown"}`);
1470
1798
  console.log(`Gas token: ${profile.gasToken}`);
1471
1799
  console.log(`Bridge: ${profile.security.bridgePhase}`);
1472
1800
  console.log(`Trustless exit: ${profile.security.trustlessExit ? "yes" : "no"}`);
@@ -1481,22 +1809,48 @@ async function runL2ProfileShow(options) {
1481
1809
  }
1482
1810
 
1483
1811
  // src/runners/l2-profile-validate-runner.ts
1484
- import { getL2Profile as getL2Profile2, validateL2Profile } from "@hardkas/l2";
1812
+ import { resolveL2Profile as resolveL2Profile2, validateL2Profile, EvmJsonRpcClient } from "@hardkas/l2";
1813
+ import { loadHardkasConfig as loadHardkasConfig6 } from "@hardkas/config";
1485
1814
  async function runL2ProfileValidate(options) {
1486
- const profile = getL2Profile2(options.name);
1487
- if (!profile) {
1488
- throw new Error(`L2 profile '${options.name}' not found.`);
1489
- }
1815
+ const loaded = await loadHardkasConfig6();
1816
+ const name = options.name || options.network;
1817
+ const profile = resolveL2Profile2({
1818
+ name,
1819
+ userProfiles: loaded.config.l2?.networks,
1820
+ cliOverrides: {
1821
+ ...options.url !== void 0 ? { url: options.url } : {}
1822
+ }
1823
+ });
1490
1824
  const result = validateL2Profile(profile);
1825
+ const errors = [...result.errors];
1826
+ let rpcVerified = false;
1827
+ if (profile.rpcUrl) {
1828
+ try {
1829
+ const client = new EvmJsonRpcClient({ url: profile.rpcUrl });
1830
+ const remoteChainId = await client.getChainId();
1831
+ if (profile.chainId !== void 0 && profile.chainId !== remoteChainId) {
1832
+ errors.push(`Chain ID mismatch: Profile configured for ${profile.chainId} but RPC returned ${remoteChainId}`);
1833
+ } else {
1834
+ rpcVerified = true;
1835
+ }
1836
+ } catch (e) {
1837
+ }
1838
+ }
1839
+ const finalOk = errors.length === 0;
1491
1840
  if (options.json) {
1492
- console.log(JSON.stringify(result, null, 2));
1841
+ console.log(JSON.stringify({ ok: finalOk, errors, profile, rpcVerified }, null, 2));
1493
1842
  return;
1494
1843
  }
1495
- if (result.ok) {
1496
- console.log(`L2 profile '${options.name}' is VALID.`);
1844
+ if (finalOk) {
1845
+ console.log(`\u2713 L2 profile '${profile.name}' (${profile.source}) is VALID.`);
1846
+ if (rpcVerified) {
1847
+ console.log(` RPC connectivity verified for chainId ${profile.chainId}`);
1848
+ } else if (profile.rpcUrl) {
1849
+ console.log(` Note: RPC URL '${profile.rpcUrl}' is configured but currently unreachable.`);
1850
+ }
1497
1851
  } else {
1498
- console.log(`L2 profile '${options.name}' is INVALID:`);
1499
- for (const err of result.errors) {
1852
+ console.log(`\u2717 L2 profile '${profile.name}' is INVALID:`);
1853
+ for (const err of errors) {
1500
1854
  console.log(` - ${err}`);
1501
1855
  }
1502
1856
  process.exit(1);
@@ -1505,13 +1859,14 @@ async function runL2ProfileValidate(options) {
1505
1859
 
1506
1860
  // src/runners/l2-tx-runners.ts
1507
1861
  import fs5 from "fs/promises";
1508
- import path6 from "path";
1862
+ import path7 from "path";
1509
1863
  import {
1510
- getL2Profile as getL2Profile3,
1511
- EvmJsonRpcClient,
1864
+ resolveL2Profile as resolveL2Profile3,
1865
+ EvmJsonRpcClient as EvmJsonRpcClient2,
1512
1866
  toHexQuantity,
1513
1867
  normalizeEvmTransactionReceipt
1514
1868
  } from "@hardkas/l2";
1869
+ import { loadHardkasConfig as loadHardkasConfig7 } from "@hardkas/config";
1515
1870
  import {
1516
1871
  assertValidIgraTxPlanArtifact,
1517
1872
  assertValidIgraSignedTxArtifact,
@@ -1523,23 +1878,28 @@ import {
1523
1878
  createIgraSignedId,
1524
1879
  assertValidIgraTxReceiptArtifact,
1525
1880
  listIgraTxReceiptArtifacts,
1526
- loadIgraTxReceiptArtifact
1881
+ loadIgraTxReceiptArtifact,
1882
+ calculateContentHash
1527
1883
  } from "@hardkas/artifacts";
1528
1884
  import {
1529
- loadRealAccountStore as loadRealAccountStore2,
1885
+ loadRealAccountStore as loadRealAccountStore3,
1530
1886
  resolveRealAccountOrAddress
1531
1887
  } from "@hardkas/accounts";
1532
1888
  async function runL2TxBuild(options) {
1533
- const networkName = options.network ?? "igra";
1534
- const profile = getL2Profile3(networkName);
1535
- if (!profile) {
1536
- throw new Error(`L2 profile '${networkName}' not found.`);
1537
- }
1538
- const rpcUrl = options.url ?? profile.rpcUrl;
1889
+ const loaded = await loadHardkasConfig7();
1890
+ const profile = resolveL2Profile3({
1891
+ name: options.network,
1892
+ userProfiles: loaded.config.l2?.networks,
1893
+ cliOverrides: {
1894
+ ...options.url !== void 0 ? { url: options.url } : {},
1895
+ ...options.chainId !== void 0 ? { chainId: options.chainId } : {}
1896
+ }
1897
+ });
1898
+ const rpcUrl = profile.rpcUrl;
1539
1899
  if (!rpcUrl) {
1540
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
1900
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
1541
1901
  }
1542
- const client = new EvmJsonRpcClient({ url: rpcUrl });
1902
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
1543
1903
  if (options.from) assertEvmAddress(options.from, "from");
1544
1904
  if (!options.to) {
1545
1905
  throw new Error("Missing 'to' address. For this phase, 'to' is required.");
@@ -1569,14 +1929,14 @@ async function runL2TxBuild(options) {
1569
1929
  gasLimit = g.toString();
1570
1930
  }
1571
1931
  const estimatedFeeWei = (BigInt(gasLimit) * BigInt(gasPrice)).toString();
1572
- const planId = createIgraPlanId();
1573
1932
  const artifact = {
1574
1933
  schema: ARTIFACT_SCHEMAS2.IGRA_TX_PLAN,
1575
1934
  hardkasVersion: HARDKAS_VERSION2,
1576
1935
  networkId: profile.name,
1577
1936
  mode: "l2-rpc",
1578
1937
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1579
- planId,
1938
+ planId: "",
1939
+ // Placeholder
1580
1940
  l2Network: profile.name,
1581
1941
  chainId,
1582
1942
  request: {
@@ -1592,28 +1952,32 @@ async function runL2TxBuild(options) {
1592
1952
  estimatedFeeWei,
1593
1953
  status: "built"
1594
1954
  };
1955
+ const hash = calculateContentHash(artifact);
1956
+ const planId = createIgraPlanId(hash);
1957
+ artifact.planId = planId;
1958
+ artifact.contentHash = hash;
1595
1959
  assertValidIgraTxPlanArtifact(artifact);
1596
1960
  const outDir = options.outDir || "plans";
1597
- const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1961
+ const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1598
1962
  await fs5.mkdir(sanitizedDir, { recursive: true });
1599
- const artifactPath = path6.join(sanitizedDir, `${planId}.igra.plan.json`);
1963
+ const artifactPath = path7.join(sanitizedDir, `${planId}.igra.plan.json`);
1600
1964
  await writeArtifact2(artifactPath, artifact);
1601
1965
  if (options.json) {
1602
1966
  console.log(JSON.stringify({
1603
1967
  networkId: profile.name,
1604
- l2Network: networkName,
1605
- chainId,
1968
+ l2Network: profile.name,
1969
+ chainId: profile.chainId,
1606
1970
  planId,
1607
1971
  artifactPath,
1608
1972
  artifact
1609
1973
  }, null, 2));
1610
1974
  return;
1611
1975
  }
1612
- console.log(`${profile.displayName} L2 transaction plan built`);
1976
+ console.log(`${profile.displayName} L2 transaction plan built (${profile.source})`);
1613
1977
  console.log("");
1614
1978
  console.log(`Plan ID: ${planId}`);
1615
- console.log(`Network: ${networkName}`);
1616
- console.log(`Chain ID: ${chainId}`);
1979
+ console.log(`Network: ${profile.name}`);
1980
+ console.log(`Chain ID: ${profile.chainId}`);
1617
1981
  console.log(`Mode: l2-rpc`);
1618
1982
  if (options.from) console.log(`From: ${options.from}`);
1619
1983
  console.log(`To: ${options.to}`);
@@ -1647,7 +2011,7 @@ async function runL2TxSign(options) {
1647
2011
  }
1648
2012
  let accountInfo;
1649
2013
  if (options.account) {
1650
- const store = await loadRealAccountStore2();
2014
+ const store = await loadRealAccountStore3();
1651
2015
  const accountData = resolveRealAccountOrAddress(store, options.account);
1652
2016
  if (plan.request.from && plan.request.from.toLowerCase() !== accountData.address.toLowerCase()) {
1653
2017
  throw new Error(`Account address mismatch: plan specifies '${plan.request.from}' but resolved account '${accountData.name ?? accountData.address}' is '${accountData.address}'`);
@@ -1686,14 +2050,14 @@ async function runL2TxSign(options) {
1686
2050
  }
1687
2051
  throw e;
1688
2052
  }
1689
- const signedId = createIgraSignedId();
1690
2053
  const artifact = {
1691
2054
  schema: ARTIFACT_SCHEMAS2.IGRA_SIGNED_TX,
1692
2055
  hardkasVersion: HARDKAS_VERSION2,
1693
2056
  networkId: plan.networkId,
1694
2057
  mode: "l2-rpc",
1695
2058
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1696
- signedId,
2059
+ signedId: "",
2060
+ // Placeholder
1697
2061
  sourcePlanId: plan.planId,
1698
2062
  sourcePlanPath: options.planPath,
1699
2063
  l2Network: plan.l2Network,
@@ -1702,11 +2066,15 @@ async function runL2TxSign(options) {
1702
2066
  txHash: result.txHash || "unknown",
1703
2067
  status: "signed"
1704
2068
  };
2069
+ const hash = calculateContentHash(artifact);
2070
+ const signedId = createIgraSignedId(hash);
2071
+ artifact.signedId = signedId;
2072
+ artifact.contentHash = hash;
1705
2073
  assertValidIgraSignedTxArtifact(artifact);
1706
2074
  const outDir = options.outDir || "signed";
1707
- const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2075
+ const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1708
2076
  await fs5.mkdir(sanitizedDir, { recursive: true });
1709
- const artifactPath = path6.join(sanitizedDir, `${signedId}.igra.signed.json`);
2077
+ const artifactPath = path7.join(sanitizedDir, `${signedId}.igra.signed.json`);
1710
2078
  await writeArtifact2(artifactPath, artifact);
1711
2079
  if (options.json) {
1712
2080
  console.log(JSON.stringify({
@@ -1739,53 +2107,50 @@ async function runL2TxSign(options) {
1739
2107
  console.log(" This is an Igra L2 EVM signed transaction, not a Kaspa L1 UTXO transaction.");
1740
2108
  }
1741
2109
  async function runL2TxSend(options) {
2110
+ const loaded = await loadHardkasConfig7();
1742
2111
  const artifactData = await readArtifact2(options.signedPath);
1743
2112
  assertValidIgraSignedTxArtifact(artifactData);
1744
2113
  const artifact = artifactData;
1745
- if (artifact.schema !== ARTIFACT_SCHEMAS2.IGRA_SIGNED_TX) {
1746
- throw new Error(`Invalid signed artifact schema: ${artifact.schema}`);
2114
+ const profile = resolveL2Profile3({
2115
+ name: options.network || artifact.l2Network,
2116
+ userProfiles: loaded.config.l2?.networks,
2117
+ cliOverrides: {
2118
+ ...options.url !== void 0 ? { url: options.url } : {},
2119
+ ...options.chainId !== void 0 ? { chainId: options.chainId } : {}
2120
+ }
2121
+ });
2122
+ const isMainnet = profile.name === "mainnet" || profile.name.includes("mainnet") || artifact.networkId === "mainnet" || artifact.chainId === 1 || profile.chainId === 1;
2123
+ if (isMainnet) {
2124
+ throw new Error("L2 mainnet broadcast is disabled in HardKAS v0.2-alpha.");
1747
2125
  }
1748
- if (artifact.mode !== "l2-rpc") {
1749
- throw new Error(`Invalid artifact mode: ${artifact.mode} (expected 'l2-rpc')`);
2126
+ if (!options.yes) {
2127
+ console.log("\n L2 broadcast cancelled: --yes flag is required for this phase.");
2128
+ process.exit(0);
1750
2129
  }
1751
- if (artifact.status !== "signed") {
1752
- throw new Error(`Invalid artifact status: ${artifact.status} (expected 'signed')`);
2130
+ const rpcUrl = options.url ?? profile.rpcUrl;
2131
+ if (!rpcUrl) {
2132
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
1753
2133
  }
1754
- if (!options.yes) {
2134
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
2135
+ const remoteChainId = await client.getChainId();
2136
+ if (profile.chainId !== void 0 && String(remoteChainId) !== String(profile.chainId)) {
1755
2137
  console.log("");
1756
- console.log("Refusing to submit Igra L2 transaction without --yes.");
2138
+ console.log("Refusing to submit Igra L2 transaction: profile chainId does not match RPC endpoint.");
1757
2139
  console.log("");
1758
- console.log("Reason:");
1759
- console.log(" This operation broadcasts a signed L2 transaction.");
2140
+ console.log(`Profile chainId: ${profile.chainId}`);
2141
+ console.log(`RPC chainId: ${remoteChainId}`);
1760
2142
  console.log("");
1761
- console.log("Use:");
1762
- console.log(` hardkas l2 tx send ${options.signedPath} --yes`);
2143
+ console.log("Suggestion:");
2144
+ console.log(" Check --url and --network.");
1763
2145
  process.exit(1);
1764
2146
  }
1765
- const networkName = options.network ?? artifact.l2Network ?? "igra";
1766
- const profile = getL2Profile3(networkName);
1767
- if (!profile) {
1768
- throw new Error(`L2 profile '${networkName}' not found.`);
1769
- }
1770
- const isMainnet = networkName === "mainnet" || profile.name.includes("mainnet") || artifact.networkId === "mainnet" || artifact.chainId === 1;
1771
- if (isMainnet) {
1772
- throw new Error("L2 mainnet broadcast is disabled in HardKAS v0.2-alpha.");
1773
- }
1774
- const rpcUrl = options.url ?? profile.rpcUrl;
1775
- if (!rpcUrl) {
1776
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
1777
- }
1778
- const client = new EvmJsonRpcClient({ url: rpcUrl });
1779
- const remoteChainId = await client.getChainId();
1780
- if (remoteChainId !== artifact.chainId) {
2147
+ if (String(remoteChainId) !== String(artifact.chainId)) {
1781
2148
  console.log("");
1782
2149
  console.log("Refusing to submit Igra L2 transaction: signed artifact chainId does not match RPC endpoint.");
1783
2150
  console.log("");
1784
2151
  console.log(`Artifact chainId: ${artifact.chainId}`);
1785
2152
  console.log(`RPC chainId: ${remoteChainId}`);
1786
2153
  console.log("");
1787
- console.log("Suggestion:");
1788
- console.log(" Check --url and --network.");
1789
2154
  process.exit(1);
1790
2155
  }
1791
2156
  const txHash = await client.sendRawTransaction(artifact.rawTransaction);
@@ -1804,14 +2169,14 @@ async function runL2TxSend(options) {
1804
2169
  status: "submitted"
1805
2170
  };
1806
2171
  assertValidIgraTxReceiptArtifact(receipt);
1807
- const receiptDir = path6.join(".hardkas", "l2-receipts");
2172
+ const receiptDir = path7.join(".hardkas", "l2-receipts");
1808
2173
  await fs5.mkdir(receiptDir, { recursive: true });
1809
- const receiptPath = path6.join(receiptDir, `${txHash}.igra.receipt.json`);
2174
+ const receiptPath = path7.join(receiptDir, `${txHash}.igra.receipt.json`);
1810
2175
  await writeArtifact2(receiptPath, receipt);
1811
2176
  if (options.json) {
1812
2177
  console.log(JSON.stringify({
1813
2178
  networkId: artifact.networkId,
1814
- l2Network: networkName,
2179
+ l2Network: profile.name,
1815
2180
  chainId: artifact.chainId,
1816
2181
  rpcUrl,
1817
2182
  txHash,
@@ -1824,8 +2189,8 @@ async function runL2TxSend(options) {
1824
2189
  console.log("Igra L2 transaction submitted");
1825
2190
  console.log("");
1826
2191
  console.log(`Tx hash: ${txHash}`);
1827
- console.log(`Network: ${networkName}`);
1828
- console.log(`Chain ID: ${artifact.chainId}`);
2192
+ console.log(`Network: ${profile.name} (${profile.source})`);
2193
+ console.log(`Chain ID: ${profile.chainId}`);
1829
2194
  console.log(`Mode: l2-rpc`);
1830
2195
  console.log(`Source: ${options.signedPath}`);
1831
2196
  console.log(`RPC: ${rpcUrl}`);
@@ -1835,23 +2200,29 @@ async function runL2TxSend(options) {
1835
2200
  console.log("");
1836
2201
  console.log("Next:");
1837
2202
  console.log(" Check receipt:");
1838
- console.log(` hardkas l2 tx receipt ${txHash} --network ${networkName}`);
2203
+ console.log(` hardkas l2 tx receipt ${txHash} --network ${profile.name}`);
1839
2204
  console.log("");
1840
2205
  console.log("Warning:");
1841
2206
  console.log(" This is an Igra L2 EVM transaction, not a Kaspa L1 UTXO transaction.");
1842
2207
  }
1843
2208
  async function runL2TxReceipt(options) {
2209
+ const loaded = await loadHardkasConfig7();
1844
2210
  let localReceipt;
1845
2211
  try {
1846
2212
  localReceipt = await loadIgraTxReceiptArtifact(options.txHash);
1847
2213
  } catch (e) {
1848
2214
  }
1849
- const networkName = options.network ?? localReceipt?.l2Network ?? "igra";
1850
- const profile = getL2Profile3(networkName);
1851
- const rpcUrl = options.url ?? profile?.rpcUrl;
2215
+ const profile = resolveL2Profile3({
2216
+ name: options.network || localReceipt?.l2Network,
2217
+ userProfiles: loaded.config.l2?.networks,
2218
+ cliOverrides: {
2219
+ ...options.url !== void 0 ? { url: options.url } : {}
2220
+ }
2221
+ });
2222
+ const rpcUrl = profile.rpcUrl;
1852
2223
  let remoteReceipt = null;
1853
2224
  if (rpcUrl) {
1854
- const client = new EvmJsonRpcClient({ url: rpcUrl });
2225
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
1855
2226
  const raw = await client.getTransactionReceipt(options.txHash);
1856
2227
  remoteReceipt = normalizeEvmTransactionReceipt(raw);
1857
2228
  }
@@ -1862,7 +2233,7 @@ async function runL2TxReceipt(options) {
1862
2233
  if (options.json) {
1863
2234
  console.log(JSON.stringify({
1864
2235
  networkId: localReceipt?.networkId ?? "igra",
1865
- l2Network: networkName,
2236
+ l2Network: profile.name,
1866
2237
  chainId: localReceipt?.chainId,
1867
2238
  rpcUrl,
1868
2239
  txHash: options.txHash,
@@ -1875,7 +2246,7 @@ async function runL2TxReceipt(options) {
1875
2246
  console.log("Igra L2 transaction receipt");
1876
2247
  console.log("");
1877
2248
  console.log(`Tx hash: ${options.txHash}`);
1878
- console.log(`Network: ${networkName}`);
2249
+ console.log(`Network: ${profile.name} (${profile.source})`);
1879
2250
  if (localReceipt) {
1880
2251
  console.log(`Chain ID: ${localReceipt.chainId}`);
1881
2252
  console.log(`Local: found`);
@@ -1898,20 +2269,26 @@ async function runL2TxReceipt(options) {
1898
2269
  console.log(" This is an Igra L2 EVM transaction receipt, not a Kaspa L1 transaction.");
1899
2270
  }
1900
2271
  async function runL2TxStatus(options) {
1901
- const networkName = options.network ?? "igra";
1902
- const profile = getL2Profile3(networkName);
1903
- const rpcUrl = options.url ?? profile?.rpcUrl;
2272
+ const loaded = await loadHardkasConfig7();
2273
+ const profile = resolveL2Profile3({
2274
+ name: options.network,
2275
+ userProfiles: loaded.config.l2?.networks,
2276
+ cliOverrides: {
2277
+ ...options.url !== void 0 ? { url: options.url } : {}
2278
+ }
2279
+ });
2280
+ const rpcUrl = profile.rpcUrl;
1904
2281
  if (!rpcUrl) {
1905
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
2282
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
1906
2283
  }
1907
- const client = new EvmJsonRpcClient({ url: rpcUrl });
2284
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
1908
2285
  const raw = await client.getTransactionReceipt(options.txHash);
1909
2286
  const remoteReceipt = normalizeEvmTransactionReceipt(raw);
1910
2287
  const status = remoteReceipt?.status ?? "pending";
1911
2288
  if (options.json) {
1912
2289
  console.log(JSON.stringify({
1913
- networkId: profile?.name ?? networkName,
1914
- l2Network: networkName,
2290
+ networkId: profile.name,
2291
+ l2Network: profile.name,
1915
2292
  rpcUrl,
1916
2293
  txHash: options.txHash,
1917
2294
  status,
@@ -1922,7 +2299,7 @@ async function runL2TxStatus(options) {
1922
2299
  console.log("Igra L2 transaction status");
1923
2300
  console.log("");
1924
2301
  console.log(`Tx hash: ${options.txHash}`);
1925
- console.log(`Network: ${networkName}`);
2302
+ console.log(`Network: ${profile.name} (${profile.source})`);
1926
2303
  console.log(`Status: ${status}`);
1927
2304
  if (remoteReceipt) {
1928
2305
  console.log(`Block: ${remoteReceipt.blockNumber ?? "unknown"}`);
@@ -1942,31 +2319,36 @@ function assertHexData(data, field) {
1942
2319
 
1943
2320
  // src/runners/l2-contract-runners.ts
1944
2321
  import fs6 from "fs/promises";
1945
- import path7 from "path";
2322
+ import path8 from "path";
1946
2323
  import {
1947
- getL2Profile as getL2Profile4,
1948
- EvmJsonRpcClient as EvmJsonRpcClient2,
2324
+ resolveL2Profile as resolveL2Profile4,
2325
+ EvmJsonRpcClient as EvmJsonRpcClient3,
1949
2326
  toHexQuantity as toHexQuantity2,
1950
2327
  encodeConstructorArgs
1951
2328
  } from "@hardkas/l2";
2329
+ import { loadHardkasConfig as loadHardkasConfig8 } from "@hardkas/config";
1952
2330
  import {
1953
2331
  assertValidIgraTxPlanArtifact as assertValidIgraTxPlanArtifact2,
1954
2332
  writeArtifact as writeArtifact3,
1955
2333
  HARDKAS_VERSION as HARDKAS_VERSION3,
1956
2334
  ARTIFACT_SCHEMAS as ARTIFACT_SCHEMAS3,
1957
- createIgraDeployPlanId
2335
+ createIgraDeployPlanId,
2336
+ calculateContentHash as calculateContentHash2
1958
2337
  } from "@hardkas/artifacts";
1959
2338
  async function runL2ContractDeployPlan(options) {
1960
- const networkName = options.network ?? "igra";
1961
- const profile = getL2Profile4(networkName);
1962
- if (!profile) {
1963
- throw new Error(`L2 profile '${networkName}' not found.`);
1964
- }
1965
- const rpcUrl = options.url ?? profile.rpcUrl;
2339
+ const loaded = await loadHardkasConfig8();
2340
+ const profile = resolveL2Profile4({
2341
+ name: options.network,
2342
+ userProfiles: loaded.config.l2?.networks,
2343
+ cliOverrides: {
2344
+ ...options.url !== void 0 ? { url: options.url } : {}
2345
+ }
2346
+ });
2347
+ const rpcUrl = profile.rpcUrl;
1966
2348
  if (!rpcUrl) {
1967
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
2349
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
1968
2350
  }
1969
- const client = new EvmJsonRpcClient2({ url: rpcUrl });
2351
+ const client = new EvmJsonRpcClient3({ url: rpcUrl });
1970
2352
  assertEvmAddress2(options.from, "from");
1971
2353
  if (!options.bytecode || options.bytecode === "0x") {
1972
2354
  throw new Error("Missing or empty bytecode. Provide non-empty 0x-prefixed hex.");
@@ -1999,14 +2381,14 @@ async function runL2ContractDeployPlan(options) {
1999
2381
  gasLimit = g.toString();
2000
2382
  }
2001
2383
  const estimatedFeeWei = (BigInt(gasLimit) * BigInt(gasPrice)).toString();
2002
- const planId = createIgraDeployPlanId();
2003
2384
  const artifact = {
2004
2385
  schema: ARTIFACT_SCHEMAS3.IGRA_TX_PLAN,
2005
2386
  hardkasVersion: HARDKAS_VERSION3,
2006
2387
  networkId: profile.name,
2007
2388
  mode: "l2-rpc",
2008
2389
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2009
- planId,
2390
+ planId: "",
2391
+ // Placeholder
2010
2392
  l2Network: profile.name,
2011
2393
  chainId,
2012
2394
  txType: "contract-deploy",
@@ -2022,28 +2404,32 @@ async function runL2ContractDeployPlan(options) {
2022
2404
  estimatedFeeWei,
2023
2405
  status: "built"
2024
2406
  };
2407
+ const hash = calculateContentHash2(artifact);
2408
+ const planId = createIgraDeployPlanId(hash);
2409
+ artifact.planId = planId;
2410
+ artifact.contentHash = hash;
2025
2411
  assertValidIgraTxPlanArtifact2(artifact);
2026
2412
  const outDir = options.outDir || "plans";
2027
- const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2413
+ const sanitizedDir = path8.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2028
2414
  await fs6.mkdir(sanitizedDir, { recursive: true });
2029
- const artifactPath = path7.join(sanitizedDir, `${planId}.igra.deploy.plan.json`);
2415
+ const artifactPath = path8.join(sanitizedDir, `${planId}.igra.deploy.plan.json`);
2030
2416
  await writeArtifact3(artifactPath, artifact);
2031
2417
  if (options.json) {
2032
2418
  console.log(JSON.stringify({
2033
2419
  networkId: profile.name,
2034
- l2Network: networkName,
2035
- chainId,
2420
+ l2Network: profile.name,
2421
+ chainId: profile.chainId,
2036
2422
  planId,
2037
2423
  artifactPath,
2038
2424
  artifact
2039
2425
  }, null, 2));
2040
2426
  return;
2041
2427
  }
2042
- console.log(`Igra L2 contract deploy plan built`);
2428
+ console.log(`Igra L2 contract deploy plan built (${profile.source})`);
2043
2429
  console.log("");
2044
2430
  console.log(`Plan ID: ${planId}`);
2045
- console.log(`Network: ${networkName}`);
2046
- console.log(`Chain ID: ${chainId}`);
2431
+ console.log(`Network: ${profile.name}`);
2432
+ console.log(`Chain ID: ${profile.chainId}`);
2047
2433
  console.log(`Mode: l2-rpc`);
2048
2434
  console.log(`From: ${options.from}`);
2049
2435
  console.log(`Type: contract-deploy`);
@@ -2076,18 +2462,24 @@ function assertHexData2(data, field) {
2076
2462
  }
2077
2463
 
2078
2464
  // src/runners/l2-bridge-runners.ts
2079
- import { getL2BridgeAssumptions } from "@hardkas/l2";
2465
+ import { resolveL2Profile as resolveL2Profile5 } from "@hardkas/l2";
2466
+ import { HARDKAS_VERSION as HARDKAS_VERSION4 } from "@hardkas/artifacts";
2467
+ import { loadHardkasConfig as loadHardkasConfig9 } from "@hardkas/config";
2080
2468
  async function runL2BridgeStatus(options) {
2081
- const networkName = options.network ?? "igra";
2082
- const assumptions = getL2BridgeAssumptions(networkName);
2083
- if (!assumptions) {
2084
- throw new Error(`No bridge assumptions found for network '${networkName}'.`);
2085
- }
2469
+ const loaded = await loadHardkasConfig9();
2470
+ const profile = resolveL2Profile5({
2471
+ name: options.network,
2472
+ userProfiles: loaded.config.l2?.networks,
2473
+ cliOverrides: {
2474
+ ...options.url !== void 0 ? { url: options.url } : {}
2475
+ }
2476
+ });
2477
+ const assumptions = mapProfileToAssumptions(profile);
2086
2478
  if (options.json) {
2087
2479
  console.log(JSON.stringify(assumptions, null, 2));
2088
2480
  return;
2089
2481
  }
2090
- console.log(`${capitalize(networkName)} bridge status`);
2482
+ console.log(`${capitalize(profile.name)} bridge status (${profile.source})`);
2091
2483
  console.log("");
2092
2484
  console.log(`Network: ${assumptions.l2Network}`);
2093
2485
  console.log(`Bridge phase: ${assumptions.bridgePhase}`);
@@ -2106,16 +2498,20 @@ async function runL2BridgeStatus(options) {
2106
2498
  }
2107
2499
  }
2108
2500
  async function runL2BridgeAssumptions(options) {
2109
- const networkName = options.network ?? "igra";
2110
- const assumptions = getL2BridgeAssumptions(networkName);
2111
- if (!assumptions) {
2112
- throw new Error(`No bridge assumptions found for network '${networkName}'.`);
2113
- }
2501
+ const loaded = await loadHardkasConfig9();
2502
+ const profile = resolveL2Profile5({
2503
+ name: options.network,
2504
+ userProfiles: loaded.config.l2?.networks,
2505
+ cliOverrides: {
2506
+ ...options.url !== void 0 ? { url: options.url } : {}
2507
+ }
2508
+ });
2509
+ const assumptions = mapProfileToAssumptions(profile);
2114
2510
  if (options.json) {
2115
2511
  console.log(JSON.stringify(assumptions, null, 2));
2116
2512
  return;
2117
2513
  }
2118
- console.log(`${capitalize(networkName)} bridge assumptions`);
2514
+ console.log(`${capitalize(profile.name)} bridge assumptions (${profile.source})`);
2119
2515
  console.log("");
2120
2516
  console.log("Bridge security phases:");
2121
2517
  console.log(" pre-ZK: stronger trust assumptions / non-trustless exit");
@@ -2128,21 +2524,39 @@ async function runL2BridgeAssumptions(options) {
2128
2524
  console.log("Trustless exit:");
2129
2525
  console.log(` ${assumptions.trustlessExit ? "yes" : "no"}`);
2130
2526
  }
2527
+ function mapProfileToAssumptions(profile) {
2528
+ return {
2529
+ schema: "hardkas.l2BridgeAssumptions.v1",
2530
+ hardkasVersion: HARDKAS_VERSION4,
2531
+ l2Network: profile.name,
2532
+ bridgePhase: profile.security.bridgePhase,
2533
+ trustlessExit: profile.security.trustlessExit,
2534
+ custodyModel: profile.security.custodyModel,
2535
+ exitModel: profile.security.bridgePhase === "zk" ? "Trustless exit is available via ZK proofs." : "Trustless exit is available only in the ZK phase.",
2536
+ riskProfile: profile.security.riskProfile,
2537
+ notes: profile.security.notes,
2538
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2539
+ };
2540
+ }
2131
2541
  function capitalize(s) {
2132
2542
  return s.charAt(0).toUpperCase() + s.slice(1);
2133
2543
  }
2134
2544
 
2135
2545
  // src/runners/l2-rpc-health-runner.ts
2136
- import { getL2Profile as getL2Profile5, checkEvmRpcHealth, waitForEvmRpcReady } from "@hardkas/l2";
2546
+ import { resolveL2Profile as resolveL2Profile6, checkEvmRpcHealth, waitForEvmRpcReady } from "@hardkas/l2";
2547
+ import { loadHardkasConfig as loadHardkasConfig10 } from "@hardkas/config";
2137
2548
  async function runL2RpcHealth(options) {
2138
- const networkName = options.network ?? "igra";
2139
- const profile = getL2Profile5(networkName);
2140
- if (!profile) {
2141
- throw new Error(`L2 profile '${networkName}' not found.`);
2142
- }
2143
- const rpcUrl = options.url ?? profile.rpcUrl;
2549
+ const loaded = await loadHardkasConfig10();
2550
+ const profile = resolveL2Profile6({
2551
+ name: options.network,
2552
+ userProfiles: loaded.config.l2?.networks,
2553
+ cliOverrides: {
2554
+ ...options.url !== void 0 ? { url: options.url } : {}
2555
+ }
2556
+ });
2557
+ const rpcUrl = profile.rpcUrl;
2144
2558
  if (!rpcUrl) {
2145
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
2559
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
2146
2560
  }
2147
2561
  const healthOptions = {
2148
2562
  url: rpcUrl,
@@ -2151,63 +2565,78 @@ async function runL2RpcHealth(options) {
2151
2565
  maxWaitMs: (options.timeout ?? 60) * 1e3
2152
2566
  };
2153
2567
  const health = options.wait ? await waitForEvmRpcReady(healthOptions) : await checkEvmRpcHealth(healthOptions);
2568
+ let chainIdMismatch = false;
2569
+ if (health.ready && profile.chainId !== void 0 && health.chainId !== void 0 && String(health.chainId) !== String(profile.chainId)) {
2570
+ chainIdMismatch = true;
2571
+ }
2154
2572
  if (options.json) {
2155
- console.log(JSON.stringify(health, (key, value) => typeof value === "bigint" ? value.toString() : value, 2));
2573
+ console.log(JSON.stringify({ ...health, chainIdMismatch, profileChainId: profile.chainId }, (key, value) => typeof value === "bigint" ? value.toString() : value, 2));
2156
2574
  return;
2157
2575
  }
2158
- console.log(`${profile.displayName} L2 RPC health`);
2576
+ console.log(`${profile.displayName} L2 RPC health (${profile.source})`);
2159
2577
  console.log("");
2160
- console.log(`Network: ${networkName}`);
2578
+ console.log(`Network: ${profile.name}`);
2161
2579
  console.log(`URL: ${health.url}`);
2162
2580
  console.log(`Status: ${health.ready ? "ready" : "not ready"}`);
2163
2581
  if (health.ready) {
2164
- console.log(`Chain ID: ${health.chainId}`);
2582
+ console.log(`Chain ID: ${health.chainId} ${chainIdMismatch ? `(CONFLICT! Expected ${profile.chainId})` : ""}`);
2165
2583
  console.log(`Block: ${health.blockNumber}`);
2166
2584
  console.log(`Gas: ${health.gasPriceWei} wei`);
2167
2585
  if (health.latencyMs !== void 0) {
2168
2586
  console.log(`Latency: ${health.latencyMs}ms`);
2169
2587
  }
2170
2588
  }
2589
+ if (chainIdMismatch) {
2590
+ console.log("");
2591
+ console.log("CRITICAL CONFLICT:");
2592
+ console.log(` The remote RPC reports chain ID ${health.chainId}, but your local profile is configured for ${profile.chainId}.`);
2593
+ console.log(" Ensure you are connecting to the correct network.");
2594
+ }
2171
2595
  if (health.error) {
2172
2596
  console.log(`Error: ${health.error}`);
2173
2597
  }
2174
2598
  console.log("");
2175
2599
  console.log("Warning:");
2176
2600
  console.log(" This is L2 EVM state, not Kaspa L1 UTXO state.");
2177
- if (!health.ready) {
2601
+ if (!health.ready || chainIdMismatch) {
2178
2602
  process.exitCode = 1;
2179
2603
  }
2180
2604
  }
2181
2605
 
2182
2606
  // src/runners/l2-account-runners.ts
2183
- import { getL2Profile as getL2Profile6, EvmJsonRpcClient as EvmJsonRpcClient3, formatWeiAsEtherLike } from "@hardkas/l2";
2607
+ import { resolveL2Profile as resolveL2Profile7, EvmJsonRpcClient as EvmJsonRpcClient4, formatWeiAsEtherLike } from "@hardkas/l2";
2608
+ import { loadHardkasConfig as loadHardkasConfig11 } from "@hardkas/config";
2184
2609
  async function getClient(options) {
2185
- const networkName = options.network ?? "igra";
2186
- const profile = getL2Profile6(networkName);
2187
- if (!profile) {
2188
- throw new Error(`L2 profile '${networkName}' not found.`);
2189
- }
2190
- const rpcUrl = options.url ?? profile.rpcUrl;
2610
+ const loaded = await loadHardkasConfig11();
2611
+ const profile = resolveL2Profile7({
2612
+ name: options.network,
2613
+ userProfiles: loaded.config.l2?.networks,
2614
+ cliOverrides: {
2615
+ ...options.url !== void 0 ? { url: options.url } : {},
2616
+ ...options.chainId !== void 0 ? { chainId: options.chainId } : {}
2617
+ }
2618
+ });
2619
+ const rpcUrl = profile.rpcUrl;
2191
2620
  if (!rpcUrl) {
2192
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
2621
+ throw new Error(`No L2 RPC URL configured for network '${profile.name}'. Pass --url <rpcUrl>.`);
2193
2622
  }
2194
2623
  return {
2195
- client: new EvmJsonRpcClient3({ url: rpcUrl }),
2196
- profile,
2197
- networkName
2624
+ client: new EvmJsonRpcClient4({ url: rpcUrl }),
2625
+ profile
2198
2626
  };
2199
2627
  }
2200
2628
  async function runL2Balance(address, options) {
2201
- const { client, profile, networkName } = await getClient(options);
2629
+ const { client, profile } = await getClient(options);
2202
2630
  const blockTag = options.block ?? "latest";
2203
2631
  const balanceWei = await client.getBalanceWei(address, blockTag);
2204
2632
  const balanceFormatted = formatWeiAsEtherLike(balanceWei, profile.gasToken, profile.nativeTokenDecimals);
2205
2633
  if (options.json) {
2206
2634
  console.log(JSON.stringify({
2207
2635
  networkId: profile.name,
2208
- l2Network: networkName,
2636
+ l2Network: profile.name,
2209
2637
  chainId: profile.chainId,
2210
2638
  rpcUrl: profile.rpcUrl,
2639
+ source: profile.source,
2211
2640
  address,
2212
2641
  block: blockTag,
2213
2642
  balanceWei: balanceWei.toString(),
@@ -2216,9 +2645,9 @@ async function runL2Balance(address, options) {
2216
2645
  }, null, 2));
2217
2646
  return;
2218
2647
  }
2219
- console.log(`${profile.displayName} L2 balance`);
2648
+ console.log(`${profile.displayName} L2 balance (${profile.source})`);
2220
2649
  console.log("");
2221
- console.log(`Network: ${networkName}`);
2650
+ console.log(`Network: ${profile.name}`);
2222
2651
  console.log(`Address: ${address}`);
2223
2652
  console.log(`Block: ${blockTag}`);
2224
2653
  console.log(`Balance: ${balanceFormatted}`);
@@ -2228,24 +2657,25 @@ async function runL2Balance(address, options) {
2228
2657
  console.log(" This is L2 EVM account state, not Kaspa L1 UTXO state.");
2229
2658
  }
2230
2659
  async function runL2Nonce(address, options) {
2231
- const { client, profile, networkName } = await getClient(options);
2660
+ const { client, profile } = await getClient(options);
2232
2661
  const blockTag = options.block ?? "latest";
2233
2662
  const nonce = await client.getTransactionCount(address, blockTag);
2234
2663
  if (options.json) {
2235
2664
  console.log(JSON.stringify({
2236
2665
  networkId: profile.name,
2237
- l2Network: networkName,
2666
+ l2Network: profile.name,
2238
2667
  chainId: profile.chainId,
2239
2668
  rpcUrl: profile.rpcUrl,
2669
+ source: profile.source,
2240
2670
  address,
2241
2671
  block: blockTag,
2242
2672
  nonce: nonce.toString()
2243
2673
  }, null, 2));
2244
2674
  return;
2245
2675
  }
2246
- console.log(`${profile.displayName} L2 nonce`);
2676
+ console.log(`${profile.displayName} L2 nonce (${profile.source})`);
2247
2677
  console.log("");
2248
- console.log(`Network: ${networkName}`);
2678
+ console.log(`Network: ${profile.name}`);
2249
2679
  console.log(`Address: ${address}`);
2250
2680
  console.log(`Block: ${blockTag}`);
2251
2681
  console.log(`Nonce: ${nonce}`);
@@ -2261,14 +2691,14 @@ function registerL2Commands(program) {
2261
2691
  await runL2Networks(options);
2262
2692
  });
2263
2693
  const l2Profile = l2.command("profile").description("L2 profile management");
2264
- l2Profile.command("show <name>").description("Show L2 profile details").option("--json", "Output results in JSON format").action(async (name, options) => {
2694
+ l2Profile.command("show [name]").description("Show L2 profile details").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--chain-id <id>", "Chain ID override").option("--json", "Output results in JSON format").action(async (name, options) => {
2265
2695
  await runL2ProfileShow({ name, ...options });
2266
2696
  });
2267
- l2Profile.command("validate <name>").description("Validate L2 profile").option("--json", "Output results in JSON format").action(async (name, options) => {
2697
+ l2Profile.command("validate [name]").description("Validate L2 profile").option("--network <name>", "L2 network name").option("--url <url>", "Override RPC URL for validation").option("--json", "Output results in JSON format").action(async (name, options) => {
2268
2698
  await runL2ProfileValidate({ name, ...options });
2269
2699
  });
2270
2700
  const l2tx = l2.command("tx").description("Igra transaction management");
2271
- l2tx.command("build").description("Build L2 transaction plan").option("--network <name>", "L2 network name", "igra").option("--url <url>", "RPC URL").option("--from <address>", "From address").option("--to <address>", "To address").option("--value <wei>", "Value in wei", "0").option("--data <hex>", "Call data", "0x").option("--json", "Output as JSON").action(async (options) => {
2701
+ l2tx.command("build").description("Build L2 transaction plan").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--chain-id <id>", "Chain ID override").option("--from <address>", "From address").option("--to <address>", "To address").option("--value <wei>", "Value in wei", "0").option("--data <hex>", "Call data", "0x").option("--json", "Output as JSON").action(async (options) => {
2272
2702
  try {
2273
2703
  await runL2TxBuild(options);
2274
2704
  } catch (e) {
@@ -2304,7 +2734,7 @@ function registerL2Commands(program) {
2304
2734
  }
2305
2735
  });
2306
2736
  const l2contract = l2.command("contract").description("Igra contract management");
2307
- l2contract.command("deploy-plan").description("Build L2 contract deployment plan").option("--network <name>", "L2 network name", "igra").option("--bytecode <hex>", "Contract bytecode").option("--constructor <sig>", "Constructor signature").option("--args <csv>", "Constructor arguments").option("--json", "Output as JSON").action(async (options) => {
2737
+ l2contract.command("deploy-plan").description("Build L2 contract deployment plan").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--chain-id <id>", "Chain ID override").option("--bytecode <hex>", "Contract bytecode").option("--constructor <sig>", "Constructor signature").option("--args <csv>", "Constructor arguments").option("--json", "Output as JSON").action(async (options) => {
2308
2738
  try {
2309
2739
  await runL2ContractDeployPlan(options);
2310
2740
  } catch (e) {
@@ -2312,7 +2742,7 @@ function registerL2Commands(program) {
2312
2742
  }
2313
2743
  });
2314
2744
  const l2bridge = l2.command("bridge").description("Igra bridge awareness");
2315
- l2bridge.command("status").description("Show bridge security status").option("--json", "Output as JSON").action(async (options) => {
2745
+ l2bridge.command("status").description("Show bridge security status").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--json", "Output as JSON").action(async (options) => {
2316
2746
  try {
2317
2747
  await runL2BridgeStatus(options);
2318
2748
  } catch (e) {
@@ -2327,21 +2757,21 @@ function registerL2Commands(program) {
2327
2757
  }
2328
2758
  });
2329
2759
  const l2rpc = l2.command("rpc").description("Igra RPC diagnostics");
2330
- l2rpc.command("health").description("Check L2 RPC health").option("--json", "Output as JSON").action(async (options) => {
2760
+ l2rpc.command("health").description("Check L2 RPC health").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--json", "Output as JSON").action(async (options) => {
2331
2761
  try {
2332
2762
  await runL2RpcHealth(options);
2333
2763
  } catch (e) {
2334
2764
  handleError(e);
2335
2765
  }
2336
2766
  });
2337
- l2.command("balance <address>").description("Check Igra L2 balance").option("--json", "Output as JSON").action(async (address, options) => {
2767
+ l2.command("balance <address>").description("Check Igra L2 balance").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--chain-id <id>", "Chain ID override").option("--json", "Output as JSON").action(async (address, options) => {
2338
2768
  try {
2339
2769
  await runL2Balance(address, options);
2340
2770
  } catch (e) {
2341
2771
  handleError(e);
2342
2772
  }
2343
2773
  });
2344
- l2.command("nonce <address>").description("Check Igra L2 nonce").option("--json", "Output as JSON").action(async (address, options) => {
2774
+ l2.command("nonce <address>").description("Check Igra L2 nonce").option("--network <name>", "L2 network name").option("--url <url>", "RPC URL override").option("--chain-id <id>", "Chain ID override").option("--json", "Output as JSON").action(async (address, options) => {
2345
2775
  try {
2346
2776
  await runL2Nonce(address, options);
2347
2777
  } catch (e) {
@@ -2356,20 +2786,19 @@ async function runNodeStart(input) {
2356
2786
  const runner = new DockerKaspadRunner(input);
2357
2787
  const status = await runner.start();
2358
2788
  const lines = [
2359
- "Kaspa node started",
2789
+ "Kaspa node started successfully",
2360
2790
  "",
2361
- "Backend: Docker",
2791
+ `Backend: Docker`,
2362
2792
  `Image: ${status.image}`,
2363
2793
  `Container: ${status.containerName}`,
2364
- `Network: ${status.network}`,
2365
- `Status: ${status.running ? "running" : "stopped"}`,
2794
+ `Status: \u2713 running`,
2366
2795
  "",
2367
- "RPC:",
2368
- ` gRPC: 127.0.0.1:${status.ports.rpc}`,
2369
- ` Borsh: 127.0.0.1:${status.ports.borshRpc}`,
2370
- ` JSON RPC: 127.0.0.1:${status.ports.jsonRpc}`,
2796
+ "RPC Readiness:",
2797
+ ` gRPC: ${status.transports.grpc.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.rpc})`,
2798
+ ` Borsh: ${status.transports.borsh.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.borshRpc})`,
2799
+ ` JSON RPC: ${status.transports.json.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.jsonRpc})`,
2371
2800
  "",
2372
- "Data:",
2801
+ "Data Directory:",
2373
2802
  ` ${status.dataDir}`
2374
2803
  ];
2375
2804
  return {
@@ -2386,50 +2815,212 @@ async function runNodeStatus(input) {
2386
2815
  const lines = [
2387
2816
  "Kaspa node status",
2388
2817
  "",
2389
- "Backend: Docker",
2390
- `Container: ${status.containerName}`,
2391
- `Network: ${status.network}`,
2392
- `Status: ${status.running ? "running" : "stopped"}`,
2818
+ `Backend: Docker`,
2819
+ `Status: ${status.running ? "\u2713 running" : "\u2717 stopped"}`,
2820
+ "",
2821
+ "RPC Readiness:",
2822
+ ` gRPC: ${status.transports.grpc.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.rpc})`,
2823
+ ` Borsh: ${status.transports.borsh.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.borshRpc})`,
2824
+ ` JSON RPC: ${status.transports.json.ready ? "\u2713 ready" : "\u2717 not ready"} (port ${status.ports.jsonRpc})`,
2393
2825
  "",
2394
- "RPC:",
2395
- ` gRPC: 127.0.0.1:${status.ports.rpc}`,
2396
- ` Borsh: 127.0.0.1:${status.ports.borshRpc}`,
2397
- ` JSON RPC: 127.0.0.1:${status.ports.jsonRpc}`
2826
+ "Node Information:",
2827
+ ` Container: ${status.containerName}`,
2828
+ ` Image: ${status.image}`,
2829
+ ` Network: ${status.network}`,
2830
+ ` Data Dir: ${status.dataDir}`
2398
2831
  ];
2832
+ if (status.lastError && !status.rpcReady) {
2833
+ lines.push("", "Last Error:", ` ${status.lastError}`);
2834
+ }
2399
2835
  return {
2400
2836
  status,
2401
2837
  formatted: lines.join("\n")
2402
2838
  };
2403
2839
  }
2404
2840
 
2841
+ // src/runners/node-stop-runner.ts
2842
+ import { DockerKaspadRunner as DockerKaspadRunner3 } from "@hardkas/node-runner";
2843
+ async function runNodeStop(input) {
2844
+ const runner = new DockerKaspadRunner3(input.containerName ? { containerName: input.containerName } : {});
2845
+ return runner.stop();
2846
+ }
2847
+
2848
+ // src/runners/node-restart-runner.ts
2849
+ import { DockerKaspadRunner as DockerKaspadRunner4 } from "@hardkas/node-runner";
2850
+ async function runNodeRestart(input) {
2851
+ const runner = new DockerKaspadRunner4({
2852
+ ...input.containerName ? { containerName: input.containerName } : {},
2853
+ ...input.image ? { image: input.image } : {}
2854
+ });
2855
+ const status = await runner.restart();
2856
+ return {
2857
+ status,
2858
+ formatted: `Kaspa node restarted (Container: ${status.containerName})`
2859
+ };
2860
+ }
2861
+
2862
+ // src/runners/node-reset-runner.ts
2863
+ import { DockerKaspadRunner as DockerKaspadRunner5 } from "@hardkas/node-runner";
2864
+ async function runNodeReset(input) {
2865
+ const runner = new DockerKaspadRunner5(input.containerName ? { containerName: input.containerName } : {});
2866
+ const status = await runner.reset({ removeData: input.removeData !== false });
2867
+ return {
2868
+ status,
2869
+ formatted: `Kaspa node reset complete. Data removed: ${input.removeData !== false}. Node is currently ${status.running ? "running" : "stopped"}.`
2870
+ };
2871
+ }
2872
+
2873
+ // src/runners/node-logs-runner.ts
2874
+ import { DockerKaspadRunner as DockerKaspadRunner6 } from "@hardkas/node-runner";
2875
+ async function runNodeLogs(input) {
2876
+ const runner = new DockerKaspadRunner6(input.containerName ? { containerName: input.containerName } : {});
2877
+ return await runner.logs(input.tail ? { tail: input.tail } : {}) ?? "";
2878
+ }
2879
+
2405
2880
  // src/commands/node.ts
2406
2881
  function registerNodeCommands(program) {
2407
2882
  const nodeCmd = program.command("node").description("Kaspa node management (Docker)");
2408
- nodeCmd.command("start").description("Start local node").option("--image <image>", "Docker image").action(async (options) => {
2883
+ nodeCmd.command("start").description(`Start local node ${UI.maturity("stable")}`).option("--image <image>", "Docker image").option("--allow-floating-image", "Allow using a floating tag like 'latest' without warning", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
2884
+ const { withLock: withLock2 } = await import("@hardkas/core");
2885
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
2409
2886
  try {
2410
- const result = await runNodeStart(options);
2411
- console.log(result.formatted);
2887
+ await withLock2({
2888
+ rootDir: process.cwd(),
2889
+ name: "node",
2890
+ command: "hardkas node start",
2891
+ wait: options.waitLock,
2892
+ timeoutMs: parseInt(options.lockTimeout)
2893
+ }, async () => {
2894
+ UI.info("Starting Kaspa node (Docker)...");
2895
+ const result = await runNodeStart(options);
2896
+ console.log(result.formatted);
2897
+ });
2412
2898
  } catch (e) {
2413
- handleError(e);
2899
+ handleLockError2(e);
2414
2900
  }
2415
2901
  });
2416
- nodeCmd.command("status").description("Check node status").action(async () => {
2902
+ nodeCmd.command("stop").description(`Stop local node ${UI.maturity("stable")}`).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
2903
+ const { withLock: withLock2 } = await import("@hardkas/core");
2904
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
2417
2905
  try {
2418
- const result = await runNodeStatus({});
2419
- console.log(result.formatted);
2906
+ await withLock2({
2907
+ rootDir: process.cwd(),
2908
+ name: "node",
2909
+ command: "hardkas node stop",
2910
+ wait: options.waitLock,
2911
+ timeoutMs: parseInt(options.lockTimeout)
2912
+ }, async () => {
2913
+ const result = await runNodeStop({});
2914
+ UI.success(`Node stopped (Container: ${result.containerName})`);
2915
+ });
2420
2916
  } catch (e) {
2421
- handleError(e);
2917
+ handleLockError2(e);
2422
2918
  }
2423
2919
  });
2424
- }
2425
-
2426
- // src/commands/config.ts
2427
- function registerConfigCommands(program) {
2428
- const configCmd = program.command("config").description("Manage HardKAS configuration");
2920
+ nodeCmd.command("restart").description(`Restart local node ${UI.maturity("stable")}`).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
2921
+ const { withLock: withLock2 } = await import("@hardkas/core");
2922
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
2923
+ try {
2924
+ await withLock2({
2925
+ rootDir: process.cwd(),
2926
+ name: "node",
2927
+ command: "hardkas node restart",
2928
+ wait: options.waitLock,
2929
+ timeoutMs: parseInt(options.lockTimeout)
2930
+ }, async () => {
2931
+ const result = await runNodeRestart({});
2932
+ console.log(result.formatted);
2933
+ });
2934
+ } catch (e) {
2935
+ handleLockError2(e);
2936
+ }
2937
+ });
2938
+ 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).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
2939
+ const { withLock: withLock2 } = await import("@hardkas/core");
2940
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
2941
+ try {
2942
+ await withLock2({
2943
+ rootDir: process.cwd(),
2944
+ name: "node",
2945
+ command: "hardkas node reset",
2946
+ wait: options.waitLock,
2947
+ timeoutMs: parseInt(options.lockTimeout)
2948
+ }, async () => {
2949
+ if (!options.yes) {
2950
+ const confirmed = await UI.confirm("This will delete all local chain data. Are you sure?");
2951
+ if (!confirmed) {
2952
+ console.log(" Aborted.");
2953
+ return;
2954
+ }
2955
+ }
2956
+ const result = await runNodeReset({ removeData: true });
2957
+ UI.success(result.formatted);
2958
+ if (options.start) {
2959
+ UI.info("Starting node...");
2960
+ const startResult = await runNodeStart({});
2961
+ console.log(startResult.formatted);
2962
+ }
2963
+ });
2964
+ } catch (e) {
2965
+ handleLockError2(e);
2966
+ }
2967
+ });
2968
+ nodeCmd.command("status").description("Check node status").option("--json", "Return status in JSON format", false).action(async (options) => {
2969
+ try {
2970
+ const result = await runNodeStatus({});
2971
+ if (options.json) {
2972
+ console.log(JSON.stringify({
2973
+ schema: "hardkas.nodeStatus.v1",
2974
+ docker: {
2975
+ available: true,
2976
+ // If we reached here, docker CLI at least worked in runner
2977
+ daemonReady: result.status.statusText !== "not-found"
2978
+ },
2979
+ container: {
2980
+ exists: result.status.statusText !== "not-found",
2981
+ running: result.status.running,
2982
+ name: result.status.containerName,
2983
+ image: result.status.image
2984
+ },
2985
+ rpc: {
2986
+ url: result.status.rpcUrl,
2987
+ ready: result.status.rpcReady,
2988
+ lastError: result.status.lastError,
2989
+ transports: result.status.transports
2990
+ },
2991
+ ports: [
2992
+ { protocol: "grpc", host: "127.0.0.1", port: result.status.ports.rpc, ready: result.status.transports.grpc.ready },
2993
+ { protocol: "borsh", host: "127.0.0.1", port: result.status.ports.borshRpc, ready: result.status.transports.borsh.ready },
2994
+ { protocol: "json", host: "127.0.0.1", port: result.status.ports.jsonRpc, ready: result.status.transports.json.ready }
2995
+ ],
2996
+ dataDir: result.status.dataDir
2997
+ }, null, 2));
2998
+ } else {
2999
+ console.log(result.formatted);
3000
+ }
3001
+ } catch (e) {
3002
+ handleError(e);
3003
+ }
3004
+ });
3005
+ 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) => {
3006
+ try {
3007
+ const result = await runNodeLogs({
3008
+ tail: parseInt(options.tail, 10)
3009
+ });
3010
+ if (result) console.log(result);
3011
+ } catch (e) {
3012
+ handleError(e);
3013
+ }
3014
+ });
3015
+ }
3016
+
3017
+ // src/commands/config.ts
3018
+ function registerConfigCommands(program) {
3019
+ const configCmd = program.command("config").description("Manage HardKAS configuration");
2429
3020
  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: loadHardkasConfig3 } = await import("@hardkas/config");
3021
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
2431
3022
  try {
2432
- const loaded = await loadHardkasConfig3(options.config ? { configPath: options.config } : {});
3023
+ const loaded = await loadHardkasConfig15(options.config ? { configPath: options.config } : {});
2433
3024
  if (options.json) {
2434
3025
  console.log(JSON.stringify(loaded, null, 2));
2435
3026
  return;
@@ -2459,23 +3050,23 @@ function registerConfigCommands(program) {
2459
3050
  }
2460
3051
 
2461
3052
  // src/commands/misc.ts
2462
- import { loadOrCreateLocalnetState as loadOrCreateLocalnetState2 } from "@hardkas/localnet";
3053
+ import { loadOrCreateLocalnetState as loadOrCreateLocalnetState3 } from "@hardkas/localnet";
2463
3054
 
2464
3055
  // src/runners/example-list-runner.ts
2465
3056
  import fs7 from "fs/promises";
2466
- import path8 from "path";
3057
+ import path9 from "path";
2467
3058
  async function runExampleList() {
2468
3059
  UI.box("HardKAS", "Example Registry");
2469
3060
  try {
2470
3061
  let currentDir = process.cwd();
2471
- let registryPath = path8.join(currentDir, "examples", "registry.json");
3062
+ let registryPath = path9.join(currentDir, "examples", "registry.json");
2472
3063
  for (let i = 0; i < 3; i++) {
2473
3064
  try {
2474
3065
  await fs7.access(registryPath);
2475
3066
  break;
2476
3067
  } catch {
2477
- currentDir = path8.dirname(currentDir);
2478
- registryPath = path8.join(currentDir, "examples", "registry.json");
3068
+ currentDir = path9.dirname(currentDir);
3069
+ registryPath = path9.join(currentDir, "examples", "registry.json");
2479
3070
  }
2480
3071
  }
2481
3072
  const data = await fs7.readFile(registryPath, "utf-8");
@@ -2495,19 +3086,19 @@ async function runExampleList() {
2495
3086
 
2496
3087
  // src/runners/example-run-runner.ts
2497
3088
  import fs8 from "fs/promises";
2498
- import path9 from "path";
3089
+ import path10 from "path";
2499
3090
  import { spawn } from "child_process";
2500
3091
  async function runExampleRun(id) {
2501
3092
  try {
2502
3093
  let currentDir = process.cwd();
2503
- let registryPath = path9.join(currentDir, "examples", "registry.json");
3094
+ let registryPath = path10.join(currentDir, "examples", "registry.json");
2504
3095
  for (let i = 0; i < 3; i++) {
2505
3096
  try {
2506
3097
  await fs8.access(registryPath);
2507
3098
  break;
2508
3099
  } catch {
2509
- currentDir = path9.dirname(currentDir);
2510
- registryPath = path9.join(currentDir, "examples", "registry.json");
3100
+ currentDir = path10.dirname(currentDir);
3101
+ registryPath = path10.join(currentDir, "examples", "registry.json");
2511
3102
  }
2512
3103
  }
2513
3104
  const data = await fs8.readFile(registryPath, "utf-8");
@@ -2528,10 +3119,10 @@ async function runExampleRun(id) {
2528
3119
  shell: true
2529
3120
  // Required for pnpm/npx on Windows
2530
3121
  });
2531
- return new Promise((resolve, reject) => {
3122
+ return new Promise((resolve3, reject) => {
2532
3123
  child.on("close", (code) => {
2533
3124
  if (code === 0) {
2534
- resolve();
3125
+ resolve3();
2535
3126
  } else {
2536
3127
  reject(new Error(`Example execution failed with exit code ${code}`));
2537
3128
  }
@@ -2566,7 +3157,7 @@ function registerMiscCommands(program) {
2566
3157
  });
2567
3158
  program.command("dev").description("Start development environment").option("--mode <mode>", "simulated or node", "simulated").action(async (options) => {
2568
3159
  if (options.mode === "simulated") {
2569
- const state = await loadOrCreateLocalnetState2();
3160
+ const state = await loadOrCreateLocalnetState3();
2570
3161
  UI.success("Local HardKAS devnet (simulated) is ready.");
2571
3162
  UI.info(`Network: ${state.networkId}`);
2572
3163
  UI.info(`Accounts: ${state.accounts.length}`);
@@ -2577,13 +3168,226 @@ function registerMiscCommands(program) {
2577
3168
  }
2578
3169
 
2579
3170
  // src/commands/query.ts
3171
+ import pc from "picocolors";
2580
3172
  function registerQueryCommands(program) {
2581
3173
  const queryCmd = program.command("query").description("Query and introspect HardKAS artifacts, lineage, and workflows");
2582
- const artifactsCmd = queryCmd.command("artifacts").description("Query artifact store");
3174
+ const artifactsCmd = queryCmd.command("artifacts").description(`Query artifact store ${UI.maturity("stable")}`);
3175
+ const storeCmd = queryCmd.command("store").description(`Manage query store index ${UI.maturity("stable")}`);
3176
+ storeCmd.command("doctor").description("Integrity and freshness check of the query store index").option("--migrate", "Apply pending migrations if found", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
3177
+ const { withLock: withLock2 } = await import("@hardkas/core");
3178
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
3179
+ try {
3180
+ const action = async () => {
3181
+ const engine = await getQueryEngine();
3182
+ if (options.migrate) {
3183
+ console.log("\n Checking and applying migrations...");
3184
+ await engine.backend.migrate();
3185
+ }
3186
+ const report = await engine.backend.doctor();
3187
+ console.log("\n \u2550\u2550\u2550 Query Store Doctor \u2550\u2550\u2550\n");
3188
+ console.log(` Backend: ${engine.backend.kind()}`);
3189
+ console.log(` Overall: ${report.ok ? pc.green("\u2713 HEALTHY") : pc.red("\u2717 STALE / ISSUES")}`);
3190
+ console.log(` Last Indexed: ${report.lastIndexedAt || "never"}`);
3191
+ if (report.storeIssues && report.storeIssues.length > 0) {
3192
+ console.log("\n Store Issues:");
3193
+ for (const issue of report.storeIssues) {
3194
+ const icon = issue.severity === "error" ? pc.red("\u2717") : pc.yellow("\u26A0");
3195
+ console.log(` ${icon} [${issue.code}] ${issue.message}`);
3196
+ if (issue.suggestion) console.log(` Suggestion: ${issue.suggestion}`);
3197
+ }
3198
+ }
3199
+ if (report.corruptedFiles?.length > 0) {
3200
+ console.log("\n Corrupted Files:");
3201
+ for (const f of report.corruptedFiles) console.log(` ${pc.red("\u2717")} ${f}`);
3202
+ }
3203
+ if (!report.ok) {
3204
+ const cmd = report.storeIssues?.some((i) => i.code.includes("MIGRATION")) ? "migrate" : "rebuild";
3205
+ console.log(`
3206
+ ${UI.warning("Recommendation:")} Run 'hardkas query store ${cmd}' to fix issues.
3207
+ `);
3208
+ process.exitCode = 1;
3209
+ } else {
3210
+ console.log("\n \u2713 Everything looks good.\n");
3211
+ }
3212
+ };
3213
+ if (options.migrate) {
3214
+ await withLock2({
3215
+ rootDir: process.cwd(),
3216
+ name: "query-store",
3217
+ command: "hardkas query store doctor --migrate",
3218
+ wait: options.waitLock,
3219
+ timeoutMs: parseInt(options.lockTimeout)
3220
+ }, action);
3221
+ } else {
3222
+ await action();
3223
+ }
3224
+ } catch (e) {
3225
+ handleLockError2(e);
3226
+ process.exitCode = 1;
3227
+ }
3228
+ });
3229
+ storeCmd.command("migrate").description("Apply pending schema migrations to the query store").option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").action(async (options) => {
3230
+ const { withLock: withLock2 } = await import("@hardkas/core");
3231
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
3232
+ try {
3233
+ await withLock2({
3234
+ rootDir: process.cwd(),
3235
+ name: "query-store",
3236
+ command: "hardkas query store migrate",
3237
+ wait: options.waitLock,
3238
+ timeoutMs: parseInt(options.lockTimeout)
3239
+ }, async () => {
3240
+ console.log("\n Checking for pending migrations...");
3241
+ const engine = await getQueryEngine();
3242
+ const result = await engine.backend.migrate();
3243
+ if (result.applied > 0) {
3244
+ UI.success(`Applied ${result.applied} migration(s). Store is up to date.`);
3245
+ } else {
3246
+ UI.info("No pending migrations found. Store is already up to date.");
3247
+ }
3248
+ console.log("");
3249
+ });
3250
+ } catch (e) {
3251
+ handleLockError2(e);
3252
+ process.exitCode = 1;
3253
+ }
3254
+ });
3255
+ storeCmd.command("sync").alias("index").description("Synchronize the filesystem artifacts with the query store index").option("--strict", "Fail on any corrupted data", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
3256
+ const { withLock: withLock2 } = await import("@hardkas/core");
3257
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
3258
+ try {
3259
+ await withLock2({
3260
+ rootDir: process.cwd(),
3261
+ name: "query-store",
3262
+ command: "hardkas query store sync",
3263
+ wait: options.waitLock,
3264
+ timeoutMs: parseInt(options.lockTimeout)
3265
+ }, async () => {
3266
+ if (!options.json) console.log("\n Synchronizing query store index...");
3267
+ const engine = await getQueryEngine();
3268
+ const start = Date.now();
3269
+ const result = await engine.backend.sync({ strict: options.strict });
3270
+ if (options.json) {
3271
+ console.log(JSON.stringify(result, null, 2));
3272
+ } else {
3273
+ const elapsed = Date.now() - start;
3274
+ console.log(` \u2713 Index synchronized in ${elapsed}ms.`);
3275
+ console.log(`
3276
+ Artifacts: ${result.artifacts.indexed}/${result.artifacts.scanned} indexed (${result.artifacts.corrupted} corrupted)`);
3277
+ console.log(` Events: ${result.events.indexed}/${result.events.scanned} indexed (${result.events.corrupted} corrupted)`);
3278
+ if (result.issues && result.issues.length > 0) {
3279
+ console.log("\n Issues Found:");
3280
+ for (const issue of result.issues.slice(0, 10)) {
3281
+ console.log(` ${issue.severity === "error" ? pc.red("\u2717") : pc.yellow("\u26A0")} [${issue.code}] ${issue.message}`);
3282
+ if (issue.path) console.log(` At: ${issue.path}${issue.lineNumber ? ":" + issue.lineNumber : ""}`);
3283
+ }
3284
+ if (result.issues.length > 10) console.log(` ... and ${result.issues.length - 10} more.`);
3285
+ }
3286
+ if (!result.ok) {
3287
+ console.log(`
3288
+ ${UI.error("Synchronization encountered corruption.")} Use --strict for fail-fast behavior.`);
3289
+ process.exitCode = 1;
3290
+ }
3291
+ console.log("");
3292
+ }
3293
+ });
3294
+ } catch (e) {
3295
+ handleLockError2(e);
3296
+ process.exitCode = 1;
3297
+ }
3298
+ });
3299
+ storeCmd.command("rebuild").description("Force a complete rebuild of the query store index").option("--strict", "Fail on any corrupted data", false).option("--wait-lock", "Wait for workspace lock if held", false).option("--lock-timeout <ms>", "Lock wait timeout in ms", "30000").option("--json", "Output as JSON", false).action(async (options) => {
3300
+ const { withLock: withLock2 } = await import("@hardkas/core");
3301
+ const { handleLockError: handleLockError2 } = await import("./ui-SUYOHGGP.js");
3302
+ try {
3303
+ await withLock2({
3304
+ rootDir: process.cwd(),
3305
+ name: "query-store",
3306
+ command: "hardkas query store rebuild",
3307
+ wait: options.waitLock,
3308
+ timeoutMs: parseInt(options.lockTimeout)
3309
+ }, async () => {
3310
+ if (!options.json) console.log("\n Rebuilding query store index...");
3311
+ const engine = await getQueryEngine();
3312
+ const start = Date.now();
3313
+ const result = await engine.backend.rebuild({ strict: options.strict });
3314
+ if (options.json) {
3315
+ console.log(JSON.stringify(result, null, 2));
3316
+ } else {
3317
+ const elapsed = Date.now() - start;
3318
+ console.log(` \u2713 Index rebuilt successfully in ${elapsed}ms.`);
3319
+ console.log(`
3320
+ Artifacts: ${result.artifacts.indexed}/${result.artifacts.scanned} indexed (${result.artifacts.corrupted} corrupted)`);
3321
+ console.log(` Events: ${result.events.indexed}/${result.events.scanned} indexed (${result.events.corrupted} corrupted)`);
3322
+ if (result.issues && result.issues.length > 0) {
3323
+ console.log("\n Corruption Issues:");
3324
+ for (const issue of result.issues.slice(0, 10)) {
3325
+ console.log(` ${issue.severity === "error" ? pc.red("\u2717") : pc.yellow("\u26A0")} [${issue.code}] ${issue.message}`);
3326
+ if (issue.path) console.log(` At: ${issue.path}${issue.lineNumber ? ":" + issue.lineNumber : ""}`);
3327
+ }
3328
+ if (result.issues.length > 10) console.log(` ... and ${result.issues.length - 10} more.`);
3329
+ }
3330
+ if (!result.ok) {
3331
+ console.log(`
3332
+ ${UI.error("Rebuild failed or encountered corruption.")} Use --strict for fail-fast behavior.`);
3333
+ process.exitCode = 1;
3334
+ }
3335
+ console.log("");
3336
+ }
3337
+ });
3338
+ } catch (e) {
3339
+ handleLockError2(e);
3340
+ process.exitCode = 1;
3341
+ }
3342
+ });
3343
+ storeCmd.command("sql <query>").description("Run a raw SQL query against the query store").option("--json", "Output as JSON", false).action(async (query, options) => {
3344
+ try {
3345
+ const engine = await getQueryEngine();
3346
+ if (typeof engine.backend.executeRawSql !== "function") {
3347
+ throw new Error("Raw SQL execution not supported by current backend");
3348
+ }
3349
+ const result = await engine.backend.executeRawSql(query);
3350
+ if (options.json) {
3351
+ console.log(JSON.stringify(result, null, 2));
3352
+ } else {
3353
+ if (result.length === 0) {
3354
+ console.log("\n No results.\n");
3355
+ } else {
3356
+ console.table(result);
3357
+ }
3358
+ }
3359
+ } catch (e) {
3360
+ handleError(e);
3361
+ process.exitCode = 1;
3362
+ }
3363
+ });
3364
+ storeCmd.command("export").description("Export logical store state to JSON").option("--output <path>", "Output file path").action(async (options) => {
3365
+ try {
3366
+ const { HardkasStore } = await import("@hardkas/query-store");
3367
+ const store = new HardkasStore();
3368
+ store.connect({ autoMigrate: true });
3369
+ const db = store.getDatabase();
3370
+ const artifacts = db.prepare("SELECT * FROM artifacts ORDER BY artifact_id ASC").all();
3371
+ const events = db.prepare("SELECT * FROM events ORDER BY event_id ASC").all();
3372
+ const dump = { artifacts, events };
3373
+ const json = JSON.stringify(dump, null, 2);
3374
+ if (options.output) {
3375
+ const fs12 = await import("fs");
3376
+ fs12.writeFileSync(options.output, json);
3377
+ UI.success(`Store exported to ${options.output}`);
3378
+ } else {
3379
+ console.log(json);
3380
+ }
3381
+ store.disconnect();
3382
+ } catch (e) {
3383
+ handleError(e);
3384
+ process.exitCode = 1;
3385
+ }
3386
+ });
2583
3387
  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
3388
  try {
2585
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2586
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3389
+ const { createQueryRequest } = await import("@hardkas/query");
3390
+ const engine = await getQueryEngine();
2587
3391
  const filters = [];
2588
3392
  if (options.schema) filters.push({ field: "schema", op: "eq", value: `hardkas.${options.schema}` });
2589
3393
  if (options.network) filters.push({ field: "networkId", op: "eq", value: options.network });
@@ -2617,8 +3421,8 @@ function registerQueryCommands(program) {
2617
3421
  });
2618
3422
  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
3423
  try {
2620
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2621
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3424
+ const { createQueryRequest } = await import("@hardkas/query");
3425
+ const engine = await getQueryEngine();
2622
3426
  const request = createQueryRequest({
2623
3427
  domain: "artifacts",
2624
3428
  op: "inspect",
@@ -2639,8 +3443,8 @@ function registerQueryCommands(program) {
2639
3443
  });
2640
3444
  artifactsCmd.command("diff <left> <right>").description("Semantic diff between two artifacts").option("--json", "Output as JSON", false).action(async (left, right, options) => {
2641
3445
  try {
2642
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2643
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3446
+ const { createQueryRequest } = await import("@hardkas/query");
3447
+ const engine = await getQueryEngine();
2644
3448
  const request = createQueryRequest({
2645
3449
  domain: "artifacts",
2646
3450
  op: "diff",
@@ -2658,11 +3462,11 @@ function registerQueryCommands(program) {
2658
3462
  process.exitCode = 1;
2659
3463
  }
2660
3464
  });
2661
- const lineageCmd = queryCmd.command("lineage").description("Traverse artifact lineage");
3465
+ const lineageCmd = queryCmd.command("lineage").description(`Traverse artifact lineage ${UI.maturity("stable")}`);
2662
3466
  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
3467
  try {
2664
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2665
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3468
+ const { createQueryRequest } = await import("@hardkas/query");
3469
+ const engine = await getQueryEngine();
2666
3470
  const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
2667
3471
  const request = createQueryRequest({
2668
3472
  domain: "lineage",
@@ -2684,8 +3488,8 @@ function registerQueryCommands(program) {
2684
3488
  });
2685
3489
  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
3490
  try {
2687
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2688
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3491
+ const { createQueryRequest } = await import("@hardkas/query");
3492
+ const engine = await getQueryEngine();
2689
3493
  const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
2690
3494
  const request = createQueryRequest({
2691
3495
  domain: "lineage",
@@ -2707,8 +3511,8 @@ function registerQueryCommands(program) {
2707
3511
  });
2708
3512
  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
3513
  try {
2710
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2711
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3514
+ const { createQueryRequest } = await import("@hardkas/query");
3515
+ const engine = await getQueryEngine();
2712
3516
  const request = createQueryRequest({
2713
3517
  domain: "lineage",
2714
3518
  op: "orphans",
@@ -2726,11 +3530,11 @@ function registerQueryCommands(program) {
2726
3530
  process.exitCode = 1;
2727
3531
  }
2728
3532
  });
2729
- const replayCmd = queryCmd.command("replay").description("Inspect replay history and divergence");
3533
+ const replayCmd = queryCmd.command("replay").description(`Inspect replay history and divergence ${UI.maturity("stable")}`);
2730
3534
  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
3535
  try {
2732
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2733
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3536
+ const { createQueryRequest } = await import("@hardkas/query");
3537
+ const engine = await getQueryEngine();
2734
3538
  const filters = [];
2735
3539
  if (options.status) filters.push({ field: "status", op: "eq", value: options.status });
2736
3540
  const request = createQueryRequest({ domain: "replay", op: "list", filters, limit: parseInt(options.limit, 10) });
@@ -2748,8 +3552,8 @@ function registerQueryCommands(program) {
2748
3552
  });
2749
3553
  replayCmd.command("summary <txId>").description("Detailed receipt + trace summary for a transaction").option("--json", "Output as JSON", false).action(async (txId, options) => {
2750
3554
  try {
2751
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2752
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3555
+ const { createQueryRequest } = await import("@hardkas/query");
3556
+ const engine = await getQueryEngine();
2753
3557
  const request = createQueryRequest({ domain: "replay", op: "summary", params: { txId } });
2754
3558
  const result = await engine.execute(request);
2755
3559
  if (options.json) {
@@ -2765,8 +3569,8 @@ function registerQueryCommands(program) {
2765
3569
  });
2766
3570
  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
3571
  try {
2768
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2769
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3572
+ const { createQueryRequest } = await import("@hardkas/query");
3573
+ const engine = await getQueryEngine();
2770
3574
  const request = createQueryRequest({
2771
3575
  domain: "replay",
2772
3576
  op: "divergences",
@@ -2786,8 +3590,8 @@ function registerQueryCommands(program) {
2786
3590
  });
2787
3591
  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
3592
  try {
2789
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2790
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3593
+ const { createQueryRequest } = await import("@hardkas/query");
3594
+ const engine = await getQueryEngine();
2791
3595
  const request = createQueryRequest({
2792
3596
  domain: "replay",
2793
3597
  op: "invariants",
@@ -2806,11 +3610,11 @@ function registerQueryCommands(program) {
2806
3610
  process.exitCode = 1;
2807
3611
  }
2808
3612
  });
2809
- const dagCmd = queryCmd.command("dag").description("Query simulated DAG state (deterministic-light-model, NOT GHOSTDAG)");
3613
+ const dagCmd = queryCmd.command("dag").description(`Query simulated DAG state ${UI.maturity("research")}`);
2810
3614
  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
3615
  try {
2812
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2813
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3616
+ const { createQueryRequest } = await import("@hardkas/query");
3617
+ const engine = await getQueryEngine();
2814
3618
  const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
2815
3619
  const request = createQueryRequest({ domain: "dag", op: "conflicts", explain });
2816
3620
  const result = await engine.execute(request);
@@ -2827,8 +3631,8 @@ function registerQueryCommands(program) {
2827
3631
  });
2828
3632
  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
3633
  try {
2830
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2831
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3634
+ const { createQueryRequest } = await import("@hardkas/query");
3635
+ const engine = await getQueryEngine();
2832
3636
  const explain = options.explain === true ? "brief" : options.explain || false;
2833
3637
  const request = createQueryRequest({ domain: "dag", op: "displaced", explain });
2834
3638
  const result = await engine.execute(request);
@@ -2845,8 +3649,8 @@ function registerQueryCommands(program) {
2845
3649
  });
2846
3650
  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
3651
  try {
2848
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2849
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3652
+ const { createQueryRequest } = await import("@hardkas/query");
3653
+ const engine = await getQueryEngine();
2850
3654
  const explain = options.why ? "full" : options.explain === true ? "brief" : options.explain || false;
2851
3655
  const request = createQueryRequest({ domain: "dag", op: "history", params: { txId }, explain });
2852
3656
  const result = await engine.execute(request);
@@ -2863,8 +3667,8 @@ function registerQueryCommands(program) {
2863
3667
  });
2864
3668
  dagCmd.command("sink-path").description("Show current selected path from genesis to sink").option("--json", "Output as JSON", false).action(async (options) => {
2865
3669
  try {
2866
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2867
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3670
+ const { createQueryRequest } = await import("@hardkas/query");
3671
+ const engine = await getQueryEngine();
2868
3672
  const request = createQueryRequest({ domain: "dag", op: "sink-path" });
2869
3673
  const result = await engine.execute(request);
2870
3674
  if (options.json) {
@@ -2880,8 +3684,8 @@ function registerQueryCommands(program) {
2880
3684
  });
2881
3685
  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
3686
  try {
2883
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2884
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3687
+ const { createQueryRequest } = await import("@hardkas/query");
3688
+ const engine = await getQueryEngine();
2885
3689
  const explain = options.explain === true ? "brief" : options.explain || false;
2886
3690
  const request = createQueryRequest({ domain: "dag", op: "anomalies", explain });
2887
3691
  const result = await engine.execute(request);
@@ -2898,8 +3702,8 @@ function registerQueryCommands(program) {
2898
3702
  });
2899
3703
  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
3704
  try {
2901
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2902
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3705
+ const { createQueryRequest } = await import("@hardkas/query");
3706
+ const engine = await getQueryEngine();
2903
3707
  const filters = [];
2904
3708
  if (options.domain) filters.push({ field: "domain", op: "eq", value: options.domain });
2905
3709
  if (options.kind) filters.push({ field: "kind", op: "eq", value: options.kind });
@@ -2926,10 +3730,10 @@ function registerQueryCommands(program) {
2926
3730
  process.exitCode = 1;
2927
3731
  }
2928
3732
  });
2929
- queryCmd.command("tx <txId>").description("Aggregate all data for a transaction").option("--json", "Output as deterministic JSON", false).option("--explain [level]", "Attach explain metadata (brief|full)").action(async (txId, options) => {
3733
+ 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
3734
  try {
2931
- const { QueryEngine, createQueryRequest } = await import("@hardkas/query");
2932
- const engine = new QueryEngine({ artifactDir: process.cwd() });
3735
+ const { createQueryRequest } = await import("@hardkas/query");
3736
+ const engine = await getQueryEngine();
2933
3737
  const request = createQueryRequest({
2934
3738
  domain: "tx",
2935
3739
  op: "aggregate",
@@ -2960,9 +3764,12 @@ function printArtifactList(result) {
2960
3764
  }
2961
3765
  console.log(`
2962
3766
  queryHash: ${result.queryHash.slice(0, 16)}...`);
2963
- console.log(` ${result.annotations.executionMs}ms | ${result.annotations.filesScanned ?? 0} files scanned
3767
+ const backend = result.annotations.backendUsed || "unknown";
3768
+ const freshness = result.annotations.freshness ? ` | ${result.annotations.freshness}` : "";
3769
+ console.log(` ${result.annotations.executionMs}ms | backend:${backend}${freshness} | ${result.annotations.filesScanned ?? 0} files scanned
2964
3770
  `);
2965
- if (result.explain) printExplainChains(result.explain);
3771
+ printExplain(result.explain);
3772
+ printWhy(result.why);
2966
3773
  }
2967
3774
  function printInspectResult(result) {
2968
3775
  const item = result.items[0];
@@ -2988,7 +3795,8 @@ function printInspectResult(result) {
2988
3795
  for (const err of item.integrity.errors) console.log(` \u2717 ${err}`);
2989
3796
  }
2990
3797
  console.log("");
2991
- if (result.explain) printExplainChains(result.explain);
3798
+ printExplain(result.explain);
3799
+ printWhy(result.why);
2992
3800
  }
2993
3801
  function printDiffResult(result) {
2994
3802
  const diff = result.items[0];
@@ -3029,7 +3837,8 @@ function printLineageChain(result) {
3029
3837
  console.log(`${prefix} ${node.schema} [${node.contentHash.slice(0, 12)}...] ${node.networkId}/${node.mode}`);
3030
3838
  }
3031
3839
  console.log("");
3032
- if (result.explain) printExplainChains(result.explain);
3840
+ printExplain(result.explain);
3841
+ printWhy(result.why);
3033
3842
  }
3034
3843
  function printTransitions(result) {
3035
3844
  console.log(`
@@ -3040,7 +3849,8 @@ function printTransitions(result) {
3040
3849
  console.log(` ${marker} ${t.from.schema} \u2192 ${t.to.schema} [${t.rule}]`);
3041
3850
  }
3042
3851
  console.log("");
3043
- if (result.explain) printExplainChains(result.explain);
3852
+ printExplain(result.explain);
3853
+ printWhy(result.why);
3044
3854
  }
3045
3855
  function printOrphans(result) {
3046
3856
  if (result.total === 0) {
@@ -3056,7 +3866,8 @@ function printOrphans(result) {
3056
3866
  console.log(` Reason: ${o.reason}
3057
3867
  `);
3058
3868
  }
3059
- if (result.explain) printExplainChains(result.explain);
3869
+ printExplain(result.explain);
3870
+ printWhy(result.why);
3060
3871
  }
3061
3872
  function printReplayList(result) {
3062
3873
  console.log(`
@@ -3101,7 +3912,8 @@ function printDivergences(result) {
3101
3912
  console.log(` Actual: ${d.actual.slice(0, 60)}
3102
3913
  `);
3103
3914
  }
3104
- if (result.explain) printExplainChains(result.explain);
3915
+ printExplain(result.explain);
3916
+ printWhy(result.why);
3105
3917
  }
3106
3918
  function printInvariants(result) {
3107
3919
  const inv = result.items[0];
@@ -3121,7 +3933,8 @@ function printInvariants(result) {
3121
3933
  for (const i of inv.issues) console.log(` \u2717 ${i}`);
3122
3934
  }
3123
3935
  console.log("");
3124
- if (result.explain) printExplainChains(result.explain);
3936
+ printExplain(result.explain);
3937
+ printWhy(result.why);
3125
3938
  }
3126
3939
  function printDagConflicts(result) {
3127
3940
  console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
@@ -3137,7 +3950,8 @@ function printDagConflicts(result) {
3137
3950
  for (const l of c.loserTxIds) console.log(` \u2514\u2500 LOSER: ${l.slice(0, 24)}...`);
3138
3951
  console.log("");
3139
3952
  }
3140
- if (result.explain) printExplainChains(result.explain);
3953
+ printExplain(result.explain);
3954
+ printWhy(result.why);
3141
3955
  }
3142
3956
  function printDagDisplaced(result) {
3143
3957
  console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
@@ -3153,7 +3967,8 @@ function printDagDisplaced(result) {
3153
3967
  console.log(` ${d.reason}
3154
3968
  `);
3155
3969
  }
3156
- if (result.explain) printExplainChains(result.explain);
3970
+ printExplain(result.explain);
3971
+ printWhy(result.why);
3157
3972
  }
3158
3973
  function printDagHistory(result) {
3159
3974
  console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
@@ -3169,7 +3984,8 @@ function printDagHistory(result) {
3169
3984
  console.log(` ${status.padEnd(10)} block:${e.blockId.slice(0, 12)}... daa:${e.daaScore} ${sinkPath}`);
3170
3985
  }
3171
3986
  console.log("");
3172
- if (result.explain) printExplainChains(result.explain);
3987
+ printExplain(result.explain);
3988
+ printWhy(result.why);
3173
3989
  }
3174
3990
  function printSinkPath(result) {
3175
3991
  console.log("\n \u26A0 DAG model: deterministic-light-model (NOT GHOSTDAG)\n");
@@ -3202,19 +4018,39 @@ function printDagAnomalies(result) {
3202
4018
  console.log(` \u2717 [${a.kind}] ${a.description}
3203
4019
  `);
3204
4020
  }
3205
- if (result.explain) printExplainChains(result.explain);
4021
+ if (result.explain) printExplain(result.explain);
3206
4022
  }
3207
- function printExplainChains(chains) {
3208
- console.log(" \u2500\u2500\u2500 Explain \u2500\u2500\u2500\n");
3209
- for (const chain of chains) {
3210
- console.log(` Q: ${chain.question}`);
3211
- for (const step of chain.steps) {
4023
+ function printExplain(explain) {
4024
+ if (!explain) return;
4025
+ console.log(" \u2500\u2500\u2500 Explain: Technical Diagnostics \u2500\u2500\u2500\n");
4026
+ console.log(` Backend: ${explain.backend}`);
4027
+ console.log(` Freshness: ${explain.freshness}`);
4028
+ console.log(` Rows Read: ${explain.rowsRead}`);
4029
+ console.log(` Files Scan: ${explain.scannedFiles}`);
4030
+ if (explain.executionPlan && explain.executionPlan.length > 0) {
4031
+ console.log(` Plan: ${explain.executionPlan.join(" \u2192 ")}`);
4032
+ }
4033
+ if (explain.warnings && explain.warnings.length > 0) {
4034
+ console.log(` Warnings:`);
4035
+ for (const w of explain.warnings) console.log(` \u26A0 ${w}`);
4036
+ }
4037
+ console.log("");
4038
+ }
4039
+ function printWhy(why) {
4040
+ if (!why || why.length === 0) return;
4041
+ console.log(" \u2500\u2500\u2500 Why: Causal Analysis \u2500\u2500\u2500\n");
4042
+ for (const block of why) {
4043
+ console.log(` Q: ${block.question}`);
4044
+ console.log(` A: ${block.answer}`);
4045
+ for (const step of block.causalChain) {
3212
4046
  console.log(` ${step.order}. ${step.assertion}`);
3213
- if (step.rule) console.log(` Rule: ${step.rule}`);
4047
+ console.log(` Evidence: ${step.evidence}`);
4048
+ if (step.rule) console.log(` Rule: ${step.rule}`);
3214
4049
  }
3215
- console.log(` \u2192 ${chain.conclusion}`);
3216
- console.log(` [model: ${chain.model}, confidence: ${chain.confidence}]
3217
- `);
4050
+ if (block.evidence && block.evidence.length > 0) {
4051
+ console.log(` Evidence Refs: ${block.evidence.map((e) => `${e.type}:${e.value.slice(0, 12)}...`).join(", ")}`);
4052
+ }
4053
+ console.log("");
3218
4054
  }
3219
4055
  }
3220
4056
  function printEventList(result) {
@@ -3229,7 +4065,8 @@ function printEventList(result) {
3229
4065
  queryHash: ${result.queryHash.slice(0, 16)}...`);
3230
4066
  console.log(` ${result.annotations.executionMs}ms
3231
4067
  `);
3232
- if (result.explain) printExplainChains(result.explain);
4068
+ printExplain(result.explain);
4069
+ printWhy(result.why);
3233
4070
  }
3234
4071
  function printTxAggregate(result) {
3235
4072
  const agg = result.items[0];
@@ -3264,32 +4101,82 @@ function printTxAggregate(result) {
3264
4101
  }
3265
4102
  }
3266
4103
  console.log("");
3267
- if (result.explain) printExplainChains(result.explain);
4104
+ if (result.explain) printExplain(result.explain);
4105
+ }
4106
+ async function getQueryEngine() {
4107
+ const { QueryEngine } = await import("@hardkas/query");
4108
+ return QueryEngine.create({
4109
+ artifactDir: process.cwd()
4110
+ });
3268
4111
  }
3269
4112
 
3270
- // src/commands/test.ts
4113
+ // src/runners/test-runner.ts
3271
4114
  import { Hardkas } from "@hardkas/sdk";
3272
- function registerTestCommands(program) {
3273
- program.command("test [files...]").description("Run HardKAS tests against localnet").option("--network <network>", "Network to test against", "simnet").action(async (files, options) => {
3274
- try {
3275
- console.log(`Starting HardKAS Test Runner...`);
3276
- console.log(`Network: ${options.network}`);
3277
- const hardkas = await Hardkas.open(".");
3278
- if (options.network === "simnet") {
3279
- console.log(`Initializing deterministic localnet...`);
3280
- await hardkas.localnet.start();
4115
+ async function runTest(options) {
4116
+ const { files, network, watch, json, reporter } = options;
4117
+ if (!json) {
4118
+ UI.header("HardKAS Test Runner");
4119
+ UI.info(`Network: ${network}`);
4120
+ }
4121
+ let hardkas;
4122
+ try {
4123
+ hardkas = await Hardkas.open(".");
4124
+ } catch (e) {
4125
+ throw new Error("Could not find a valid HardKAS project in this directory.");
4126
+ }
4127
+ const searchPatterns = files.length > 0 ? files : ["test/**/*.test.ts", "tests/**/*.test.ts"];
4128
+ if (!json) {
4129
+ UI.info(`Searching for tests: ${searchPatterns.join(", ")}`);
4130
+ }
4131
+ try {
4132
+ const { startVitest } = await import("vitest/node");
4133
+ const vitestOptions = {
4134
+ run: !watch,
4135
+ watch: !!watch,
4136
+ reporter: json ? "json" : reporter || "default",
4137
+ globals: true,
4138
+ environment: "node",
4139
+ include: searchPatterns,
4140
+ exclude: ["**/node_modules/**", "**/dist/**", "**/.hardkas/**"],
4141
+ // Injected environment variables for tests to consume
4142
+ env: {
4143
+ HARDKAS_NETWORK: network,
4144
+ HARDKAS_CWD: process.cwd()
3281
4145
  }
3282
- const targetFiles = files.length > 0 ? files : ["test/**/*.test.ts"];
3283
- console.log(`
3284
- Discovered ${targetFiles.length} test files.`);
3285
- console.log(`
3286
- [RUNNING] ${targetFiles[0] || "test/example.test.ts"}`);
3287
- console.log(` \u2713 should perform deterministic tx`);
3288
- console.log(` \u2713 should reject double spend`);
3289
- console.log(`
3290
- \u2705 2 passing (1.5s)`);
4146
+ };
4147
+ const vitest = await startVitest("test", searchPatterns, vitestOptions);
4148
+ if (!vitest) {
4149
+ throw new Error("Failed to initialize test engine.");
4150
+ }
4151
+ } catch (e) {
4152
+ const error = e;
4153
+ if (error.code === "ERR_MODULE_NOT_FOUND" || error.message?.includes("vitest")) {
4154
+ UI.warning("Vitest is not installed in this project.");
4155
+ UI.info("Run 'pnpm add -D vitest' to enable real test execution.");
4156
+ UI.divider();
4157
+ UI.info("Fallback: No real tests were executed because the engine is missing.");
4158
+ process.exit(1);
4159
+ }
4160
+ throw e;
4161
+ }
4162
+ }
4163
+
4164
+ // src/commands/test.ts
4165
+ function registerTestCommands(program) {
4166
+ 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("--mass-report", "Show mass/fee report after test run", false).option("--mass-snapshot <label>", "Save mass snapshot for regression detection").option("--mass-compare <label>", "Compare against saved mass snapshot").action(async (files, options) => {
4167
+ try {
4168
+ await runTest({
4169
+ files,
4170
+ network: options.network,
4171
+ watch: options.watch,
4172
+ json: options.json,
4173
+ reporter: options.reporter,
4174
+ massReport: options.massReport,
4175
+ ...options.massSnapshot ? { massSnapshot: options.massSnapshot } : {},
4176
+ ...options.massCompare ? { massCompare: options.massCompare } : {}
4177
+ });
3291
4178
  } catch (e) {
3292
- console.error("Test execution failed:", e instanceof Error ? e.message : e);
4179
+ handleError(e, "Test execution failed");
3293
4180
  process.exit(1);
3294
4181
  }
3295
4182
  });
@@ -3297,100 +4184,1010 @@ Discovered ${targetFiles.length} test files.`);
3297
4184
 
3298
4185
  // src/commands/doctor.ts
3299
4186
  import os from "os";
3300
- import path10 from "path";
4187
+ import path11 from "path";
3301
4188
  import fs9 from "fs/promises";
3302
- import pc from "picocolors";
3303
- import { loadHardkasConfig as loadHardkasConfig2 } from "@hardkas/config";
3304
- import { JsonWrpcKaspaClient as JsonWrpcKaspaClient3 } from "@hardkas/kaspa-rpc";
3305
- import { HardkasStore } from "@hardkas/query-store";
4189
+ import pc2 from "picocolors";
4190
+ import { DockerKaspadRunner as DockerKaspadRunner7 } from "@hardkas/node-runner";
4191
+ import { execa } from "execa";
3306
4192
  function registerDoctorCommand(program) {
3307
- program.command("doctor").description("Perform a full system diagnostic and health report").action(async () => {
4193
+ program.command("doctor").description(`Perform a full system diagnostic and health report ${UI.maturity("stable")}`).option("--json", "Output results as stable JSON schema", false).action(async (opts) => {
3308
4194
  try {
3309
- await runDoctor();
4195
+ await runDoctor(opts);
3310
4196
  } catch (err) {
3311
4197
  handleError(err);
3312
4198
  }
3313
4199
  });
3314
4200
  }
3315
- async function runDoctor() {
3316
- UI.box("HardKAS System Doctor", "Operational Health Check");
3317
- UI.header("Environment Status");
3318
- UI.field("OS", `${os.type()} ${os.release()} (${os.arch()})`);
3319
- UI.field("Node", process.version);
3320
- UI.field("CWD", process.cwd());
3321
- UI.divider();
3322
- UI.header("Configuration Analysis");
3323
- try {
3324
- const loaded = await loadHardkasConfig2({ cwd: process.cwd() });
3325
- UI.success(`Config found: ${pc.cyan(path10.basename(loaded.path || "unknown"))}`);
3326
- UI.field("Default Network", loaded.config.defaultNetwork || "simnet");
3327
- } catch (e) {
3328
- UI.error("Configuration issues detected", e.message);
4201
+ async function runDoctor(opts) {
4202
+ const report = {
4203
+ version: "0.2.2-alpha.1",
4204
+ // In real world, get this from constants
4205
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4206
+ checks: [],
4207
+ summary: { total: 0, passed: 0, failed: 0, warnings: 0, skipped: 0 }
4208
+ };
4209
+ if (!opts.json) {
4210
+ UI.box("HardKAS System Doctor", "Operational Health Check");
4211
+ }
4212
+ const addCheck = (check) => {
4213
+ report.checks.push(check);
4214
+ report.summary.total++;
4215
+ if (check.status === "pass") report.summary.passed++;
4216
+ else if (check.status === "fail") report.summary.failed++;
4217
+ else if (check.status === "warn") report.summary.warnings++;
4218
+ else if (check.status === "skip") report.summary.skipped++;
4219
+ if (!opts.json) {
4220
+ let icon = pc2.green("\u2705");
4221
+ if (check.status === "fail") icon = pc2.red("\u274C");
4222
+ if (check.status === "warn") icon = pc2.yellow("\u26A0\uFE0F");
4223
+ if (check.status === "skip") icon = pc2.gray("\u23ED\uFE0F");
4224
+ console.log(` ${icon} ${pc2.bold(check.name)}: ${check.message}`);
4225
+ if (check.suggestion && check.status !== "pass") {
4226
+ console.log(` ${pc2.dim("Suggestion: " + check.suggestion)}`);
4227
+ }
4228
+ }
4229
+ };
4230
+ const nodeVer = process.version;
4231
+ const nodeMajor = parseInt(nodeVer.slice(1).split(".")[0]);
4232
+ if (nodeMajor >= 18) {
4233
+ addCheck({
4234
+ name: "Node.js version",
4235
+ category: "runtime",
4236
+ status: "pass",
4237
+ message: `${nodeVer} (>= 18 required)`
4238
+ });
4239
+ } else {
4240
+ addCheck({
4241
+ name: "Node.js version",
4242
+ category: "runtime",
4243
+ status: "fail",
4244
+ message: `${nodeVer} (>= 18 required)`,
4245
+ suggestion: "Upgrade Node.js to v18 or higher."
4246
+ });
3329
4247
  }
3330
- UI.divider();
3331
- UI.header("RPC Connectivity & Health");
3332
4248
  try {
3333
- const loaded = await loadHardkasConfig2({ cwd: process.cwd() });
3334
- const networkId = loaded.config.defaultNetwork || "simnet";
3335
- const target = loaded.config.networks?.[networkId];
3336
- let rpcUrl = "ws://127.0.0.1:18210";
3337
- if (target?.rpcUrl) rpcUrl = target.rpcUrl;
3338
- UI.info(`Connecting to ${pc.cyan(rpcUrl)}...`);
3339
- const rpc = new JsonWrpcKaspaClient3({ rpcUrl });
3340
- const info = await rpc.getInfo();
3341
- UI.success(`RPC Alive: ${pc.bold(info.networkId || "active")}`);
3342
- UI.field("Synced", info.isSynced ? pc.green("YES") : pc.yellow("NO"));
3343
- if (info.serverVersion) UI.field("Version", info.serverVersion);
3344
- } catch (e) {
3345
- UI.error("RPC Connection Failed", "Is the localnet or node running? Check your network config.");
4249
+ const { stdout } = await execa("pnpm", ["-v"]);
4250
+ addCheck({
4251
+ name: "pnpm",
4252
+ category: "runtime",
4253
+ status: "pass",
4254
+ message: `v${stdout.trim()}`
4255
+ });
4256
+ } catch {
4257
+ addCheck({
4258
+ name: "pnpm",
4259
+ category: "runtime",
4260
+ status: "fail",
4261
+ message: "Not found in PATH",
4262
+ suggestion: "Install pnpm (npm install -g pnpm)."
4263
+ });
3346
4264
  }
3347
- UI.divider();
3348
- UI.header("Artifact Store Integrity");
3349
- const hardkasDir = path10.join(process.cwd(), ".hardkas");
4265
+ const hardkasDir = path11.join(process.cwd(), ".hardkas");
4266
+ let dirExists = false;
3350
4267
  try {
3351
4268
  const stats = await fs9.stat(hardkasDir);
4269
+ dirExists = stats.isDirectory();
4270
+ addCheck({
4271
+ name: ".hardkas/ directory",
4272
+ category: "persistence",
4273
+ status: "pass",
4274
+ message: "Exists and is active"
4275
+ });
4276
+ } catch {
4277
+ addCheck({
4278
+ name: ".hardkas/ directory",
4279
+ category: "persistence",
4280
+ status: "fail",
4281
+ message: "Not found",
4282
+ suggestion: "Run 'hardkas init' to initialize project."
4283
+ });
4284
+ }
4285
+ if (dirExists) {
4286
+ try {
4287
+ const gitignorePath = path11.join(process.cwd(), ".gitignore");
4288
+ const gitignore = await fs9.readFile(gitignorePath, "utf-8");
4289
+ if (gitignore.includes(".hardkas")) {
4290
+ addCheck({
4291
+ name: ".gitignore protection",
4292
+ category: "persistence",
4293
+ status: "pass",
4294
+ message: "Contains .hardkas/"
4295
+ });
4296
+ } else {
4297
+ addCheck({
4298
+ name: ".gitignore protection",
4299
+ category: "persistence",
4300
+ status: "warn",
4301
+ message: "Does not contain .hardkas/",
4302
+ suggestion: "Add '.hardkas/' to your .gitignore to avoid committing artifacts."
4303
+ });
4304
+ }
4305
+ } catch {
4306
+ addCheck({
4307
+ name: ".gitignore protection",
4308
+ category: "persistence",
4309
+ status: "skip",
4310
+ message: ".gitignore not found"
4311
+ });
4312
+ }
4313
+ const dbPath = path11.join(hardkasDir, "store.db");
4314
+ try {
4315
+ await fs9.access(dbPath);
4316
+ addCheck({
4317
+ name: "store.db accessibility",
4318
+ category: "persistence",
4319
+ status: "pass",
4320
+ message: "File exists and is accessible"
4321
+ });
4322
+ } catch {
4323
+ addCheck({
4324
+ name: "store.db accessibility",
4325
+ category: "persistence",
4326
+ status: "warn",
4327
+ message: "store.db not found",
4328
+ suggestion: "Run 'hardkas query store rebuild' to populate the index."
4329
+ });
4330
+ }
4331
+ try {
4332
+ const files = await fs9.readdir(hardkasDir);
4333
+ const locks = files.filter((f) => f.endsWith(".lock"));
4334
+ if (locks.length === 0) {
4335
+ addCheck({
4336
+ name: "Workspace locks",
4337
+ category: "persistence",
4338
+ status: "pass",
4339
+ message: "No active or stale locks found"
4340
+ });
4341
+ } else {
4342
+ addCheck({
4343
+ name: "Workspace locks",
4344
+ category: "persistence",
4345
+ status: "warn",
4346
+ message: `${locks.length} lock(s) found`,
4347
+ suggestion: "Use 'hardkas lock doctor' to diagnose if they are stale."
4348
+ });
4349
+ }
4350
+ } catch {
4351
+ }
4352
+ }
4353
+ const keystoreDir = path11.join(hardkasDir, "keystore");
4354
+ try {
4355
+ const stats = await fs9.stat(keystoreDir);
3352
4356
  if (stats.isDirectory()) {
4357
+ const files = await fs9.readdir(keystoreDir);
4358
+ let allOk = true;
4359
+ for (const file of files) {
4360
+ const filePath = path11.join(keystoreDir, file);
4361
+ const fstats = await fs9.stat(filePath);
4362
+ if (os.platform() !== "win32") {
4363
+ const mode = fstats.mode & 511;
4364
+ if (mode !== 384) allOk = false;
4365
+ }
4366
+ }
4367
+ if (allOk) {
4368
+ addCheck({
4369
+ name: "Keystore permissions",
4370
+ category: "security",
4371
+ status: "pass",
4372
+ message: "0600 permissions enforced"
4373
+ });
4374
+ } else {
4375
+ addCheck({
4376
+ name: "Keystore permissions",
4377
+ category: "security",
4378
+ status: "warn",
4379
+ message: "Relaxed permissions detected",
4380
+ suggestion: "Ensure .hardkas/keystore/* files are chmod 600."
4381
+ });
4382
+ }
4383
+ }
4384
+ } catch {
4385
+ addCheck({
4386
+ name: "Keystore permissions",
4387
+ category: "security",
4388
+ status: "pass",
4389
+ message: "No keystore found (nothing to secure)"
4390
+ });
4391
+ }
4392
+ if (dirExists) {
4393
+ try {
3353
4394
  const files = await fs9.readdir(hardkasDir);
3354
- const artifacts = files.filter((f) => f.endsWith(".json") && !f.endsWith(".enc.json"));
3355
- UI.success(`Artifact directory .hardkas/ is active`);
3356
- UI.field("Cached Artifacts", artifacts.length);
3357
- const hasEvents = files.includes("events.jsonl");
3358
- if (hasEvents) {
3359
- UI.success("Observability event log (events.jsonl) is present");
4395
+ const accountsJson = files.find((f) => f.includes("accounts") && f.endsWith(".json"));
4396
+ if (accountsJson) {
4397
+ const content = await fs9.readFile(path11.join(hardkasDir, accountsJson), "utf-8");
4398
+ if (content.includes("privateKey") && !content.includes("encrypted")) {
4399
+ addCheck({
4400
+ name: "Plaintext keys check",
4401
+ category: "security",
4402
+ status: "fail",
4403
+ message: "Plaintext private keys detected in artifacts!",
4404
+ suggestion: "Use encrypted accounts and avoid --unsafe-plaintext."
4405
+ });
4406
+ } else {
4407
+ addCheck({
4408
+ name: "Plaintext keys check",
4409
+ category: "security",
4410
+ status: "pass",
4411
+ message: "No plaintext keys found in account artifacts"
4412
+ });
4413
+ }
4414
+ }
4415
+ } catch {
4416
+ }
4417
+ }
4418
+ let dockerDaemonOk = false;
4419
+ try {
4420
+ await execa("docker", ["info"]);
4421
+ dockerDaemonOk = true;
4422
+ addCheck({
4423
+ name: "Docker daemon",
4424
+ category: "docker",
4425
+ status: "pass",
4426
+ message: "Reachable"
4427
+ });
4428
+ } catch {
4429
+ addCheck({
4430
+ name: "Docker daemon",
4431
+ category: "docker",
4432
+ status: "fail",
4433
+ message: "Not reachable",
4434
+ suggestion: "Ensure Docker is running."
4435
+ });
4436
+ }
4437
+ if (dockerDaemonOk) {
4438
+ try {
4439
+ const runner = new DockerKaspadRunner7();
4440
+ const status = await runner.status();
4441
+ addCheck({
4442
+ name: "kaspad image",
4443
+ category: "docker",
4444
+ status: "pass",
4445
+ message: status.image || "unknown"
4446
+ });
4447
+ if (status.running) {
4448
+ addCheck({
4449
+ name: "Local node RPC",
4450
+ category: "network",
4451
+ status: status.rpcReady ? "pass" : "fail",
4452
+ message: status.rpcReady ? "Ready" : "Not Ready",
4453
+ suggestion: !status.rpcReady ? "Check node logs: hardkas node logs" : void 0
4454
+ });
3360
4455
  } else {
3361
- UI.warning("Event log missing. Operational queries may be limited.");
4456
+ addCheck({
4457
+ name: "Local node RPC",
4458
+ category: "network",
4459
+ status: "skip",
4460
+ message: "Node is not running"
4461
+ });
4462
+ }
4463
+ } catch {
4464
+ }
4465
+ }
4466
+ if (opts.json) {
4467
+ console.log(JSON.stringify(report, null, 2));
4468
+ } else {
4469
+ UI.divider();
4470
+ console.log(` Summary: ${report.summary.passed} passed, ${report.summary.failed} failed, ${report.summary.warnings} warning, ${report.summary.skipped} skipped`);
4471
+ UI.footer("Use 'hardkas capabilities' to see supported features.");
4472
+ }
4473
+ }
4474
+
4475
+ // src/commands/faucet.ts
4476
+ function registerFaucetCommand(program) {
4477
+ 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) => {
4478
+ try {
4479
+ const amountSompi = BigInt(parseFloat(options.amount) * 1e8);
4480
+ const result = await runAccountsFund({ identifier, amountSompi });
4481
+ console.log(result.formatted);
4482
+ } catch (e) {
4483
+ handleError(e);
4484
+ process.exitCode = 1;
4485
+ }
4486
+ });
4487
+ }
4488
+
4489
+ // src/runners/script-runner.ts
4490
+ import { resolve } from "path";
4491
+ import { existsSync, unlinkSync } from "fs";
4492
+ import { execSync } from "child_process";
4493
+ import { loadHardkasConfig as loadHardkasConfig12 } from "@hardkas/config";
4494
+ async function runScript(script, opts) {
4495
+ const scriptPath = resolve(script);
4496
+ if (!existsSync(scriptPath)) {
4497
+ console.error(`Script not found: ${scriptPath}`);
4498
+ process.exit(1);
4499
+ }
4500
+ const { config } = await loadHardkasConfig12();
4501
+ const netConfig = config.networks?.[opts.network] || { kind: "simulated" };
4502
+ const isSimulated = netConfig.kind === "simulated";
4503
+ let injectionCode = "";
4504
+ if (opts.harness) {
4505
+ if (isSimulated) {
4506
+ injectionCode = `
4507
+ import { createTestHarness } from "@hardkas/testing/harness";
4508
+ const hardkas = createTestHarness({
4509
+ accounts: ${parseInt(opts.accounts)},
4510
+ initialBalance: ${opts.balance}n,
4511
+ networkId: "${opts.network}",
4512
+ });
4513
+ (globalThis as any).hardkas = hardkas;
4514
+ `;
4515
+ } else {
4516
+ const rpcUrl = netConfig.rpcUrl;
4517
+ const networkId = netConfig.network || opts.network;
4518
+ injectionCode = `
4519
+ import { KaspaRpcClient } from "@hardkas/kaspa-rpc";
4520
+ const rpc = new KaspaRpcClient({ url: "${rpcUrl}" });
4521
+ (globalThis as any).hardkas = {
4522
+ network: "${opts.network}",
4523
+ networkId: "${networkId}",
4524
+ rpcUrl: "${rpcUrl}",
4525
+ rpc: rpc
4526
+ };
4527
+ `;
4528
+ }
4529
+ }
4530
+ const wrapperCode = `
4531
+ ${injectionCode}
4532
+ (async () => {
4533
+ try {
4534
+ await import("file://${scriptPath.replace(/\\/g, "/")}");
4535
+ } catch (err) {
4536
+ console.error(err);
4537
+ process.exit(1);
4538
+ }
4539
+ })();
4540
+ `;
4541
+ const dotHardkas = resolve(process.cwd(), ".hardkas");
4542
+ if (!existsSync(dotHardkas)) {
4543
+ const { mkdirSync } = await import("fs");
4544
+ mkdirSync(dotHardkas, { recursive: true });
4545
+ }
4546
+ const tempWrapper = resolve(dotHardkas, `run-${Date.now()}.mts`);
4547
+ const { writeFileAtomicSync } = await import("@hardkas/core");
4548
+ writeFileAtomicSync(tempWrapper, wrapperCode);
4549
+ try {
4550
+ const tsxBin = resolve("node_modules/.bin/tsx");
4551
+ const actualTsx = existsSync(tsxBin) ? tsxBin : "npx tsx";
4552
+ execSync(`${actualTsx} ${tempWrapper}`, {
4553
+ stdio: "inherit",
4554
+ cwd: process.cwd(),
4555
+ env: {
4556
+ ...process.env,
4557
+ HARDKAS_NETWORK: opts.network,
4558
+ HARDKAS_ACCOUNTS: opts.accounts,
4559
+ HARDKAS_BALANCE: opts.balance
4560
+ }
4561
+ });
4562
+ } finally {
4563
+ if (existsSync(tempWrapper)) {
4564
+ unlinkSync(tempWrapper);
4565
+ }
4566
+ }
4567
+ }
4568
+
4569
+ // src/commands/run.ts
4570
+ function registerRunCommand(program) {
4571
+ program.command("run <script>").description(`Execute a TypeScript or JavaScript file with HardKAS SDK injected ${UI.maturity("stable")}`).option("--network <name>", "Network name", "simnet").option("--accounts <n>", "Number of simulated accounts", "3").option("--balance <sompi>", "Initial balance per account in sompi", "100000000000").option("--no-harness", "Skip automatic harness creation").action(async (script, opts) => {
4572
+ await runScript(script, opts);
4573
+ });
4574
+ }
4575
+
4576
+ // src/commands/capabilities.ts
4577
+ import pc3 from "picocolors";
4578
+ import { HARDKAS_VERSION as HARDKAS_VERSION5, CURRENT_HASH_VERSION } from "@hardkas/artifacts";
4579
+ function registerCapabilitiesCommand(program) {
4580
+ program.command("capabilities").description("Show HardKAS capabilities and maturity level").option("--json", "Output as stable JSON schema", false).action(async (opts) => {
4581
+ const caps = {
4582
+ version: HARDKAS_VERSION5,
4583
+ maturity: "hardened-alpha",
4584
+ proofVersion: "repro-v0",
4585
+ hashVersion: CURRENT_HASH_VERSION,
4586
+ capabilities: {
4587
+ artifacts: true,
4588
+ lineageVerification: true,
4589
+ deterministicHashing: true,
4590
+ atomicPersistence: true,
4591
+ workspaceLocks: true,
4592
+ corruptionDetection: true,
4593
+ secretRedaction: true,
4594
+ mainnetGuards: true,
4595
+ localnetSimulation: true,
4596
+ ghostdagSimulation: true,
4597
+ dagConflictResolution: true,
4598
+ massProfiler: true,
4599
+ simulationScenarios: true,
4600
+ queryStore: true,
4601
+ replayVerification: true,
4602
+ schemaMigrations: true,
4603
+ dockerNode: true,
4604
+ scriptRunner: true,
4605
+ testingFramework: true,
4606
+ l2Profiles: true,
4607
+ l2BridgeAssumptions: true,
4608
+ consensusValidation: false,
4609
+ productionWallet: false,
4610
+ silverScript: false,
4611
+ covenants: false,
4612
+ trustlessExit: false,
4613
+ differentialDagValidation: false
4614
+ },
4615
+ trustBoundaries: {
4616
+ replay: "local-workflow-only",
4617
+ artifacts: "internal-integrity-only",
4618
+ simulator: "research-experimental",
4619
+ queryStore: "rebuildable-read-model",
4620
+ l2Bridge: "pre-zk-assumptions"
3362
4621
  }
4622
+ };
4623
+ if (opts.json) {
4624
+ console.log(JSON.stringify(caps, null, 2));
4625
+ } else {
4626
+ renderHumanReadable(caps);
4627
+ }
4628
+ });
4629
+ }
4630
+ function renderHumanReadable(caps) {
4631
+ console.log(`${pc3.bold("HardKAS")} ${pc3.cyan("v" + caps.version)} \u2014 ${pc3.green("Hardened Alpha")}
4632
+ `);
4633
+ const printGroup = (title, items) => {
4634
+ console.log(` ${pc3.bold(title)}`);
4635
+ for (const [name, desc, enabled] of items) {
4636
+ const icon = enabled ? pc3.green("\u2705") : pc3.red("\u274C");
4637
+ const label = enabled ? pc3.white(name.padEnd(16)) : pc3.dim(name.padEnd(16));
4638
+ console.log(` ${icon} ${label} ${pc3.dim(desc)}`);
4639
+ }
4640
+ console.log("");
4641
+ };
4642
+ printGroup("Core", [
4643
+ ["Artifacts", "Canonical hashing v3 (NFC + newline normalization)", true],
4644
+ ["Lineage", "Contamination detection, monotonic sequences", true],
4645
+ ["Determinism", "Reproducibility proof v0 (cross-platform CI)", true],
4646
+ ["Atomic writes", "Temp-file-and-rename with fsync", true],
4647
+ ["Workspace locks", "O_EXCL + PID liveness + deadlock ordering", true],
4648
+ ["Corruption", "27 machine-readable issue codes", true],
4649
+ ["Secret redaction", "All error paths masked", true],
4650
+ ["Mainnet guards", "Hard refusal without --allow-mainnet-signing", true]
4651
+ ]);
4652
+ printGroup("Simulation", [
4653
+ ["Localnet", "Simulated UTXO state + transactions", true],
4654
+ ["GHOSTDAG", "Approximate engine (RESEARCH_EXPERIMENTAL)", true],
4655
+ ["DAG conflicts", "GHOSTDAG-aligned blue/red ordering", true],
4656
+ ["Mass profiler", "Regression detection + snapshots", true],
4657
+ ["Scenarios", "Linear, wide, fork, diamond", true]
4658
+ ]);
4659
+ printGroup("Query & Replay", [
4660
+ ["Query store", "SQLite with forward-only migrations", true],
4661
+ ["Replay", "Local workflow verification", true],
4662
+ ["Migrations", "Checksummed, transactional", true]
4663
+ ]);
4664
+ printGroup("Infrastructure", [
4665
+ ["Docker node", "Pinned kaspad image on simnet", true],
4666
+ ["Script runner", "hardkas run script.ts via tsx", true],
4667
+ ["Testing", "Harness + 11 semantic matchers", true]
4668
+ ]);
4669
+ printGroup("L2", [
4670
+ ["Igra profiles", "Built-in + user config registry", true],
4671
+ ["Bridge model", "Pre-ZK phase awareness", true]
4672
+ ]);
4673
+ printGroup("Not Yet Implemented", [
4674
+ ["Consensus validation", "", false],
4675
+ ["Production wallet", "", false],
4676
+ ["SilverScript / covenants", "", false],
4677
+ ["Trustless exit", "(requires ZK bridge)", false],
4678
+ ["Differential DAG validation", "", false]
4679
+ ]);
4680
+ console.log(` ${pc3.bold("Trust Boundaries")}`);
4681
+ console.log(` Replay: ${pc3.dim(caps.trustBoundaries.replay.replace(/-/g, " "))}`);
4682
+ console.log(` Artifacts: ${pc3.dim(caps.trustBoundaries.artifacts.replace(/-/g, " "))}`);
4683
+ console.log(` Simulator: ${pc3.dim(caps.trustBoundaries.simulator.replace(/-/g, " "))}`);
4684
+ console.log(` Query store: ${pc3.dim(caps.trustBoundaries.queryStore.replace(/-/g, " "))}`);
4685
+ console.log(` L2 bridge: ${pc3.dim(caps.trustBoundaries.l2Bridge.replace(/-/g, " "))}`);
4686
+ }
4687
+
4688
+ // src/commands/new.ts
4689
+ import path12 from "path";
4690
+ import fs10 from "fs/promises";
4691
+ import pc4 from "picocolors";
4692
+ import { execa as execa2 } from "execa";
4693
+
4694
+ // src/templates/basic.ts
4695
+ function generateBasicTemplate(config) {
4696
+ return {
4697
+ "package.json": JSON.stringify({
4698
+ name: config.name,
4699
+ version: "0.1.0",
4700
+ private: true,
4701
+ type: "module",
4702
+ scripts: {
4703
+ "test": "vitest run",
4704
+ "transfer": "hardkas run scripts/transfer.ts",
4705
+ "balance": "hardkas run scripts/check-balance.ts"
4706
+ },
4707
+ devDependencies: {
4708
+ "@hardkas/cli": "alpha",
4709
+ "@hardkas/testing": "alpha",
4710
+ "@hardkas/artifacts": "alpha",
4711
+ "@hardkas/core": "alpha",
4712
+ "vitest": "^2.0.0"
4713
+ }
4714
+ }, null, 2),
4715
+ "hardkas.config.ts": `import { defineConfig } from "@hardkas/cli";
4716
+
4717
+ export default defineConfig({
4718
+ network: "${config.network}",
4719
+ accounts: ${config.accounts},
4720
+ initialBalance: "1000", // KAS
4721
+ });
4722
+ `,
4723
+ ".gitignore": `node_modules
4724
+ dist
4725
+ .hardkas/
4726
+ .turbo
4727
+ *.log
4728
+ `,
4729
+ "scripts/transfer.ts": `// Run with: hardkas run scripts/transfer.ts
4730
+ // or: pnpm transfer
4731
+
4732
+ const h = (globalThis as any).hardkas;
4733
+
4734
+ const [alice, bob] = h.accountNames();
4735
+
4736
+ console.log(\`\\nAccounts:\`);
4737
+ console.log(\` Alice: \${h.balanceOf(alice)} sompi\`);
4738
+ console.log(\` Bob: \${h.balanceOf(bob)} sompi\`);
4739
+
4740
+ console.log(\`\\nSending 10 KAS from \${alice} to \${bob}...\`);
4741
+ const result = h.send({
4742
+ from: alice,
4743
+ to: bob,
4744
+ amountSompi: 10_000_000_000n
4745
+ });
4746
+
4747
+ console.log(\` Status: \${result.receipt.status}\`);
4748
+ console.log(\` TxId: \${result.receipt.txId}\`);
4749
+
4750
+ console.log(\`\\nBalances after:\`);
4751
+ console.log(\` Alice: \${h.balanceOf(alice)} sompi\`);
4752
+ console.log(\` Bob: \${h.balanceOf(bob)} sompi\`);
4753
+ `,
4754
+ "scripts/check-balance.ts": `const h = (globalThis as any).hardkas;
4755
+
4756
+ console.log("\\nAccount Balances:");
4757
+ for (const name of h.accountNames()) {
4758
+ console.log(\` \${name}: \${h.balanceOf(name)} sompi\`);
4759
+ }
4760
+ `,
4761
+ "test/transfer.test.ts": `import { describe, it, expect } from "vitest";
4762
+ import { createTestHarness } from "@hardkas/testing";
4763
+ import "@hardkas/testing/setup";
4764
+
4765
+ describe("Transfer workflow", () => {
4766
+ it("sends KAS from alice to bob", () => {
4767
+ const h = createTestHarness({ accounts: 3, initialBalance: 100_000_000_000n });
4768
+ const [alice, bob] = h.accountNames();
4769
+
4770
+ const result = h.send({
4771
+ from: alice,
4772
+ to: bob,
4773
+ amountSompi: 10_000_000_000n
4774
+ });
4775
+
4776
+ expect(result.receipt).toBeAccepted();
4777
+ expect(result.receipt).toHaveValidTxId();
4778
+ });
4779
+
4780
+ it("rejects insufficient funds", () => {
4781
+ const h = createTestHarness({ accounts: 2, initialBalance: 10_000_000_000n });
4782
+ const [alice, bob] = h.accountNames();
4783
+
4784
+ const result = h.send({
4785
+ from: alice,
4786
+ to: bob,
4787
+ amountSompi: 999_000_000_000n
4788
+ });
4789
+
4790
+ expect(result.ok).toBe(false);
4791
+ expect(result.receipt).toBeFailed();
4792
+ });
4793
+
4794
+ it("produces deterministic txId", () => {
4795
+ const h1 = createTestHarness({ accounts: 2, initialBalance: 100_000_000_000n });
4796
+ const h2 = createTestHarness({ accounts: 2, initialBalance: 100_000_000_000n });
4797
+
4798
+ const r1 = h1.send({ from: h1.accountNames()[0], to: h1.accountNames()[1], amountSompi: 5_000_000_000n });
4799
+ const r2 = h2.send({ from: h2.accountNames()[0], to: h2.accountNames()[1], amountSompi: 5_000_000_000n });
4800
+
4801
+ expect(r1.receipt.txId).toBe(r2.receipt.txId);
4802
+ });
4803
+ });
4804
+ `,
4805
+ "vitest.config.ts": `import { defineConfig } from "vitest/config";
4806
+
4807
+ export default defineConfig({
4808
+ test: {
4809
+ globals: false,
4810
+ environment: "node",
4811
+ },
4812
+ });
4813
+ `,
4814
+ "README.md": `# ${config.name}
4815
+
4816
+ Built with [HardKAS](https://github.com/KasLabDevs/HardKas) \u2014 Kaspa developer toolkit.
4817
+
4818
+ ## Quick start
4819
+
4820
+ \`\`\`bash
4821
+ pnpm install
4822
+ pnpm transfer # Run a simulated transfer
4823
+ pnpm balance # Check account balances
4824
+ pnpm test # Run tests
4825
+ \`\`\`
4826
+ `
4827
+ };
4828
+ }
4829
+
4830
+ // src/commands/new.ts
4831
+ function registerNewCommand(program) {
4832
+ program.command("new <name>").description(`Create a new HardKAS project ${UI.maturity("stable")}`).option("--template <type>", "Project template", "basic").option("--network <name>", "Default network", "simnet").option("--accounts <n>", "Number of simulated accounts", "3").option("--skip-install", "Skip pnpm install", false).action(async (name, opts) => {
4833
+ try {
4834
+ await createProject(name, opts);
4835
+ } catch (err) {
4836
+ handleError(err);
4837
+ }
4838
+ });
4839
+ }
4840
+ async function createProject(name, opts) {
4841
+ const projectDir = path12.resolve(process.cwd(), name);
4842
+ try {
4843
+ const stats = await fs10.stat(projectDir);
4844
+ if (stats) {
4845
+ console.error(pc4.red(`Error: Directory '${name}' already exists.`));
4846
+ process.exit(1);
3363
4847
  }
3364
4848
  } catch {
3365
- UI.error("Artifact Store not initialized", "Run 'hardkas init' to create a project.");
3366
4849
  }
3367
- UI.divider();
3368
- UI.header("Query Store (SQLite) Status");
3369
- const dbPath = path10.join(hardkasDir, "query.db");
4850
+ UI.box("HardKAS Project Scaffolding", `Creating '${name}'...`);
4851
+ const files = generateBasicTemplate({
4852
+ name,
4853
+ network: opts.network,
4854
+ accounts: parseInt(opts.accounts)
4855
+ });
4856
+ await fs10.mkdir(projectDir, { recursive: true });
4857
+ for (const [relativePath, content] of Object.entries(files)) {
4858
+ const filePath = path12.join(projectDir, relativePath);
4859
+ await fs10.mkdir(path12.dirname(filePath), { recursive: true });
4860
+ await fs10.writeFile(filePath, content);
4861
+ console.log(` ${pc4.green("create")} ${relativePath}`);
4862
+ }
4863
+ if (!opts.skipInstall) {
4864
+ console.log(pc4.cyan("\nInstalling dependencies with pnpm..."));
4865
+ try {
4866
+ await execa2("pnpm", ["install"], { cwd: projectDir, stdio: "inherit" });
4867
+ } catch (err) {
4868
+ console.warn(pc4.yellow("\nWarning: 'pnpm install' failed. You may need to run it manually."));
4869
+ }
4870
+ }
4871
+ console.log(pc4.green(`
4872
+ \u2705 Created project: ${name}`));
4873
+ console.log(`
4874
+ Next steps:`);
4875
+ console.log(pc4.cyan(` cd ${name}`));
4876
+ if (opts.skipInstall) {
4877
+ console.log(pc4.cyan(` pnpm install`));
4878
+ }
4879
+ console.log(pc4.cyan(` pnpm transfer # Run your first transfer`));
4880
+ console.log(pc4.cyan(` pnpm test # Run tests`));
4881
+ console.log(pc4.cyan(` hardkas capabilities # See what's available`));
4882
+ console.log(`
4883
+ Happy building! \u{1F680}
4884
+ `);
4885
+ }
4886
+
4887
+ // src/runners/console-runner.ts
4888
+ import repl from "repl";
4889
+ import fs11 from "fs";
4890
+ import path13 from "path";
4891
+ import { createTestHarness } from "@hardkas/testing/harness";
4892
+ import { calculateContentHash as calculateContentHash3, canonicalStringify } from "@hardkas/artifacts";
4893
+ import { maskSecrets, formatSompi as formatSompi5, parseKasToSompi as parseKasToSompi2 } from "@hardkas/core";
4894
+ async function startConsole(opts) {
4895
+ const harness = createTestHarness({
4896
+ accounts: opts.accounts,
4897
+ initialBalance: opts.balance,
4898
+ networkId: opts.network
4899
+ });
4900
+ console.log(`
4901
+ HardKAS Console \u2014 ${opts.network}`);
4902
+ console.log(` ${opts.accounts} accounts, ${formatSompi5(opts.balance)} each
4903
+ `);
4904
+ console.log(" Available globals:");
4905
+ console.log(" h \u2014 test harness (send, balanceOf, accountNames, reset, snapshot)");
4906
+ console.log(" hash(obj) \u2014 calculateContentHash");
4907
+ console.log(" canonical(obj) \u2014 canonicalStringify");
4908
+ console.log(" kas(str) \u2014 parseKasToSompi ('1.5' \u2192 150000000n)");
4909
+ console.log(" sompi(n) \u2014 formatSompi (150000000n \u2192 '1.5 KAS')");
4910
+ console.log("");
4911
+ console.log(" Quick start:");
4912
+ console.log(" h.accountNames()");
4913
+ console.log(" h.balanceOf('alice')");
4914
+ console.log(" h.send({ from: 'alice', to: 'bob', amountSompi: 10_000_000_000n })");
4915
+ console.log("");
4916
+ const historyDir = path13.join(process.cwd(), ".hardkas");
4917
+ const historyPath = path13.join(historyDir, "console-history");
4918
+ if (!fs11.existsSync(historyDir)) fs11.mkdirSync(historyDir, { recursive: true });
4919
+ const r = repl.start({
4920
+ prompt: "hardkas> ",
4921
+ useGlobal: false
4922
+ });
4923
+ r.context.h = harness;
4924
+ r.context.hash = calculateContentHash3;
4925
+ r.context.canonical = canonicalStringify;
4926
+ r.context.kas = parseKasToSompi2;
4927
+ r.context.sompi = formatSompi5;
4928
+ r.context.maskSecrets = maskSecrets;
3370
4929
  try {
3371
- const store = new HardkasStore({ dbPath });
3372
- store.connect();
3373
- const db = store.getDatabase();
3374
- const artCount = db.prepare("SELECT COUNT(*) as count FROM artifacts").get().count;
3375
- const eventCount = db.prepare("SELECT COUNT(*) as count FROM events").get().count;
3376
- UI.success("Relational index (query.db) is healthy");
3377
- UI.field("Indexed Artifacts", artCount);
3378
- UI.field("Indexed Events", eventCount);
3379
- if (artCount === 0 && eventCount === 0) {
3380
- UI.warning("Database is empty. Run 'hardkas query store index' to populate.");
3381
- }
3382
- store.disconnect();
4930
+ if (fs11.existsSync(historyPath)) {
4931
+ const history = fs11.readFileSync(historyPath, "utf-8").split("\n").filter(Boolean).reverse();
4932
+ if (r.history) {
4933
+ for (const line of history) {
4934
+ r.history.push(line);
4935
+ }
4936
+ }
4937
+ }
4938
+ } catch {
4939
+ }
4940
+ r.on("exit", () => {
4941
+ try {
4942
+ const lines = (r.history || []).slice(0, 500).reverse().join("\n");
4943
+ fs11.writeFileSync(historyPath, lines);
4944
+ } catch {
4945
+ }
4946
+ console.log("\nBye!");
4947
+ process.exit(0);
4948
+ });
4949
+ }
4950
+
4951
+ // src/commands/console.ts
4952
+ function registerConsoleCommand(program) {
4953
+ program.command("console").description(`Open an interactive REPL with HardKAS SDK pre-loaded ${UI.maturity("stable")}`).option("--network <name>", "Network name", "simnet").option("--accounts <n>", "Number of simulated accounts", "3").option("--balance <sompi>", "Initial balance per account in sompi", "100000000000").action(async (opts) => {
4954
+ await startConsole({
4955
+ network: opts.network,
4956
+ accounts: parseInt(opts.accounts, 10),
4957
+ balance: BigInt(opts.balance)
4958
+ });
4959
+ });
4960
+ }
4961
+
4962
+ // src/commands/networks.ts
4963
+ import { loadHardkasConfig as loadHardkasConfig13 } from "@hardkas/config";
4964
+ function registerNetworksCommand(program) {
4965
+ program.command("networks").description(`List configured networks ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (opts) => {
4966
+ const { config } = await loadHardkasConfig13();
4967
+ const networks = config.networks || {};
4968
+ if (opts.json) {
4969
+ console.log(JSON.stringify(networks, null, 2));
4970
+ return;
4971
+ }
4972
+ UI.header("HardKAS Networks");
4973
+ const header = " Network RPC Kind";
4974
+ console.log(header);
4975
+ console.log(" " + "\u2500".repeat(header.length - 2));
4976
+ for (const [name, net] of Object.entries(networks)) {
4977
+ const rpc = net.rpcUrl || "simulated";
4978
+ const kind = net.kind || "unknown";
4979
+ console.log(` ${name.padEnd(14)} ${rpc.padEnd(32)} ${kind}`);
4980
+ }
4981
+ console.log("");
4982
+ });
4983
+ }
4984
+
4985
+ // src/runners/localnet-runners.ts
4986
+ import { loadHardkasConfig as loadHardkasConfig14, resolveNetworkTarget as resolveNetworkTarget3 } from "@hardkas/config";
4987
+ import { JsonWrpcKaspaClient as JsonWrpcKaspaClient4 } from "@hardkas/kaspa-rpc";
4988
+ import { forkFromNetwork, saveLocalnetState as saveLocalnetState3 } from "@hardkas/localnet";
4989
+ import { resolve as resolve2 } from "path";
4990
+ import { withLock } from "@hardkas/core";
4991
+ async function runLocalnetFork(opts) {
4992
+ UI.header(`HardKAS Localnet Fork`);
4993
+ const { config } = await loadHardkasConfig14();
4994
+ const { target } = resolveNetworkTarget3({ config, network: opts.network });
4995
+ if (target.kind === "simulated") {
4996
+ throw new Error("Cannot fork from a simulated network.");
4997
+ }
4998
+ const rpcUrl = target.rpcUrl;
4999
+ if (!rpcUrl) throw new Error(`No RPC URL configured for network '${opts.network}'.`);
5000
+ UI.info(`Forking from: ${opts.network} (${rpcUrl})`);
5001
+ if (opts.addresses.length > 0) {
5002
+ UI.info(`Addresses: ${opts.addresses.join(", ")}`);
5003
+ } else {
5004
+ UI.warning("No addresses specified. Forked state will be empty.");
5005
+ }
5006
+ const client = new JsonWrpcKaspaClient4({ rpcUrl });
5007
+ try {
5008
+ await withLock({
5009
+ rootDir: process.cwd(),
5010
+ name: "workspace",
5011
+ command: "hardkas localnet fork"
5012
+ }, async () => {
5013
+ const state = await forkFromNetwork(client, {
5014
+ network: opts.network,
5015
+ rpcUrl,
5016
+ addresses: opts.addresses,
5017
+ ...opts.atDaaScore ? { atDaaScore: opts.atDaaScore } : {}
5018
+ });
5019
+ const outputPath = opts.outputPath ? resolve2(opts.outputPath) : resolve2(process.cwd(), ".hardkas", "localnet-state.json");
5020
+ await saveLocalnetState3(state);
5021
+ UI.success(`Forked state saved to: ${outputPath}`);
5022
+ UI.info(`DAA Score: ${state.daaScore}`);
5023
+ UI.info(`UTXOs: ${state.utxos.length}`);
5024
+ });
3383
5025
  } catch (e) {
3384
- UI.error("Query Store Issues", "The SQLite database might be corrupt or inaccessible.");
5026
+ handleError(e, "Forking failed");
5027
+ process.exit(1);
5028
+ } finally {
5029
+ await client.close();
3385
5030
  }
3386
- UI.footer("Use 'hardkas query' for deep operational introspection.");
3387
5031
  }
3388
5032
 
3389
- // src/index.ts
3390
- var HARDKAS_VERSION4 = "0.2.2-alpha";
3391
- async function main() {
5033
+ // src/commands/localnet.ts
5034
+ function registerLocalnetCommands(program) {
5035
+ const localnet = program.command("localnet").description("Manage localnet state and snapshots");
5036
+ localnet.command("fork").description(`Fork state from a real Kaspa network for local simulation ${UI.maturity("preview")}`).requiredOption("--network <name>", "Network to fork from").option("--addresses <addrs...>", "Only fetch UTXOs for these addresses").option("--at-daa-score <score>", "Fork at specific DAA score (default: latest)").option("--output <path>", "Save fork snapshot to file").option("--json", "Output as JSON", false).action(async (opts) => {
5037
+ await runLocalnetFork({
5038
+ network: opts.network,
5039
+ addresses: opts.addresses || [],
5040
+ atDaaScore: opts.atDaaScore,
5041
+ outputPath: opts.output
5042
+ });
5043
+ });
5044
+ }
5045
+
5046
+ // src/commands/deploy.ts
5047
+ function registerDeployCommands(program) {
5048
+ const deployCmd = program.command("deploy").description("Track and manage deployments");
5049
+ deployCmd.command("track <label>").description(`Create a deployment record for a transaction ${UI.maturity("stable")}`).requiredOption("--network <name>", "Network where deployed").option("--tx-id <txId>", "Transaction ID").option("--plan <artifactId>", "Reference to plan artifact").option("--receipt <artifactId>", "Reference to receipt artifact").option("--status <status>", "Deployment status", "sent").option("--notes <text>", "Notes about this deployment").option("--json", "Output as JSON", false).action(async (label, opts) => {
5050
+ const { UI: UI2 } = await import("./ui-SUYOHGGP.js");
5051
+ await trackDeployment({ label, ...opts });
5052
+ });
5053
+ deployCmd.command("list").description(`List all tracked deployments ${UI.maturity("stable")}`).option("--network <name>", "Filter by network").option("--status <status>", "Filter by status").option("--json", "Output as JSON", false).action(async (opts) => {
5054
+ await listAllDeployments(opts);
5055
+ });
5056
+ deployCmd.command("inspect <label>").description(`Show full details of a deployment ${UI.maturity("stable")}`).requiredOption("--network <name>", "Network").option("--json", "Output as JSON", false).action(async (label, opts) => {
5057
+ await inspectDeployment({ label, ...opts });
5058
+ });
5059
+ deployCmd.command("status <label>").description(`Check deployment status (query RPC if available) ${UI.maturity("stable")}`).requiredOption("--network <name>", "Network").option("--verify", "Verify against RPC node", false).option("--json", "Output as JSON", false).action(async (label, opts) => {
5060
+ await verifyDeploymentStatus({ label, ...opts });
5061
+ });
5062
+ deployCmd.command("history").description(`Show deployment history across all networks ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (opts) => {
5063
+ await showDeploymentHistory(opts);
5064
+ });
5065
+ }
5066
+
5067
+ // src/program.ts
5068
+ import { readFileSync } from "fs";
5069
+ import { fileURLToPath } from "url";
5070
+ import path14 from "path";
5071
+
5072
+ // src/commands/lock.ts
5073
+ import pc5 from "picocolors";
5074
+ import { listLocks, clearLock } from "@hardkas/core";
5075
+ function registerLockCommands(program) {
5076
+ const lockCmd = program.command("lock").description("Manage HardKAS workspace locks");
5077
+ lockCmd.command("list").description(`List all active workspace locks ${UI.maturity("stable")}`).option("--json", "Output as JSON", false).action(async (options) => {
5078
+ try {
5079
+ const locks = listLocks(process.cwd());
5080
+ if (options.json) {
5081
+ console.log(JSON.stringify(locks, null, 2));
5082
+ return;
5083
+ }
5084
+ UI.header("Active Workspace Locks");
5085
+ if (locks.length === 0) {
5086
+ console.log(" No active locks found.\n");
5087
+ return;
5088
+ }
5089
+ for (const lock of locks) {
5090
+ const status = lock.isAlive ? pc5.green("live") : pc5.red("STALE");
5091
+ console.log(` ${pc5.bold(lock.name.padEnd(12))} ${pc5.dim("PID:")} ${lock.metadata.pid.toString().padEnd(6)} ${pc5.dim("State:")} ${status}`);
5092
+ console.log(` ${pc5.dim("Command:")} ${lock.metadata.command}`);
5093
+ console.log(` ${pc5.dim("Created:")} ${lock.metadata.createdAt}`);
5094
+ console.log("");
5095
+ }
5096
+ } catch (e) {
5097
+ handleLockError(e);
5098
+ process.exitCode = 1;
5099
+ }
5100
+ });
5101
+ lockCmd.command("status [name]").description(`Show status of one or all locks ${UI.maturity("stable")}`).action(async (name) => {
5102
+ try {
5103
+ const locks = listLocks(process.cwd());
5104
+ if (name) {
5105
+ const lock = locks.find((l) => l.name === name);
5106
+ if (!lock) {
5107
+ console.log(`
5108
+ Lock '${name}' is ${pc5.green("FREE")}.
5109
+ `);
5110
+ return;
5111
+ }
5112
+ UI.header(`Lock Status: ${name}`);
5113
+ console.log(` State: ${lock.isAlive ? pc5.green("HELD (live)") : pc5.red("HELD (STALE)")}`);
5114
+ console.log(` PID: ${lock.metadata.pid}`);
5115
+ console.log(` Host: ${lock.metadata.hostname}`);
5116
+ console.log(` Command: ${lock.metadata.command}`);
5117
+ console.log(` Created: ${lock.metadata.createdAt}`);
5118
+ console.log(` Path: ${lock.path}`);
5119
+ console.log("");
5120
+ } else {
5121
+ UI.header("Lock Summary");
5122
+ for (const lock of locks) {
5123
+ console.log(` ${pc5.bold(lock.name.padEnd(12))}: ${lock.isAlive ? pc5.green("HELD") : pc5.red("STALE")}`);
5124
+ }
5125
+ if (locks.length === 0) console.log(" All locks are FREE.");
5126
+ console.log("");
5127
+ }
5128
+ } catch (e) {
5129
+ handleLockError(e);
5130
+ process.exitCode = 1;
5131
+ }
5132
+ });
5133
+ lockCmd.command("doctor").description(`Analyze locks and identify stale or corrupted ones ${UI.maturity("stable")}`).action(async () => {
5134
+ try {
5135
+ const locks = listLocks(process.cwd());
5136
+ UI.header("Lock Doctor Analysis");
5137
+ let staleCount = 0;
5138
+ for (const lock of locks) {
5139
+ if (!lock.isAlive) {
5140
+ staleCount++;
5141
+ console.log(` ${pc5.red("\u2717")} Stale lock found: ${pc5.bold(lock.name)} (PID: ${lock.metadata.pid})`);
5142
+ console.log(` Suggestion: Run 'hardkas lock clear ${lock.name} --if-dead'`);
5143
+ }
5144
+ }
5145
+ if (staleCount === 0) {
5146
+ if (locks.length === 0) {
5147
+ console.log(" \u2713 No locks found. Workspace is clean.");
5148
+ } else {
5149
+ console.log(` \u2713 All ${locks.length} active lock(s) are held by live processes.`);
5150
+ }
5151
+ }
5152
+ console.log("");
5153
+ } catch (e) {
5154
+ handleLockError(e);
5155
+ process.exitCode = 1;
5156
+ }
5157
+ });
5158
+ lockCmd.command("clear <name>").description(`Safely or forcibly clear a lock ${UI.maturity("stable")}`).option("--if-dead", "Only clear if the process is no longer running", false).option("--force", "Forcibly clear the lock even if the process is alive", false).option("--yes", "Confirm clearing without prompt", false).action(async (name, options) => {
5159
+ try {
5160
+ if (!options.yes && !options.ifDead) {
5161
+ const confirmed = await UI.confirm(`Clearing an active lock may lead to data corruption if another process is writing to the workspace.
5162
+ Are you sure you want to clear '${name}'?`);
5163
+ if (!confirmed) return;
5164
+ }
5165
+ const result = clearLock(process.cwd(), name, {
5166
+ force: options.force,
5167
+ ifDead: options.ifDead
5168
+ });
5169
+ if (result.cleared) {
5170
+ UI.success(`Lock '${name}' cleared.`);
5171
+ } else {
5172
+ UI.error(`Could not clear lock '${name}': ${result.reason}`);
5173
+ process.exitCode = 1;
5174
+ }
5175
+ } catch (e) {
5176
+ handleLockError(e);
5177
+ process.exitCode = 1;
5178
+ }
5179
+ });
5180
+ }
5181
+
5182
+ // src/program.ts
5183
+ var packageJsonPath = path14.resolve(
5184
+ path14.dirname(fileURLToPath(import.meta.url)),
5185
+ "../package.json"
5186
+ );
5187
+ var { version: HARDKAS_VERSION6 } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
5188
+ function buildHardkasProgram(options) {
3392
5189
  const program = new Command();
3393
- program.name("hardkas").description("HardKAS: Kaspa-native developer operating environment").version(HARDKAS_VERSION4);
5190
+ program.name("hardkas").description("HardKAS: Kaspa-native developer operating environment").version(HARDKAS_VERSION6);
3394
5191
  registerInitCommands(program);
3395
5192
  registerTxCommands(program);
3396
5193
  registerArtifactCommands(program);
@@ -3406,15 +5203,37 @@ async function main() {
3406
5203
  registerQueryCommands(program);
3407
5204
  registerTestCommands(program);
3408
5205
  registerDoctorCommand(program);
5206
+ registerFaucetCommand(program);
5207
+ registerRunCommand(program);
5208
+ registerLockCommands(program);
5209
+ registerCapabilitiesCommand(program);
5210
+ registerNewCommand(program);
5211
+ registerConsoleCommand(program);
5212
+ registerNetworksCommand(program);
5213
+ registerLocalnetCommands(program);
5214
+ registerDeployCommands(program);
5215
+ if (options?.forDocs) {
5216
+ }
5217
+ return program;
5218
+ }
5219
+
5220
+ // src/index.ts
5221
+ async function main() {
5222
+ const program = buildHardkasProgram();
3409
5223
  try {
3410
5224
  await program.parseAsync(process.argv);
3411
5225
  } catch (err) {
5226
+ const { maskSecrets: maskSecrets2 } = await import("@hardkas/core");
3412
5227
  console.error(`
3413
- Error: ${err.message}`);
5228
+ Error: ${maskSecrets2(err.message || String(err))}`);
3414
5229
  process.exit(1);
3415
5230
  }
3416
5231
  }
3417
- main().catch((err) => {
3418
- console.error("Fatal error:", err);
5232
+ main().catch(async (err) => {
5233
+ const { maskSecrets: maskSecrets2 } = await import("@hardkas/core");
5234
+ console.error("Fatal error:", maskSecrets2(err.message || String(err)));
5235
+ if (err.stack) {
5236
+ console.error(maskSecrets2(err.stack));
5237
+ }
3419
5238
  process.exit(1);
3420
5239
  });