@hardkas/cli 0.2.2-alpha.1 → 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-AQWQW5ZG.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
@@ -71,35 +82,43 @@ async function runUp() {
71
82
  // src/commands/init.ts
72
83
  function registerInitCommands(program) {
73
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 path12 = await import("path");
77
- let targetDir = process.cwd();
78
- if (name) {
79
- targetDir = path12.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 = path12.join(targetDir, "hardkas.config.ts");
85
- const pkgFile = path12.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,34 +139,35 @@ 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
- const gitIgnoreFile = path12.join(targetDir, ".gitignore");
134
- const gitIgnoreEntry = "\n# HardKAS local storage\n.hardkas/\n";
135
- if (!fs10.existsSync(gitIgnoreFile)) {
136
- fs10.writeFileSync(gitIgnoreFile, gitIgnoreEntry, "utf-8");
137
- UI.info("Created: .gitignore");
138
- } else {
139
- const content = fs10.readFileSync(gitIgnoreFile, "utf-8");
140
- if (!content.includes(".hardkas/")) {
141
- fs10.appendFileSync(gitIgnoreFile, gitIgnoreEntry, "utf-8");
142
- UI.info("Updated: .gitignore (added .hardkas/)");
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
+ }
143
163
  }
144
- }
145
- UI.success(`HardKAS project '${name || "current"}' initialized successfully.`);
146
- if (name) UI.info(`Project folder: ${targetDir}`);
147
- UI.info(`Created: hardkas.config.ts (v0.2-alpha)`);
148
- UI.footer(`Run 'cd ${name || "."}' and then 'hardkas up' to start.`);
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
+ });
149
169
  } catch (e) {
150
- handleError(e, "Initialization failed");
170
+ handleLockError2(e);
151
171
  process.exitCode = 1;
152
172
  }
153
173
  });
@@ -340,6 +360,22 @@ async function runTxSign(input) {
340
360
  const { planArtifact, accountName, config, allowMainnetSigning } = input;
341
361
  const targetAccountName = accountName || planArtifact.from.accountName || planArtifact.from.input || planArtifact.from.address;
342
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
+ }
343
379
  const signedArtifact = await signTxPlanArtifact({
344
380
  planArtifact,
345
381
  account,
@@ -348,6 +384,12 @@ async function runTxSign(input) {
348
384
  });
349
385
  return signedArtifact;
350
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
+ }
351
393
 
352
394
  // src/runners/tx-send-runner.ts
353
395
  import {
@@ -436,7 +478,7 @@ async function runTxSend(input) {
436
478
  to: { address: signedArtifact.to.address },
437
479
  amountSompi: signedArtifact.amountSompi,
438
480
  feeSompi: simResult.receipt.feeSompi,
439
- daaScore: simResult.receipt.daaScore.toString(),
481
+ daaScore: simResult.receipt.daaScore?.toString() || "0",
440
482
  submittedAt: simResult.receipt.createdAt,
441
483
  confirmedAt: simResult.receipt.createdAt,
442
484
  rpcUrl: "simulated://local"
@@ -497,7 +539,7 @@ Receipt: ${receiptPath}`
497
539
  amountSompi: signedArtifact.amountSompi,
498
540
  feeSompi: signedArtifact.metadata?.estimatedFeeSompi || "0",
499
541
  submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
500
- rpcUrl
542
+ ...rpcUrl ? { rpcUrl } : {}
501
543
  };
502
544
  return {
503
545
  accepted: !!result.accepted,
@@ -681,108 +723,148 @@ async function runTxReceipt(input) {
681
723
  // src/commands/tx.ts
682
724
  function registerTxCommands(program) {
683
725
  const tx = program.command("tx").description("L1 Transaction commands");
684
- tx.command("profile <path>").description("Show detailed mass and fee breakdown for a transaction plan").action(async (path12) => {
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) => {
685
727
  try {
686
- await runTxProfile({ path: path12 });
728
+ await runTxProfile({ path: path15, ...options });
687
729
  } catch (e) {
688
730
  handleError(e);
689
731
  process.exitCode = 1;
690
732
  }
691
733
  });
692
- tx.command("plan").description(`Build a transaction plan artifact ${UI.maturity("stable")}`).option("--from <accountOrAddress>", "Sender account name or address").option("--to <address>", "Recipient address").option("--amount <kas>", "Amount in KAS").option("--network <name>", "Kaspa network name", "simnet").option("--fee-rate <sompiPerMass>", "Fee rate in sompi per mass", "1").option("--url <url>", "RPC URL (optional override)").option("--out <path>", "Save plan as artifact JSON").option("--json", "Output as JSON", false).action(async (options) => {
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");
693
737
  try {
694
- const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
695
- const { writeArtifact: writeArtifact4, formatTxPlanArtifact } = await import("@hardkas/artifacts");
696
- const loaded = await loadHardkasConfig5();
697
- const artifact = await runTxPlan({
698
- from: options.from || "alice",
699
- to: options.to || "bob",
700
- amount: options.amount || "1",
701
- networkId: options.network,
702
- feeRate: options.feeRate,
703
- config: loaded.config,
704
- ...options.url ? { url: options.url } : {}
705
- });
706
- if (options.out) await writeArtifact4(options.out, artifact);
707
- if (options.json) console.log(JSON.stringify(artifact, bigIntReplacer, 2));
708
- else {
709
- console.log(formatTxPlanArtifact(artifact));
710
- 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(`
711
762
  Artifact saved to: ${options.out}`);
712
- }
763
+ }
764
+ });
713
765
  } catch (e) {
714
- handleError(e);
766
+ handleLockError2(e);
715
767
  process.exitCode = 1;
716
768
  }
717
769
  });
718
- tx.command("sign <planPath>").description(`Sign a transaction plan artifact ${UI.maturity("stable")}`).option("--account <name>", "Account name to sign with").option("--out <path>", "Save signed artifact JSON").option("--allow-mainnet-signing", "Allow signing for mainnet", false).option("--json", "Output as JSON", false).action(async (planPath, options) => {
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");
719
773
  try {
720
- const { readTxPlanArtifact, writeArtifact: writeArtifact4, formatSignedTxArtifact } = await import("@hardkas/artifacts");
721
- const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
722
- const planArtifact = await readTxPlanArtifact(planPath);
723
- const loaded = await loadHardkasConfig5();
724
- const signedArtifact = await runTxSign({
725
- planArtifact,
726
- ...options.account ? { accountName: options.account } : {},
727
- config: loaded.config,
728
- allowMainnetSigning: options.allowMainnetSigning
729
- });
730
- if (options.out) await writeArtifact4(options.out, signedArtifact);
731
- if (options.json) console.log(JSON.stringify(signedArtifact, bigIntReplacer, 2));
732
- else {
733
- console.log(formatSignedTxArtifact(signedArtifact));
734
- 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(`
735
796
  Signed artifact saved to: ${options.out}`);
736
- }
797
+ }
798
+ });
737
799
  } catch (e) {
738
- handleError(e);
800
+ handleLockError2(e);
739
801
  process.exitCode = 1;
740
802
  }
741
803
  });
742
- tx.command("send [signedPath]").description(`Broadcast a signed transaction or send directly ${UI.maturity("stable")}`).option("--from <accountOrAddress>", "Sender (shortcut mode)").option("--to <address>", "Recipient (shortcut mode)").option("--amount <kas>", "Amount in KAS (shortcut mode)").option("--network <name>", "Network name", "simnet").option("--url <url>", "RPC URL (optional override)").option("--yes", "Confirm broadcast", false).option("--json", "Output as JSON", false).action(async (signedPath, options) => {
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");
743
807
  try {
744
- const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
745
- const loaded = await loadHardkasConfig5();
746
- if (signedPath) {
747
- const { readSignedTxArtifact } = await import("@hardkas/artifacts");
748
- const signedArtifact = await readSignedTxArtifact(signedPath);
749
- if (!options.yes && signedArtifact.networkId !== "simnet") {
750
- console.log(`Transaction is for network: ${signedArtifact.networkId}`);
751
- console.log("Run with --yes to broadcast.");
752
- 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;
753
860
  }
754
- const result = await runTxSend({
755
- signedArtifact,
756
- network: options.network,
757
- config: loaded.config,
758
- ...options.url ? { url: options.url } : {}
759
- });
760
- if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
761
- else console.log(result.formatted);
762
- } else if (options.from && options.to && options.amount) {
763
- const result = await runTxFlow({
764
- ...options,
765
- amount: options.amount,
766
- from: options.from,
767
- to: options.to,
768
- send: true,
769
- feeRate: "1",
770
- // Default fee rate for shortcut
771
- config: loaded.config,
772
- ...options.url ? { url: options.url } : {}
773
- });
774
- if (options.json) console.log(JSON.stringify(result, bigIntReplacer, 2));
775
- else console.log(result.steps.send.artifact?.formatted || "Flow completed");
776
- } else {
777
- console.error("Provide a path to a signed artifact or use --from, --to, --amount.");
778
- process.exitCode = 1;
779
- }
861
+ });
780
862
  } catch (e) {
781
- handleError(e);
863
+ handleLockError2(e);
782
864
  process.exitCode = 1;
783
865
  }
784
866
  });
785
- 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) => {
786
868
  try {
787
869
  const result = await runTxReceipt({ txId });
788
870
  if (options.json) console.log(JSON.stringify(result.receipt, bigIntReplacer, 2));
@@ -792,19 +874,19 @@ Signed artifact saved to: ${options.out}`);
792
874
  process.exitCode = 1;
793
875
  }
794
876
  });
795
- tx.command("verify <path>").description(`Perform deep semantic verification of a transaction plan ${UI.maturity("preview")}`).option("--json", "Output as JSON", false).action(async (path12, options) => {
796
- const { runTxVerify } = await import("./tx-verify-runner-Z5M2JDRI.js");
797
- await runTxVerify({ path: path12, ...options });
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 });
798
880
  });
799
881
  tx.command("trace <txId>").description(`Reconstruct the full operational trace of a transaction ${UI.maturity("research")}`).action(async (txId) => {
800
- const { UI: UI2 } = await import("./ui-OVK5PX6H.js");
882
+ const { UI: UI2 } = await import("./ui-SUYOHGGP.js");
801
883
  UI2.error("Tracing is temporarily disabled while the query API stabilizes.");
802
884
  process.exitCode = 1;
803
885
  });
804
886
  }
805
887
 
806
888
  // src/runners/artifact-verify-runner.ts
807
- import { verifyArtifactIntegrity, verifyArtifactSemantics } from "@hardkas/artifacts";
889
+ import { verifyArtifactIntegrity, verifyArtifactSemantics, verifyArtifactReplay } from "@hardkas/artifacts";
808
890
  import path4 from "path";
809
891
  import fs3 from "fs";
810
892
  async function runArtifactVerify(options) {
@@ -828,8 +910,11 @@ async function runArtifactVerify(options) {
828
910
  let result = await verifyArtifactIntegrity(absolutePath);
829
911
  const artifact = JSON.parse(fs3.readFileSync(absolutePath, "utf-8"));
830
912
  const semanticResult = verifyArtifactSemantics(artifact, { strict: options.strict ?? false });
913
+ const replayResult = await verifyArtifactReplay(artifact, { strict: options.strict ?? false });
831
914
  result.issues.push(...semanticResult.issues);
915
+ result.issues.push(...replayResult.issues);
832
916
  result.errors.push(...semanticResult.errors);
917
+ result.errors.push(...replayResult.errors);
833
918
  result.ok = result.ok && semanticResult.ok;
834
919
  if (options.json) {
835
920
  console.log(JSON.stringify(result, null, 2));
@@ -844,11 +929,22 @@ async function runArtifactVerify(options) {
844
929
  if (options.strict) {
845
930
  console.log(`
846
931
  Operational Audit (STRICT):`);
847
- const feeAudit = verifyArtifactSemantics(artifact, { strict: true });
848
- if (feeAudit.ok) {
849
- 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)");
850
946
  } else {
851
- UI.error(" \u2717 Economic invariants VIOLATED.");
947
+ UI.error(" \u2717 Replay verification FAILED.");
852
948
  }
853
949
  }
854
950
  } else {
@@ -985,7 +1081,8 @@ async function runArtifactExplain(options) {
985
1081
  if (explanation.security.strictOk) {
986
1082
  UI.success(" \u2713 No critical integrity violations detected.");
987
1083
  } else {
988
- UI.error(" \u2717 SECUIRTY WARNINGS DETECTED.");
1084
+ UI.error(" \u2717 SECURITY WARNINGS DETECTED.");
1085
+ process.exitCode = 1;
989
1086
  }
990
1087
  if (explanation.security.issues.length > 0) {
991
1088
  explanation.security.issues.forEach((issue) => {
@@ -999,26 +1096,26 @@ async function runArtifactExplain(options) {
999
1096
  // src/commands/artifact.ts
1000
1097
  function registerArtifactCommands(program) {
1001
1098
  const artifactCmd = program.command("artifact").description("Manage HardKAS artifacts");
1002
- artifactCmd.command("verify <path>").description(`Verify an artifact's integrity and schema ${UI.maturity("stable")}`).option("--json", "Output results as JSON", false).option("--recursive", "Recursively verify all artifacts in a directory", false).option("--strict", "Perform deep semantic and operational safety verification", false).action(async (path12, options) => {
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) => {
1003
1100
  try {
1004
- await runArtifactVerify({ path: path12, ...options });
1101
+ await runArtifactVerify({ path: path15, ...options });
1005
1102
  } catch (e) {
1006
1103
  handleError(e);
1007
1104
  process.exitCode = 1;
1008
1105
  }
1009
1106
  });
1010
- artifactCmd.command("explain <path>").description(`Provide a human-readable operational summary of an artifact ${UI.maturity("preview")}`).action(async (path12) => {
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) => {
1011
1108
  try {
1012
- await runArtifactExplain({ path: path12 });
1109
+ await runArtifactExplain({ path: path15, ...options });
1013
1110
  } catch (e) {
1014
1111
  handleError(e);
1015
1112
  process.exitCode = 1;
1016
1113
  }
1017
1114
  });
1018
- artifactCmd.command("lineage <path>").description(`Show the provenance and operational history of an artifact ${UI.maturity("preview")}`).action(async (path12) => {
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) => {
1019
1116
  try {
1020
- const { runArtifactLineage } = await import("./artifact-lineage-runner-RJWQ3R3X.js");
1021
- await runArtifactLineage({ path: path12 });
1117
+ const { runArtifactLineage } = await import("./artifact-lineage-runner-TY4YTE6H.js");
1118
+ await runArtifactLineage({ path: path15 });
1022
1119
  } catch (e) {
1023
1120
  handleError(e);
1024
1121
  process.exitCode = 1;
@@ -1029,22 +1126,22 @@ function registerArtifactCommands(program) {
1029
1126
  // src/commands/replay.ts
1030
1127
  function registerReplayCommands(program) {
1031
1128
  const replayCmd = program.command("replay").description("Manage HardKAS transaction replays");
1032
- replayCmd.command("verify <path>").description("Verify replay invariants for a directory of artifacts").action(async (path12) => {
1033
- const { runReplayVerify } = await import("./replay-verify-runner-UMYALHNT.js");
1034
- await runReplayVerify({ path: path12 });
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 });
1035
1132
  });
1036
1133
  }
1037
1134
 
1038
1135
  // src/commands/snapshot.ts
1039
1136
  function registerSnapshotCommands(program) {
1040
1137
  const snapshotCmd = program.command("snapshot").description("Manage HardKAS localnet snapshots");
1041
- snapshotCmd.command("verify <idOrName>").description("Verify the integrity of a snapshot").action(async (idOrName) => {
1042
- const { runSnapshotVerify } = await import("./snapshot-verify-runner-BWRW3NUW.js");
1043
- 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 });
1044
1141
  });
1045
- snapshotCmd.command("restore <idOrName>").description("Restore localnet state from a snapshot").action(async (idOrName) => {
1046
- const { runSnapshotRestore } = await import("./snapshot-restore-runner-QNAADGBX.js");
1047
- 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 });
1048
1145
  });
1049
1146
  }
1050
1147
 
@@ -1230,7 +1327,7 @@ function registerRpcCommands(program) {
1230
1327
  }
1231
1328
  });
1232
1329
  rpcCmd.command("doctor").description("Run comprehensive RPC diagnostics").option("--endpoints <urls...>", "Specific endpoints to audit").action(async (options) => {
1233
- const { runRpcDoctor } = await import("./rpc-doctor-runner-ERWXOXSE.js");
1330
+ const { runRpcDoctor } = await import("./rpc-doctor-runner-V7H7AAX4.js");
1234
1331
  try {
1235
1332
  await runRpcDoctor(options);
1236
1333
  } catch (e) {
@@ -1265,7 +1362,7 @@ function registerDagCommands(program) {
1265
1362
  const dagCmd = program.command("dag").description("Simulate blockDAG operations (Localnet only)");
1266
1363
  dagCmd.command("status").description("View current DAG status").action(async () => {
1267
1364
  try {
1268
- const { runDagStatus } = await import("./dag-runners-YQDHD7U6.js");
1365
+ const { runDagStatus } = await import("./dag-runners-NLBYR7I7.js");
1269
1366
  await runDagStatus();
1270
1367
  } catch (e) {
1271
1368
  handleError(e);
@@ -1274,7 +1371,7 @@ function registerDagCommands(program) {
1274
1371
  });
1275
1372
  dagCmd.command("simulate-reorg").description("Simulate a DAG reorg").option("--depth <n>", "Reorg depth", "1").action(async (options) => {
1276
1373
  try {
1277
- const { runDagSimulateReorg } = await import("./dag-runners-YQDHD7U6.js");
1374
+ const { runDagSimulateReorg } = await import("./dag-runners-NLBYR7I7.js");
1278
1375
  await runDagSimulateReorg({ depth: parseInt(options.depth) });
1279
1376
  } catch (e) {
1280
1377
  handleError(e);
@@ -1318,21 +1415,58 @@ import {
1318
1415
  loadOrCreateRealAccountStore,
1319
1416
  saveRealAccountStore as saveRealAccountStore2,
1320
1417
  importRealDevAccount,
1321
- KaspaSdkKeyGenerator
1418
+ KaspaSdkKeyGenerator,
1419
+ KeystoreManager
1322
1420
  } from "@hardkas/accounts";
1421
+ import path6 from "path";
1323
1422
  async function runAccountsRealGenerate(options = {}) {
1324
1423
  const generator = new KaspaSdkKeyGenerator(options.networkId ? { networkId: options.networkId } : {});
1325
1424
  const count = options.count || 1;
1326
1425
  let store = await loadOrCreateRealAccountStore();
1327
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
+ }
1328
1442
  for (let i = 0; i < count; i++) {
1329
1443
  const name = count === 1 && options.name ? options.name : options.name ? `${options.name}${i + 1}` : `account${i}`;
1330
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
+ }
1331
1464
  store = importRealDevAccount(store, {
1332
1465
  name,
1333
1466
  address: generated.address,
1334
1467
  ...generated.publicKey ? { publicKey: generated.publicKey } : {},
1335
- ...generated.privateKey ? { privateKey: generated.privateKey } : {}
1468
+ ...options.unsafePlaintext && generated.privateKey ? { privateKey: generated.privateKey } : {},
1469
+ ...keystoreRef ? { keystoreRef } : {}
1336
1470
  });
1337
1471
  generatedAccounts.push(store.accounts[store.accounts.length - 1]);
1338
1472
  }
@@ -1414,11 +1548,13 @@ async function runAccountsFund(options) {
1414
1548
  const loadedConfig = await loadHardkasConfig3({});
1415
1549
  const address = resolveHardkasAccountAddress2(options.identifier, loadedConfig.config);
1416
1550
  const networkId = loadedConfig.config.defaultNetwork || "simnet";
1551
+ const networkConfig = loadedConfig.config.networks?.[networkId];
1552
+ const isSimulated = networkId === "simulated" || networkId === "localnet" || networkConfig?.kind === "simulated";
1417
1553
  const allowedNetworks = ["simnet", "localnet", "dev", "simulated"];
1418
- if (!allowedNetworks.includes(networkId)) {
1554
+ if (!allowedNetworks.includes(networkId) && !isSimulated) {
1419
1555
  throw new Error(`Faucet/Funding is only allowed on development networks (${allowedNetworks.join(", ")}). Current network is: ${networkId}`);
1420
1556
  }
1421
- if (networkId === "simulated" || networkId === "localnet") {
1557
+ if (isSimulated) {
1422
1558
  const state = await loadOrCreateLocalnetState2();
1423
1559
  const amount = options.amountSompi || 1000n * 100000000n;
1424
1560
  const newState = fundAddress(state, { address, amountSompi: amount });
@@ -1443,11 +1579,11 @@ Hint: Start your node with 'hardkas node start --miningaddr ${address}' to mine
1443
1579
  // src/commands/accounts.ts
1444
1580
  function registerAccountsCommands(program) {
1445
1581
  const accountsCmd = program.command("accounts").description("Manage HardKAS accounts");
1446
- accountsCmd.command("list").description("List available HardKAS accounts").option("--config <path>", "Path to config file").option("--json", "Output as JSON", false).action(async (options) => {
1447
- const { loadHardkasConfig: loadHardkasConfig5 } = 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");
1448
1584
  const { listHardkasAccounts: listHardkasAccounts2, describeAccount } = await import("@hardkas/accounts");
1449
1585
  try {
1450
- const loaded = await loadHardkasConfig5(options.config ? { configPath: options.config } : {});
1586
+ const loaded = await loadHardkasConfig15(options.config ? { configPath: options.config } : {});
1451
1587
  const accounts = listHardkasAccounts2(loaded.config);
1452
1588
  if (options.json) {
1453
1589
  console.log(JSON.stringify(accounts.map((a) => describeAccount(a)), null, 2));
@@ -1465,70 +1601,117 @@ function registerAccountsCommands(program) {
1465
1601
  }
1466
1602
  });
1467
1603
  const realAccountsCmd = accountsCmd.command("real").description("Persistent dev account store (L1)");
1468
- 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");
1469
1607
  try {
1470
- const result = await runAccountsRealInit({ force: options.force });
1471
- if (options.json) console.log(JSON.stringify(result, null, 2));
1472
- 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
+ });
1473
1619
  } catch (e) {
1474
- handleError(e);
1620
+ handleLockError2(e);
1475
1621
  process.exitCode = 1;
1476
1622
  }
1477
1623
  });
1478
- realAccountsCmd.command("import").description("Import an account into the persistent store").option("--name <name>", "Account name").option("--address <address>", "Kaspa address").option("--private-key <hex>", "Private key (plaintext, discouraged)").option("--encrypted", "Import as encrypted keystore (recommended)", false).option("--json", "Output as JSON", false).action(async (options) => {
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");
1479
1627
  try {
1480
- const { runAccountsKeystoreImport } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
1481
- const result = await runAccountsKeystoreImport(options);
1482
- if (options.json) console.log(JSON.stringify(result, null, 2));
1483
- 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
+ });
1484
1640
  } catch (e) {
1485
- handleError(e);
1641
+ handleLockError2(e);
1486
1642
  process.exitCode = 1;
1487
1643
  }
1488
1644
  });
1489
- realAccountsCmd.command("unlock <name>").description(`Verify keystore access ${UI.maturity("internal")}`).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");
1490
1648
  try {
1491
- const { runAccountsKeystoreUnlock } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
1492
- await runAccountsKeystoreUnlock({ name });
1493
- console.log(`
1494
- \u2713 Access to account '${name}' verified.`);
1495
- console.log(` (Note: HardKAS CLI is stateless. Password will be required again for signing operations.)
1496
- `);
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
+ });
1497
1659
  } catch (e) {
1498
- handleError(e);
1660
+ handleLockError2(e);
1499
1661
  process.exitCode = 1;
1500
1662
  }
1501
1663
  });
1502
- realAccountsCmd.command("lock <name>").description(`[DEPRECATED] Lock an account ${UI.maturity("internal")}`).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) => {
1503
1665
  console.log(`
1504
1666
  \u2139 Account '${name}' session clear (redundant).`);
1505
1667
  console.log(` The CLI is already stateless. No secrets are stored in memory between commands.
1506
1668
  `);
1507
1669
  });
1508
- realAccountsCmd.command("change-password <name>").description("Change password for an encrypted account").action(async (name) => {
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");
1509
1673
  try {
1510
- const { runAccountsKeystoreChangePassword } = await import("./accounts-keystore-runners-MCJIAGZ4.js");
1511
- await runAccountsKeystoreChangePassword({ name });
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
+ });
1512
1684
  } catch (e) {
1513
- handleError(e);
1685
+ handleLockError2(e);
1514
1686
  process.exitCode = 1;
1515
1687
  }
1516
1688
  });
1517
- 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) => {
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");
1518
1692
  try {
1519
- const result = await runAccountsRealGenerate({
1520
- ...options.name ? { name: options.name } : {},
1521
- count: parseInt(options.count, 10),
1522
- networkId: options.network
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);
1523
1708
  });
1524
- if (options.json) console.log(JSON.stringify(result.accounts, null, 2));
1525
- else console.log(result.formatted);
1526
1709
  } catch (e) {
1527
- handleError(e);
1710
+ handleLockError2(e);
1528
1711
  process.exitCode = 1;
1529
1712
  }
1530
1713
  });
1531
- accountsCmd.command("balance <identifier>").description("Show account balance").option("--network <name>", "Network name (simnet, localnet, etc.)").option("--url <rpc-url>", "Explicit RPC URL").option("--json", "Output as JSON", false).action(async (identifier, options) => {
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) => {
1532
1715
  try {
1533
1716
  const result = await runAccountsBalance({ identifier, network: options.network ?? "simnet", url: options.url ?? "" });
1534
1717
  if (options.json) {
@@ -1560,8 +1743,10 @@ Account: ${result.name}`);
1560
1743
 
1561
1744
  // src/runners/l2-networks-runner.ts
1562
1745
  import { listL2Profiles } from "@hardkas/l2";
1746
+ import { loadHardkasConfig as loadHardkasConfig4 } from "@hardkas/config";
1563
1747
  async function runL2Networks(options = {}) {
1564
- const profiles = listL2Profiles();
1748
+ const loaded = await loadHardkasConfig4();
1749
+ const profiles = listL2Profiles(loaded.config.l2?.networks);
1565
1750
  if (options.json) {
1566
1751
  console.log(JSON.stringify(profiles, null, 2));
1567
1752
  return;
@@ -1572,20 +1757,30 @@ async function runL2Networks(options = {}) {
1572
1757
  console.log("No L2 profiles found.");
1573
1758
  return;
1574
1759
  }
1760
+ console.log(`${"name".padEnd(16)} ${"source".padEnd(14)} ${"type".padEnd(20)} ${"bridge".padEnd(10)} ${"exit"}`);
1761
+ console.log("\u2500".repeat(70));
1575
1762
  for (const p of profiles) {
1576
1763
  const bridge = p.security.bridgePhase;
1577
1764
  const exit = p.security.trustlessExit ? "yes" : "no";
1578
- 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}`);
1579
1767
  }
1580
1768
  }
1581
1769
 
1582
1770
  // src/runners/l2-profile-show-runner.ts
1583
- import { getL2Profile } from "@hardkas/l2";
1771
+ import { resolveL2Profile } from "@hardkas/l2";
1772
+ import { loadHardkasConfig as loadHardkasConfig5 } from "@hardkas/config";
1584
1773
  async function runL2ProfileShow(options) {
1585
- const profile = getL2Profile(options.name);
1586
- if (!profile) {
1587
- throw new Error(`L2 profile '${options.name}' not found.`);
1588
- }
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
+ });
1589
1784
  if (options.json) {
1590
1785
  console.log(JSON.stringify(profile, null, 2));
1591
1786
  return;
@@ -1593,10 +1788,13 @@ async function runL2ProfileShow(options) {
1593
1788
  console.log("L2 profile");
1594
1789
  console.log("");
1595
1790
  console.log(`Name: ${profile.name}`);
1791
+ console.log(`Source: ${profile.source}`);
1596
1792
  console.log(`Display: ${profile.displayName}`);
1597
1793
  console.log(`Type: ${profile.type}`);
1598
1794
  console.log(`Settlement: ${profile.settlementLayer === "kaspa" ? "Kaspa L1" : profile.settlementLayer}`);
1599
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"}`);
1600
1798
  console.log(`Gas token: ${profile.gasToken}`);
1601
1799
  console.log(`Bridge: ${profile.security.bridgePhase}`);
1602
1800
  console.log(`Trustless exit: ${profile.security.trustlessExit ? "yes" : "no"}`);
@@ -1611,22 +1809,48 @@ async function runL2ProfileShow(options) {
1611
1809
  }
1612
1810
 
1613
1811
  // src/runners/l2-profile-validate-runner.ts
1614
- 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";
1615
1814
  async function runL2ProfileValidate(options) {
1616
- const profile = getL2Profile2(options.name);
1617
- if (!profile) {
1618
- throw new Error(`L2 profile '${options.name}' not found.`);
1619
- }
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
+ });
1620
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;
1621
1840
  if (options.json) {
1622
- console.log(JSON.stringify(result, null, 2));
1841
+ console.log(JSON.stringify({ ok: finalOk, errors, profile, rpcVerified }, null, 2));
1623
1842
  return;
1624
1843
  }
1625
- if (result.ok) {
1626
- 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
+ }
1627
1851
  } else {
1628
- console.log(`L2 profile '${options.name}' is INVALID:`);
1629
- for (const err of result.errors) {
1852
+ console.log(`\u2717 L2 profile '${profile.name}' is INVALID:`);
1853
+ for (const err of errors) {
1630
1854
  console.log(` - ${err}`);
1631
1855
  }
1632
1856
  process.exit(1);
@@ -1635,13 +1859,14 @@ async function runL2ProfileValidate(options) {
1635
1859
 
1636
1860
  // src/runners/l2-tx-runners.ts
1637
1861
  import fs5 from "fs/promises";
1638
- import path6 from "path";
1862
+ import path7 from "path";
1639
1863
  import {
1640
- getL2Profile as getL2Profile3,
1641
- EvmJsonRpcClient,
1864
+ resolveL2Profile as resolveL2Profile3,
1865
+ EvmJsonRpcClient as EvmJsonRpcClient2,
1642
1866
  toHexQuantity,
1643
1867
  normalizeEvmTransactionReceipt
1644
1868
  } from "@hardkas/l2";
1869
+ import { loadHardkasConfig as loadHardkasConfig7 } from "@hardkas/config";
1645
1870
  import {
1646
1871
  assertValidIgraTxPlanArtifact,
1647
1872
  assertValidIgraSignedTxArtifact,
@@ -1661,16 +1886,20 @@ import {
1661
1886
  resolveRealAccountOrAddress
1662
1887
  } from "@hardkas/accounts";
1663
1888
  async function runL2TxBuild(options) {
1664
- const networkName = options.network ?? "igra";
1665
- const profile = getL2Profile3(networkName);
1666
- if (!profile) {
1667
- throw new Error(`L2 profile '${networkName}' not found.`);
1668
- }
1669
- 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;
1670
1899
  if (!rpcUrl) {
1671
- 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>.`);
1672
1901
  }
1673
- const client = new EvmJsonRpcClient({ url: rpcUrl });
1902
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
1674
1903
  if (options.from) assertEvmAddress(options.from, "from");
1675
1904
  if (!options.to) {
1676
1905
  throw new Error("Missing 'to' address. For this phase, 'to' is required.");
@@ -1729,26 +1958,26 @@ async function runL2TxBuild(options) {
1729
1958
  artifact.contentHash = hash;
1730
1959
  assertValidIgraTxPlanArtifact(artifact);
1731
1960
  const outDir = options.outDir || "plans";
1732
- const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1961
+ const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1733
1962
  await fs5.mkdir(sanitizedDir, { recursive: true });
1734
- const artifactPath = path6.join(sanitizedDir, `${planId}.igra.plan.json`);
1963
+ const artifactPath = path7.join(sanitizedDir, `${planId}.igra.plan.json`);
1735
1964
  await writeArtifact2(artifactPath, artifact);
1736
1965
  if (options.json) {
1737
1966
  console.log(JSON.stringify({
1738
1967
  networkId: profile.name,
1739
- l2Network: networkName,
1740
- chainId,
1968
+ l2Network: profile.name,
1969
+ chainId: profile.chainId,
1741
1970
  planId,
1742
1971
  artifactPath,
1743
1972
  artifact
1744
1973
  }, null, 2));
1745
1974
  return;
1746
1975
  }
1747
- console.log(`${profile.displayName} L2 transaction plan built`);
1976
+ console.log(`${profile.displayName} L2 transaction plan built (${profile.source})`);
1748
1977
  console.log("");
1749
1978
  console.log(`Plan ID: ${planId}`);
1750
- console.log(`Network: ${networkName}`);
1751
- console.log(`Chain ID: ${chainId}`);
1979
+ console.log(`Network: ${profile.name}`);
1980
+ console.log(`Chain ID: ${profile.chainId}`);
1752
1981
  console.log(`Mode: l2-rpc`);
1753
1982
  if (options.from) console.log(`From: ${options.from}`);
1754
1983
  console.log(`To: ${options.to}`);
@@ -1843,9 +2072,9 @@ async function runL2TxSign(options) {
1843
2072
  artifact.contentHash = hash;
1844
2073
  assertValidIgraSignedTxArtifact(artifact);
1845
2074
  const outDir = options.outDir || "signed";
1846
- const sanitizedDir = path6.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2075
+ const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
1847
2076
  await fs5.mkdir(sanitizedDir, { recursive: true });
1848
- const artifactPath = path6.join(sanitizedDir, `${signedId}.igra.signed.json`);
2077
+ const artifactPath = path7.join(sanitizedDir, `${signedId}.igra.signed.json`);
1849
2078
  await writeArtifact2(artifactPath, artifact);
1850
2079
  if (options.json) {
1851
2080
  console.log(JSON.stringify({
@@ -1878,53 +2107,50 @@ async function runL2TxSign(options) {
1878
2107
  console.log(" This is an Igra L2 EVM signed transaction, not a Kaspa L1 UTXO transaction.");
1879
2108
  }
1880
2109
  async function runL2TxSend(options) {
2110
+ const loaded = await loadHardkasConfig7();
1881
2111
  const artifactData = await readArtifact2(options.signedPath);
1882
2112
  assertValidIgraSignedTxArtifact(artifactData);
1883
2113
  const artifact = artifactData;
1884
- if (artifact.schema !== ARTIFACT_SCHEMAS2.IGRA_SIGNED_TX) {
1885
- 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.");
1886
2125
  }
1887
- if (artifact.mode !== "l2-rpc") {
1888
- 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);
1889
2129
  }
1890
- if (artifact.status !== "signed") {
1891
- 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>.`);
1892
2133
  }
1893
- 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)) {
1894
2137
  console.log("");
1895
- 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.");
1896
2139
  console.log("");
1897
- console.log("Reason:");
1898
- console.log(" This operation broadcasts a signed L2 transaction.");
2140
+ console.log(`Profile chainId: ${profile.chainId}`);
2141
+ console.log(`RPC chainId: ${remoteChainId}`);
1899
2142
  console.log("");
1900
- console.log("Use:");
1901
- console.log(` hardkas l2 tx send ${options.signedPath} --yes`);
2143
+ console.log("Suggestion:");
2144
+ console.log(" Check --url and --network.");
1902
2145
  process.exit(1);
1903
2146
  }
1904
- const networkName = options.network ?? artifact.l2Network ?? "igra";
1905
- const profile = getL2Profile3(networkName);
1906
- if (!profile) {
1907
- throw new Error(`L2 profile '${networkName}' not found.`);
1908
- }
1909
- const isMainnet = networkName === "mainnet" || profile.name.includes("mainnet") || artifact.networkId === "mainnet" || artifact.chainId === 1;
1910
- if (isMainnet) {
1911
- throw new Error("L2 mainnet broadcast is disabled in HardKAS v0.2-alpha.");
1912
- }
1913
- const rpcUrl = options.url ?? profile.rpcUrl;
1914
- if (!rpcUrl) {
1915
- throw new Error(`No L2 RPC URL configured for network '${networkName}'. Pass --url <rpcUrl>.`);
1916
- }
1917
- const client = new EvmJsonRpcClient({ url: rpcUrl });
1918
- const remoteChainId = await client.getChainId();
1919
- if (remoteChainId !== artifact.chainId) {
2147
+ if (String(remoteChainId) !== String(artifact.chainId)) {
1920
2148
  console.log("");
1921
2149
  console.log("Refusing to submit Igra L2 transaction: signed artifact chainId does not match RPC endpoint.");
1922
2150
  console.log("");
1923
2151
  console.log(`Artifact chainId: ${artifact.chainId}`);
1924
2152
  console.log(`RPC chainId: ${remoteChainId}`);
1925
2153
  console.log("");
1926
- console.log("Suggestion:");
1927
- console.log(" Check --url and --network.");
1928
2154
  process.exit(1);
1929
2155
  }
1930
2156
  const txHash = await client.sendRawTransaction(artifact.rawTransaction);
@@ -1943,14 +2169,14 @@ async function runL2TxSend(options) {
1943
2169
  status: "submitted"
1944
2170
  };
1945
2171
  assertValidIgraTxReceiptArtifact(receipt);
1946
- const receiptDir = path6.join(".hardkas", "l2-receipts");
2172
+ const receiptDir = path7.join(".hardkas", "l2-receipts");
1947
2173
  await fs5.mkdir(receiptDir, { recursive: true });
1948
- const receiptPath = path6.join(receiptDir, `${txHash}.igra.receipt.json`);
2174
+ const receiptPath = path7.join(receiptDir, `${txHash}.igra.receipt.json`);
1949
2175
  await writeArtifact2(receiptPath, receipt);
1950
2176
  if (options.json) {
1951
2177
  console.log(JSON.stringify({
1952
2178
  networkId: artifact.networkId,
1953
- l2Network: networkName,
2179
+ l2Network: profile.name,
1954
2180
  chainId: artifact.chainId,
1955
2181
  rpcUrl,
1956
2182
  txHash,
@@ -1963,8 +2189,8 @@ async function runL2TxSend(options) {
1963
2189
  console.log("Igra L2 transaction submitted");
1964
2190
  console.log("");
1965
2191
  console.log(`Tx hash: ${txHash}`);
1966
- console.log(`Network: ${networkName}`);
1967
- console.log(`Chain ID: ${artifact.chainId}`);
2192
+ console.log(`Network: ${profile.name} (${profile.source})`);
2193
+ console.log(`Chain ID: ${profile.chainId}`);
1968
2194
  console.log(`Mode: l2-rpc`);
1969
2195
  console.log(`Source: ${options.signedPath}`);
1970
2196
  console.log(`RPC: ${rpcUrl}`);
@@ -1974,23 +2200,29 @@ async function runL2TxSend(options) {
1974
2200
  console.log("");
1975
2201
  console.log("Next:");
1976
2202
  console.log(" Check receipt:");
1977
- console.log(` hardkas l2 tx receipt ${txHash} --network ${networkName}`);
2203
+ console.log(` hardkas l2 tx receipt ${txHash} --network ${profile.name}`);
1978
2204
  console.log("");
1979
2205
  console.log("Warning:");
1980
2206
  console.log(" This is an Igra L2 EVM transaction, not a Kaspa L1 UTXO transaction.");
1981
2207
  }
1982
2208
  async function runL2TxReceipt(options) {
2209
+ const loaded = await loadHardkasConfig7();
1983
2210
  let localReceipt;
1984
2211
  try {
1985
2212
  localReceipt = await loadIgraTxReceiptArtifact(options.txHash);
1986
2213
  } catch (e) {
1987
2214
  }
1988
- const networkName = options.network ?? localReceipt?.l2Network ?? "igra";
1989
- const profile = getL2Profile3(networkName);
1990
- 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;
1991
2223
  let remoteReceipt = null;
1992
2224
  if (rpcUrl) {
1993
- const client = new EvmJsonRpcClient({ url: rpcUrl });
2225
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
1994
2226
  const raw = await client.getTransactionReceipt(options.txHash);
1995
2227
  remoteReceipt = normalizeEvmTransactionReceipt(raw);
1996
2228
  }
@@ -2001,7 +2233,7 @@ async function runL2TxReceipt(options) {
2001
2233
  if (options.json) {
2002
2234
  console.log(JSON.stringify({
2003
2235
  networkId: localReceipt?.networkId ?? "igra",
2004
- l2Network: networkName,
2236
+ l2Network: profile.name,
2005
2237
  chainId: localReceipt?.chainId,
2006
2238
  rpcUrl,
2007
2239
  txHash: options.txHash,
@@ -2014,7 +2246,7 @@ async function runL2TxReceipt(options) {
2014
2246
  console.log("Igra L2 transaction receipt");
2015
2247
  console.log("");
2016
2248
  console.log(`Tx hash: ${options.txHash}`);
2017
- console.log(`Network: ${networkName}`);
2249
+ console.log(`Network: ${profile.name} (${profile.source})`);
2018
2250
  if (localReceipt) {
2019
2251
  console.log(`Chain ID: ${localReceipt.chainId}`);
2020
2252
  console.log(`Local: found`);
@@ -2037,20 +2269,26 @@ async function runL2TxReceipt(options) {
2037
2269
  console.log(" This is an Igra L2 EVM transaction receipt, not a Kaspa L1 transaction.");
2038
2270
  }
2039
2271
  async function runL2TxStatus(options) {
2040
- const networkName = options.network ?? "igra";
2041
- const profile = getL2Profile3(networkName);
2042
- 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;
2043
2281
  if (!rpcUrl) {
2044
- 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>.`);
2045
2283
  }
2046
- const client = new EvmJsonRpcClient({ url: rpcUrl });
2284
+ const client = new EvmJsonRpcClient2({ url: rpcUrl });
2047
2285
  const raw = await client.getTransactionReceipt(options.txHash);
2048
2286
  const remoteReceipt = normalizeEvmTransactionReceipt(raw);
2049
2287
  const status = remoteReceipt?.status ?? "pending";
2050
2288
  if (options.json) {
2051
2289
  console.log(JSON.stringify({
2052
- networkId: profile?.name ?? networkName,
2053
- l2Network: networkName,
2290
+ networkId: profile.name,
2291
+ l2Network: profile.name,
2054
2292
  rpcUrl,
2055
2293
  txHash: options.txHash,
2056
2294
  status,
@@ -2061,7 +2299,7 @@ async function runL2TxStatus(options) {
2061
2299
  console.log("Igra L2 transaction status");
2062
2300
  console.log("");
2063
2301
  console.log(`Tx hash: ${options.txHash}`);
2064
- console.log(`Network: ${networkName}`);
2302
+ console.log(`Network: ${profile.name} (${profile.source})`);
2065
2303
  console.log(`Status: ${status}`);
2066
2304
  if (remoteReceipt) {
2067
2305
  console.log(`Block: ${remoteReceipt.blockNumber ?? "unknown"}`);
@@ -2081,13 +2319,14 @@ function assertHexData(data, field) {
2081
2319
 
2082
2320
  // src/runners/l2-contract-runners.ts
2083
2321
  import fs6 from "fs/promises";
2084
- import path7 from "path";
2322
+ import path8 from "path";
2085
2323
  import {
2086
- getL2Profile as getL2Profile4,
2087
- EvmJsonRpcClient as EvmJsonRpcClient2,
2324
+ resolveL2Profile as resolveL2Profile4,
2325
+ EvmJsonRpcClient as EvmJsonRpcClient3,
2088
2326
  toHexQuantity as toHexQuantity2,
2089
2327
  encodeConstructorArgs
2090
2328
  } from "@hardkas/l2";
2329
+ import { loadHardkasConfig as loadHardkasConfig8 } from "@hardkas/config";
2091
2330
  import {
2092
2331
  assertValidIgraTxPlanArtifact as assertValidIgraTxPlanArtifact2,
2093
2332
  writeArtifact as writeArtifact3,
@@ -2097,16 +2336,19 @@ import {
2097
2336
  calculateContentHash as calculateContentHash2
2098
2337
  } from "@hardkas/artifacts";
2099
2338
  async function runL2ContractDeployPlan(options) {
2100
- const networkName = options.network ?? "igra";
2101
- const profile = getL2Profile4(networkName);
2102
- if (!profile) {
2103
- throw new Error(`L2 profile '${networkName}' not found.`);
2104
- }
2105
- 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;
2106
2348
  if (!rpcUrl) {
2107
- 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>.`);
2108
2350
  }
2109
- const client = new EvmJsonRpcClient2({ url: rpcUrl });
2351
+ const client = new EvmJsonRpcClient3({ url: rpcUrl });
2110
2352
  assertEvmAddress2(options.from, "from");
2111
2353
  if (!options.bytecode || options.bytecode === "0x") {
2112
2354
  throw new Error("Missing or empty bytecode. Provide non-empty 0x-prefixed hex.");
@@ -2168,26 +2410,26 @@ async function runL2ContractDeployPlan(options) {
2168
2410
  artifact.contentHash = hash;
2169
2411
  assertValidIgraTxPlanArtifact2(artifact);
2170
2412
  const outDir = options.outDir || "plans";
2171
- const sanitizedDir = path7.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2413
+ const sanitizedDir = path8.normalize(outDir).replace(/^(\.\.[\/\\])+/, "");
2172
2414
  await fs6.mkdir(sanitizedDir, { recursive: true });
2173
- const artifactPath = path7.join(sanitizedDir, `${planId}.igra.deploy.plan.json`);
2415
+ const artifactPath = path8.join(sanitizedDir, `${planId}.igra.deploy.plan.json`);
2174
2416
  await writeArtifact3(artifactPath, artifact);
2175
2417
  if (options.json) {
2176
2418
  console.log(JSON.stringify({
2177
2419
  networkId: profile.name,
2178
- l2Network: networkName,
2179
- chainId,
2420
+ l2Network: profile.name,
2421
+ chainId: profile.chainId,
2180
2422
  planId,
2181
2423
  artifactPath,
2182
2424
  artifact
2183
2425
  }, null, 2));
2184
2426
  return;
2185
2427
  }
2186
- console.log(`Igra L2 contract deploy plan built`);
2428
+ console.log(`Igra L2 contract deploy plan built (${profile.source})`);
2187
2429
  console.log("");
2188
2430
  console.log(`Plan ID: ${planId}`);
2189
- console.log(`Network: ${networkName}`);
2190
- console.log(`Chain ID: ${chainId}`);
2431
+ console.log(`Network: ${profile.name}`);
2432
+ console.log(`Chain ID: ${profile.chainId}`);
2191
2433
  console.log(`Mode: l2-rpc`);
2192
2434
  console.log(`From: ${options.from}`);
2193
2435
  console.log(`Type: contract-deploy`);
@@ -2220,18 +2462,24 @@ function assertHexData2(data, field) {
2220
2462
  }
2221
2463
 
2222
2464
  // src/runners/l2-bridge-runners.ts
2223
- 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";
2224
2468
  async function runL2BridgeStatus(options) {
2225
- const networkName = options.network ?? "igra";
2226
- const assumptions = getL2BridgeAssumptions(networkName);
2227
- if (!assumptions) {
2228
- throw new Error(`No bridge assumptions found for network '${networkName}'.`);
2229
- }
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);
2230
2478
  if (options.json) {
2231
2479
  console.log(JSON.stringify(assumptions, null, 2));
2232
2480
  return;
2233
2481
  }
2234
- console.log(`${capitalize(networkName)} bridge status`);
2482
+ console.log(`${capitalize(profile.name)} bridge status (${profile.source})`);
2235
2483
  console.log("");
2236
2484
  console.log(`Network: ${assumptions.l2Network}`);
2237
2485
  console.log(`Bridge phase: ${assumptions.bridgePhase}`);
@@ -2250,16 +2498,20 @@ async function runL2BridgeStatus(options) {
2250
2498
  }
2251
2499
  }
2252
2500
  async function runL2BridgeAssumptions(options) {
2253
- const networkName = options.network ?? "igra";
2254
- const assumptions = getL2BridgeAssumptions(networkName);
2255
- if (!assumptions) {
2256
- throw new Error(`No bridge assumptions found for network '${networkName}'.`);
2257
- }
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);
2258
2510
  if (options.json) {
2259
2511
  console.log(JSON.stringify(assumptions, null, 2));
2260
2512
  return;
2261
2513
  }
2262
- console.log(`${capitalize(networkName)} bridge assumptions`);
2514
+ console.log(`${capitalize(profile.name)} bridge assumptions (${profile.source})`);
2263
2515
  console.log("");
2264
2516
  console.log("Bridge security phases:");
2265
2517
  console.log(" pre-ZK: stronger trust assumptions / non-trustless exit");
@@ -2272,21 +2524,39 @@ async function runL2BridgeAssumptions(options) {
2272
2524
  console.log("Trustless exit:");
2273
2525
  console.log(` ${assumptions.trustlessExit ? "yes" : "no"}`);
2274
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
+ }
2275
2541
  function capitalize(s) {
2276
2542
  return s.charAt(0).toUpperCase() + s.slice(1);
2277
2543
  }
2278
2544
 
2279
2545
  // src/runners/l2-rpc-health-runner.ts
2280
- 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";
2281
2548
  async function runL2RpcHealth(options) {
2282
- const networkName = options.network ?? "igra";
2283
- const profile = getL2Profile5(networkName);
2284
- if (!profile) {
2285
- throw new Error(`L2 profile '${networkName}' not found.`);
2286
- }
2287
- 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;
2288
2558
  if (!rpcUrl) {
2289
- 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>.`);
2290
2560
  }
2291
2561
  const healthOptions = {
2292
2562
  url: rpcUrl,
@@ -2295,63 +2565,78 @@ async function runL2RpcHealth(options) {
2295
2565
  maxWaitMs: (options.timeout ?? 60) * 1e3
2296
2566
  };
2297
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
+ }
2298
2572
  if (options.json) {
2299
- 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));
2300
2574
  return;
2301
2575
  }
2302
- console.log(`${profile.displayName} L2 RPC health`);
2576
+ console.log(`${profile.displayName} L2 RPC health (${profile.source})`);
2303
2577
  console.log("");
2304
- console.log(`Network: ${networkName}`);
2578
+ console.log(`Network: ${profile.name}`);
2305
2579
  console.log(`URL: ${health.url}`);
2306
2580
  console.log(`Status: ${health.ready ? "ready" : "not ready"}`);
2307
2581
  if (health.ready) {
2308
- console.log(`Chain ID: ${health.chainId}`);
2582
+ console.log(`Chain ID: ${health.chainId} ${chainIdMismatch ? `(CONFLICT! Expected ${profile.chainId})` : ""}`);
2309
2583
  console.log(`Block: ${health.blockNumber}`);
2310
2584
  console.log(`Gas: ${health.gasPriceWei} wei`);
2311
2585
  if (health.latencyMs !== void 0) {
2312
2586
  console.log(`Latency: ${health.latencyMs}ms`);
2313
2587
  }
2314
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
+ }
2315
2595
  if (health.error) {
2316
2596
  console.log(`Error: ${health.error}`);
2317
2597
  }
2318
2598
  console.log("");
2319
2599
  console.log("Warning:");
2320
2600
  console.log(" This is L2 EVM state, not Kaspa L1 UTXO state.");
2321
- if (!health.ready) {
2601
+ if (!health.ready || chainIdMismatch) {
2322
2602
  process.exitCode = 1;
2323
2603
  }
2324
2604
  }
2325
2605
 
2326
2606
  // src/runners/l2-account-runners.ts
2327
- 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";
2328
2609
  async function getClient(options) {
2329
- const networkName = options.network ?? "igra";
2330
- const profile = getL2Profile6(networkName);
2331
- if (!profile) {
2332
- throw new Error(`L2 profile '${networkName}' not found.`);
2333
- }
2334
- 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;
2335
2620
  if (!rpcUrl) {
2336
- 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>.`);
2337
2622
  }
2338
2623
  return {
2339
- client: new EvmJsonRpcClient3({ url: rpcUrl }),
2340
- profile,
2341
- networkName
2624
+ client: new EvmJsonRpcClient4({ url: rpcUrl }),
2625
+ profile
2342
2626
  };
2343
2627
  }
2344
2628
  async function runL2Balance(address, options) {
2345
- const { client, profile, networkName } = await getClient(options);
2629
+ const { client, profile } = await getClient(options);
2346
2630
  const blockTag = options.block ?? "latest";
2347
2631
  const balanceWei = await client.getBalanceWei(address, blockTag);
2348
2632
  const balanceFormatted = formatWeiAsEtherLike(balanceWei, profile.gasToken, profile.nativeTokenDecimals);
2349
2633
  if (options.json) {
2350
2634
  console.log(JSON.stringify({
2351
2635
  networkId: profile.name,
2352
- l2Network: networkName,
2636
+ l2Network: profile.name,
2353
2637
  chainId: profile.chainId,
2354
2638
  rpcUrl: profile.rpcUrl,
2639
+ source: profile.source,
2355
2640
  address,
2356
2641
  block: blockTag,
2357
2642
  balanceWei: balanceWei.toString(),
@@ -2360,9 +2645,9 @@ async function runL2Balance(address, options) {
2360
2645
  }, null, 2));
2361
2646
  return;
2362
2647
  }
2363
- console.log(`${profile.displayName} L2 balance`);
2648
+ console.log(`${profile.displayName} L2 balance (${profile.source})`);
2364
2649
  console.log("");
2365
- console.log(`Network: ${networkName}`);
2650
+ console.log(`Network: ${profile.name}`);
2366
2651
  console.log(`Address: ${address}`);
2367
2652
  console.log(`Block: ${blockTag}`);
2368
2653
  console.log(`Balance: ${balanceFormatted}`);
@@ -2372,24 +2657,25 @@ async function runL2Balance(address, options) {
2372
2657
  console.log(" This is L2 EVM account state, not Kaspa L1 UTXO state.");
2373
2658
  }
2374
2659
  async function runL2Nonce(address, options) {
2375
- const { client, profile, networkName } = await getClient(options);
2660
+ const { client, profile } = await getClient(options);
2376
2661
  const blockTag = options.block ?? "latest";
2377
2662
  const nonce = await client.getTransactionCount(address, blockTag);
2378
2663
  if (options.json) {
2379
2664
  console.log(JSON.stringify({
2380
2665
  networkId: profile.name,
2381
- l2Network: networkName,
2666
+ l2Network: profile.name,
2382
2667
  chainId: profile.chainId,
2383
2668
  rpcUrl: profile.rpcUrl,
2669
+ source: profile.source,
2384
2670
  address,
2385
2671
  block: blockTag,
2386
2672
  nonce: nonce.toString()
2387
2673
  }, null, 2));
2388
2674
  return;
2389
2675
  }
2390
- console.log(`${profile.displayName} L2 nonce`);
2676
+ console.log(`${profile.displayName} L2 nonce (${profile.source})`);
2391
2677
  console.log("");
2392
- console.log(`Network: ${networkName}`);
2678
+ console.log(`Network: ${profile.name}`);
2393
2679
  console.log(`Address: ${address}`);
2394
2680
  console.log(`Block: ${blockTag}`);
2395
2681
  console.log(`Nonce: ${nonce}`);
@@ -2405,14 +2691,14 @@ function registerL2Commands(program) {
2405
2691
  await runL2Networks(options);
2406
2692
  });
2407
2693
  const l2Profile = l2.command("profile").description("L2 profile management");
2408
- 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) => {
2409
2695
  await runL2ProfileShow({ name, ...options });
2410
2696
  });
2411
- 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) => {
2412
2698
  await runL2ProfileValidate({ name, ...options });
2413
2699
  });
2414
2700
  const l2tx = l2.command("tx").description("Igra transaction management");
2415
- 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) => {
2416
2702
  try {
2417
2703
  await runL2TxBuild(options);
2418
2704
  } catch (e) {
@@ -2448,7 +2734,7 @@ function registerL2Commands(program) {
2448
2734
  }
2449
2735
  });
2450
2736
  const l2contract = l2.command("contract").description("Igra contract management");
2451
- 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) => {
2452
2738
  try {
2453
2739
  await runL2ContractDeployPlan(options);
2454
2740
  } catch (e) {
@@ -2456,7 +2742,7 @@ function registerL2Commands(program) {
2456
2742
  }
2457
2743
  });
2458
2744
  const l2bridge = l2.command("bridge").description("Igra bridge awareness");
2459
- 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) => {
2460
2746
  try {
2461
2747
  await runL2BridgeStatus(options);
2462
2748
  } catch (e) {
@@ -2471,21 +2757,21 @@ function registerL2Commands(program) {
2471
2757
  }
2472
2758
  });
2473
2759
  const l2rpc = l2.command("rpc").description("Igra RPC diagnostics");
2474
- 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) => {
2475
2761
  try {
2476
2762
  await runL2RpcHealth(options);
2477
2763
  } catch (e) {
2478
2764
  handleError(e);
2479
2765
  }
2480
2766
  });
2481
- 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) => {
2482
2768
  try {
2483
2769
  await runL2Balance(address, options);
2484
2770
  } catch (e) {
2485
2771
  handleError(e);
2486
2772
  }
2487
2773
  });
2488
- 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) => {
2489
2775
  try {
2490
2776
  await runL2Nonce(address, options);
2491
2777
  } catch (e) {
@@ -2500,20 +2786,19 @@ async function runNodeStart(input) {
2500
2786
  const runner = new DockerKaspadRunner(input);
2501
2787
  const status = await runner.start();
2502
2788
  const lines = [
2503
- "Kaspa node started",
2789
+ "Kaspa node started successfully",
2504
2790
  "",
2505
- "Backend: Docker",
2791
+ `Backend: Docker`,
2506
2792
  `Image: ${status.image}`,
2507
2793
  `Container: ${status.containerName}`,
2508
- `Network: ${status.network}`,
2509
- `Status: ${status.running ? "running" : "stopped"}`,
2794
+ `Status: \u2713 running`,
2510
2795
  "",
2511
- "RPC:",
2512
- ` gRPC: 127.0.0.1:${status.ports.rpc}`,
2513
- ` Borsh: 127.0.0.1:${status.ports.borshRpc}`,
2514
- ` 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})`,
2515
2800
  "",
2516
- "Data:",
2801
+ "Data Directory:",
2517
2802
  ` ${status.dataDir}`
2518
2803
  ];
2519
2804
  return {
@@ -2530,16 +2815,23 @@ async function runNodeStatus(input) {
2530
2815
  const lines = [
2531
2816
  "Kaspa node status",
2532
2817
  "",
2533
- "Backend: Docker",
2534
- `Container: ${status.containerName}`,
2535
- `Network: ${status.network}`,
2536
- `Status: ${status.running ? "running" : "stopped"}`,
2818
+ `Backend: Docker`,
2819
+ `Status: ${status.running ? "\u2713 running" : "\u2717 stopped"}`,
2537
2820
  "",
2538
- "RPC:",
2539
- ` gRPC: 127.0.0.1:${status.ports.rpc}`,
2540
- ` Borsh: 127.0.0.1:${status.ports.borshRpc}`,
2541
- ` JSON RPC: 127.0.0.1:${status.ports.jsonRpc}`
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})`,
2825
+ "",
2826
+ "Node Information:",
2827
+ ` Container: ${status.containerName}`,
2828
+ ` Image: ${status.image}`,
2829
+ ` Network: ${status.network}`,
2830
+ ` Data Dir: ${status.dataDir}`
2542
2831
  ];
2832
+ if (status.lastError && !status.rpcReady) {
2833
+ lines.push("", "Last Error:", ` ${status.lastError}`);
2834
+ }
2543
2835
  return {
2544
2836
  status,
2545
2837
  formatted: lines.join("\n")
@@ -2588,54 +2880,124 @@ async function runNodeLogs(input) {
2588
2880
  // src/commands/node.ts
2589
2881
  function registerNodeCommands(program) {
2590
2882
  const nodeCmd = program.command("node").description("Kaspa node management (Docker)");
2591
- nodeCmd.command("start").description(`Start local node ${UI.maturity("stable")}`).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");
2592
2886
  try {
2593
- const result = await runNodeStart(options);
2594
- 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
+ });
2595
2898
  } catch (e) {
2596
- handleError(e);
2899
+ handleLockError2(e);
2597
2900
  }
2598
2901
  });
2599
- nodeCmd.command("stop").description(`Stop local node ${UI.maturity("stable")}`).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");
2600
2905
  try {
2601
- const result = await runNodeStop({});
2602
- UI.success(`Node stopped (Container: ${result.containerName})`);
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
+ });
2603
2916
  } catch (e) {
2604
- handleError(e);
2917
+ handleLockError2(e);
2605
2918
  }
2606
2919
  });
2607
- nodeCmd.command("restart").description(`Restart local node ${UI.maturity("stable")}`).action(async () => {
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");
2608
2923
  try {
2609
- const result = await runNodeRestart({});
2610
- console.log(result.formatted);
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
+ });
2611
2934
  } catch (e) {
2612
- handleError(e);
2935
+ handleLockError2(e);
2613
2936
  }
2614
2937
  });
2615
- nodeCmd.command("reset").description(`Stop node and remove all local chain data ${UI.maturity("preview")}`).option("--start", "Restart the node after reset", false).option("--yes", "Skip confirmation prompt", false).action(async (options) => {
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");
2616
2941
  try {
2617
- if (!options.yes) {
2618
- const confirmed = await UI.confirm("This will delete all local chain data. Are you sure?");
2619
- if (!confirmed) {
2620
- console.log(" Aborted.");
2621
- return;
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
+ }
2622
2955
  }
2623
- }
2624
- const result = await runNodeReset({ removeData: true });
2625
- UI.success(result.formatted);
2626
- if (options.start) {
2627
- UI.info("Starting node...");
2628
- const startResult = await runNodeStart({});
2629
- console.log(startResult.formatted);
2630
- }
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
+ });
2631
2964
  } catch (e) {
2632
- handleError(e);
2965
+ handleLockError2(e);
2633
2966
  }
2634
2967
  });
2635
- nodeCmd.command("status").description("Check node status").action(async () => {
2968
+ nodeCmd.command("status").description("Check node status").option("--json", "Return status in JSON format", false).action(async (options) => {
2636
2969
  try {
2637
2970
  const result = await runNodeStatus({});
2638
- console.log(result.formatted);
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
+ }
2639
3001
  } catch (e) {
2640
3002
  handleError(e);
2641
3003
  }
@@ -2656,9 +3018,9 @@ function registerNodeCommands(program) {
2656
3018
  function registerConfigCommands(program) {
2657
3019
  const configCmd = program.command("config").description("Manage HardKAS configuration");
2658
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) => {
2659
- const { loadHardkasConfig: loadHardkasConfig5 } = await import("@hardkas/config");
3021
+ const { loadHardkasConfig: loadHardkasConfig15 } = await import("@hardkas/config");
2660
3022
  try {
2661
- const loaded = await loadHardkasConfig5(options.config ? { configPath: options.config } : {});
3023
+ const loaded = await loadHardkasConfig15(options.config ? { configPath: options.config } : {});
2662
3024
  if (options.json) {
2663
3025
  console.log(JSON.stringify(loaded, null, 2));
2664
3026
  return;
@@ -2692,19 +3054,19 @@ import { loadOrCreateLocalnetState as loadOrCreateLocalnetState3 } from "@hardka
2692
3054
 
2693
3055
  // src/runners/example-list-runner.ts
2694
3056
  import fs7 from "fs/promises";
2695
- import path8 from "path";
3057
+ import path9 from "path";
2696
3058
  async function runExampleList() {
2697
3059
  UI.box("HardKAS", "Example Registry");
2698
3060
  try {
2699
3061
  let currentDir = process.cwd();
2700
- let registryPath = path8.join(currentDir, "examples", "registry.json");
3062
+ let registryPath = path9.join(currentDir, "examples", "registry.json");
2701
3063
  for (let i = 0; i < 3; i++) {
2702
3064
  try {
2703
3065
  await fs7.access(registryPath);
2704
3066
  break;
2705
3067
  } catch {
2706
- currentDir = path8.dirname(currentDir);
2707
- registryPath = path8.join(currentDir, "examples", "registry.json");
3068
+ currentDir = path9.dirname(currentDir);
3069
+ registryPath = path9.join(currentDir, "examples", "registry.json");
2708
3070
  }
2709
3071
  }
2710
3072
  const data = await fs7.readFile(registryPath, "utf-8");
@@ -2724,19 +3086,19 @@ async function runExampleList() {
2724
3086
 
2725
3087
  // src/runners/example-run-runner.ts
2726
3088
  import fs8 from "fs/promises";
2727
- import path9 from "path";
3089
+ import path10 from "path";
2728
3090
  import { spawn } from "child_process";
2729
3091
  async function runExampleRun(id) {
2730
3092
  try {
2731
3093
  let currentDir = process.cwd();
2732
- let registryPath = path9.join(currentDir, "examples", "registry.json");
3094
+ let registryPath = path10.join(currentDir, "examples", "registry.json");
2733
3095
  for (let i = 0; i < 3; i++) {
2734
3096
  try {
2735
3097
  await fs8.access(registryPath);
2736
3098
  break;
2737
3099
  } catch {
2738
- currentDir = path9.dirname(currentDir);
2739
- registryPath = path9.join(currentDir, "examples", "registry.json");
3100
+ currentDir = path10.dirname(currentDir);
3101
+ registryPath = path10.join(currentDir, "examples", "registry.json");
2740
3102
  }
2741
3103
  }
2742
3104
  const data = await fs8.readFile(registryPath, "utf-8");
@@ -2757,10 +3119,10 @@ async function runExampleRun(id) {
2757
3119
  shell: true
2758
3120
  // Required for pnpm/npx on Windows
2759
3121
  });
2760
- return new Promise((resolve, reject) => {
3122
+ return new Promise((resolve3, reject) => {
2761
3123
  child.on("close", (code) => {
2762
3124
  if (code === 0) {
2763
- resolve();
3125
+ resolve3();
2764
3126
  } else {
2765
3127
  reject(new Error(`Example execution failed with exit code ${code}`));
2766
3128
  }
@@ -2806,70 +3168,244 @@ function registerMiscCommands(program) {
2806
3168
  }
2807
3169
 
2808
3170
  // src/commands/query.ts
3171
+ import pc from "picocolors";
2809
3172
  function registerQueryCommands(program) {
2810
3173
  const queryCmd = program.command("query").description("Query and introspect HardKAS artifacts, lineage, and workflows");
2811
3174
  const artifactsCmd = queryCmd.command("artifacts").description(`Query artifact store ${UI.maturity("stable")}`);
2812
- const storeCmd = queryCmd.command("store").description(`Manage query store index ${UI.maturity("alpha")}`);
2813
- storeCmd.command("doctor").description("Integrity and freshness check of the query store index").action(async () => {
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");
2814
3179
  try {
2815
- const engine = await getQueryEngine();
2816
- const report = await engine.backend.doctor();
2817
- console.log("\n \u2550\u2550\u2550 Query Store Doctor \u2550\u2550\u2550\n");
2818
- console.log(` Backend: ${engine.backend.kind()}`);
2819
- console.log(` Overall: ${report.ok ? "\u2713 HEALTHY" : "\u2717 STALE / ISSUES"}`);
2820
- console.log(` Last Indexed: ${report.lastIndexedAt || "never"}`);
2821
- console.log(` Stale Rows: ${report.staleArtifacts || 0}`);
2822
- console.log(` Zombie Rows: ${report.zombieArtifacts || 0}`);
2823
- console.log(` Orphan Edges: ${report.orphanEdges || 0}`);
2824
- if (report.corruptedFiles?.length > 0) {
2825
- console.log("\n Corrupted Files:");
2826
- for (const f of report.corruptedFiles) console.log(` \u2717 ${f}`);
2827
- }
2828
- if (!report.ok) {
2829
- console.log("\n Recommendation: Run 'hardkas query store rebuild' to fix issues.\n");
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);
2830
3221
  } else {
2831
- console.log("\n Everything looks good.\n");
3222
+ await action();
2832
3223
  }
2833
3224
  } catch (e) {
2834
- handleError(e);
3225
+ handleLockError2(e);
2835
3226
  process.exitCode = 1;
2836
3227
  }
2837
3228
  });
2838
- storeCmd.command("rebuild").description("Force a complete rebuild of the query store index").action(async () => {
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");
2839
3232
  try {
2840
- console.log("\n Rebuilding query store index...");
2841
- const engine = await getQueryEngine();
2842
- const start = Date.now();
2843
- await engine.backend.rebuild();
2844
- console.log(` \u2713 Index rebuilt successfully in ${Date.now() - start}ms.
2845
- `);
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
+ });
2846
3250
  } catch (e) {
2847
- handleError(e);
3251
+ handleLockError2(e);
2848
3252
  process.exitCode = 1;
2849
3253
  }
2850
3254
  });
2851
- artifactsCmd.command("list").description("List artifacts matching filters").option("--schema <schema>", "Filter by artifact schema (e.g. txPlan, signedTx)").option("--network <network>", "Filter by network ID").option("--mode <mode>", "Filter by mode (simulated/real)").option("--from <address>", "Filter by sender address").option("--to <address>", "Filter by recipient address").option("--sort <field:dir>", "Sort field and direction (e.g. createdAt:desc)").option("--limit <n>", "Max results", "100").option("--json", "Output as deterministic JSON", false).option("--explain [level]", "Attach explain chains (brief|full)").action(async (options) => {
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");
2852
3258
  try {
2853
- const { createQueryRequest } = await import("@hardkas/query");
2854
- const engine = await getQueryEngine();
2855
- const filters = [];
2856
- if (options.schema) filters.push({ field: "schema", op: "eq", value: `hardkas.${options.schema}` });
2857
- if (options.network) filters.push({ field: "networkId", op: "eq", value: options.network });
2858
- if (options.mode) filters.push({ field: "mode", op: "eq", value: options.mode });
2859
- if (options.from) filters.push({ field: "from.address", op: "eq", value: options.from });
2860
- if (options.to) filters.push({ field: "to.address", op: "eq", value: options.to });
2861
- let sort;
2862
- if (options.sort) {
2863
- const [field, dir] = options.sort.split(":");
2864
- sort = { field, direction: dir === "asc" ? "asc" : "desc" };
2865
- }
2866
- const request = createQueryRequest({
2867
- domain: "artifacts",
2868
- op: "list",
2869
- filters,
2870
- sort,
2871
- limit: parseInt(options.limit, 10),
2872
- explain: options.explain === true ? "brief" : options.explain || false
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
+ });
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) => {
3388
+ try {
3389
+ const { createQueryRequest } = await import("@hardkas/query");
3390
+ const engine = await getQueryEngine();
3391
+ const filters = [];
3392
+ if (options.schema) filters.push({ field: "schema", op: "eq", value: `hardkas.${options.schema}` });
3393
+ if (options.network) filters.push({ field: "networkId", op: "eq", value: options.network });
3394
+ if (options.mode) filters.push({ field: "mode", op: "eq", value: options.mode });
3395
+ if (options.from) filters.push({ field: "from.address", op: "eq", value: options.from });
3396
+ if (options.to) filters.push({ field: "to.address", op: "eq", value: options.to });
3397
+ let sort;
3398
+ if (options.sort) {
3399
+ const [field, dir] = options.sort.split(":");
3400
+ sort = { field, direction: dir === "asc" ? "asc" : "desc" };
3401
+ }
3402
+ const request = createQueryRequest({
3403
+ domain: "artifacts",
3404
+ op: "list",
3405
+ filters,
3406
+ sort,
3407
+ limit: parseInt(options.limit, 10),
3408
+ explain: options.explain === true ? "brief" : options.explain || false
2873
3409
  });
2874
3410
  const result = await engine.execute(request);
2875
3411
  if (options.json) {
@@ -2926,7 +3462,7 @@ function registerQueryCommands(program) {
2926
3462
  process.exitCode = 1;
2927
3463
  }
2928
3464
  });
2929
- const lineageCmd = queryCmd.command("lineage").description(`Traverse artifact lineage ${UI.maturity("preview")}`);
3465
+ const lineageCmd = queryCmd.command("lineage").description(`Traverse artifact lineage ${UI.maturity("stable")}`);
2930
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) => {
2931
3467
  try {
2932
3468
  const { createQueryRequest } = await import("@hardkas/query");
@@ -2994,7 +3530,7 @@ function registerQueryCommands(program) {
2994
3530
  process.exitCode = 1;
2995
3531
  }
2996
3532
  });
2997
- const replayCmd = queryCmd.command("replay").description(`Inspect replay history and divergence ${UI.maturity("preview")}`);
3533
+ const replayCmd = queryCmd.command("replay").description(`Inspect replay history and divergence ${UI.maturity("stable")}`);
2998
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) => {
2999
3535
  try {
3000
3536
  const { createQueryRequest } = await import("@hardkas/query");
@@ -3627,14 +4163,17 @@ async function runTest(options) {
3627
4163
 
3628
4164
  // src/commands/test.ts
3629
4165
  function registerTestCommands(program) {
3630
- program.command("test [files...]").description(`Run HardKAS tests against localnet ${UI.maturity("stable")}`).option("--network <network>", "Network to test against", "simnet").option("--watch", "Watch for changes", false).option("--json", "Output results as JSON", false).option("--reporter <reporter>", "Reporter to use", "default").action(async (files, options) => {
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) => {
3631
4167
  try {
3632
4168
  await runTest({
3633
4169
  files,
3634
4170
  network: options.network,
3635
4171
  watch: options.watch,
3636
4172
  json: options.json,
3637
- reporter: options.reporter
4173
+ reporter: options.reporter,
4174
+ massReport: options.massReport,
4175
+ ...options.massSnapshot ? { massSnapshot: options.massSnapshot } : {},
4176
+ ...options.massCompare ? { massCompare: options.massCompare } : {}
3638
4177
  });
3639
4178
  } catch (e) {
3640
4179
  handleError(e, "Test execution failed");
@@ -3645,93 +4184,292 @@ function registerTestCommands(program) {
3645
4184
 
3646
4185
  // src/commands/doctor.ts
3647
4186
  import os from "os";
3648
- import path10 from "path";
4187
+ import path11 from "path";
3649
4188
  import fs9 from "fs/promises";
3650
- import pc from "picocolors";
3651
- import { loadHardkasConfig as loadHardkasConfig4 } from "@hardkas/config";
3652
- import { JsonWrpcKaspaClient as JsonWrpcKaspaClient4 } from "@hardkas/kaspa-rpc";
3653
- 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";
3654
4192
  function registerDoctorCommand(program) {
3655
- 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) => {
3656
4194
  try {
3657
- await runDoctor();
4195
+ await runDoctor(opts);
3658
4196
  } catch (err) {
3659
4197
  handleError(err);
3660
4198
  }
3661
4199
  });
3662
4200
  }
3663
- async function runDoctor() {
3664
- UI.box("HardKAS System Doctor", "Operational Health Check");
3665
- UI.header("Environment Status");
3666
- UI.field("OS", `${os.type()} ${os.release()} (${os.arch()})`);
3667
- UI.field("Node", process.version);
3668
- UI.field("CWD", process.cwd());
3669
- UI.divider();
3670
- UI.header("Configuration Analysis");
3671
- try {
3672
- const loaded = await loadHardkasConfig4({ cwd: process.cwd() });
3673
- UI.success(`Config found: ${pc.cyan(path10.basename(loaded.path || "unknown"))}`);
3674
- UI.field("Default Network", loaded.config.defaultNetwork || "simnet");
3675
- } catch (e) {
3676
- 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
+ });
3677
4247
  }
3678
- UI.divider();
3679
- UI.header("RPC Connectivity & Health");
3680
4248
  try {
3681
- const loaded = await loadHardkasConfig4({ cwd: process.cwd() });
3682
- const networkId = loaded.config.defaultNetwork || "simnet";
3683
- const target = loaded.config.networks?.[networkId];
3684
- let rpcUrl = "ws://127.0.0.1:18210";
3685
- if (target?.rpcUrl) rpcUrl = target.rpcUrl;
3686
- UI.info(`Connecting to ${pc.cyan(rpcUrl)}...`);
3687
- const rpc = new JsonWrpcKaspaClient4({ rpcUrl });
3688
- const info = await rpc.getInfo();
3689
- UI.success(`RPC Alive: ${pc.bold(info.networkId || "active")}`);
3690
- UI.field("Synced", info.isSynced ? pc.green("YES") : pc.yellow("NO"));
3691
- if (info.serverVersion) UI.field("Version", info.serverVersion);
3692
- } catch (e) {
3693
- 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
+ });
3694
4264
  }
3695
- UI.divider();
3696
- UI.header("Artifact Store Integrity");
3697
- const hardkasDir = path10.join(process.cwd(), ".hardkas");
4265
+ const hardkasDir = path11.join(process.cwd(), ".hardkas");
4266
+ let dirExists = false;
3698
4267
  try {
3699
4268
  const stats = await fs9.stat(hardkasDir);
3700
- if (stats.isDirectory()) {
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 {
3701
4332
  const files = await fs9.readdir(hardkasDir);
3702
- const artifacts = files.filter((f) => f.endsWith(".json") && !f.endsWith(".enc.json"));
3703
- UI.success(`Artifact directory .hardkas/ is active`);
3704
- UI.field("Cached Artifacts", artifacts.length);
3705
- const hasEvents = files.includes("events.jsonl");
3706
- if (hasEvents) {
3707
- UI.success("Observability event log (events.jsonl) is present");
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
+ });
3708
4341
  } else {
3709
- UI.warning("Event log missing. Operational queries may be limited.");
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);
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
+ });
3710
4382
  }
3711
4383
  }
3712
4384
  } catch {
3713
- UI.error("Artifact Store not initialized", "Run 'hardkas init' to create a project.");
4385
+ addCheck({
4386
+ name: "Keystore permissions",
4387
+ category: "security",
4388
+ status: "pass",
4389
+ message: "No keystore found (nothing to secure)"
4390
+ });
3714
4391
  }
3715
- UI.divider();
3716
- UI.header("Query Store (SQLite) Status");
3717
- const dbPath = path10.join(hardkasDir, "store.db");
4392
+ if (dirExists) {
4393
+ try {
4394
+ const files = await fs9.readdir(hardkasDir);
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;
3718
4419
  try {
3719
- const store = new HardkasStore({ dbPath });
3720
- store.connect();
3721
- const db = store.getDatabase();
3722
- const artCount = db.prepare("SELECT COUNT(*) as count FROM artifacts").get().count;
3723
- const eventCount = db.prepare("SELECT COUNT(*) as count FROM events").get().count;
3724
- UI.success("Relational index (store.db) is healthy");
3725
- UI.field("Indexed Artifacts", artCount);
3726
- UI.field("Indexed Events", eventCount);
3727
- if (artCount === 0 && eventCount === 0) {
3728
- UI.warning("Database is empty. Run 'hardkas query store rebuild' to populate.");
3729
- }
3730
- store.disconnect();
3731
- } catch (e) {
3732
- UI.error("Query Store Issues", "The SQLite database might be corrupt or inaccessible. Run 'hardkas query store rebuild' to repair.");
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
+ });
4455
+ } else {
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.");
3733
4472
  }
3734
- UI.footer("Use 'hardkas query' for deep operational introspection.");
3735
4473
  }
3736
4474
 
3737
4475
  // src/commands/faucet.ts
@@ -3748,18 +4486,708 @@ function registerFaucetCommand(program) {
3748
4486
  });
3749
4487
  }
3750
4488
 
3751
- // src/index.ts
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"
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);
4847
+ }
4848
+ } catch {
4849
+ }
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;
4929
+ try {
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
+ });
5025
+ } catch (e) {
5026
+ handleError(e, "Forking failed");
5027
+ process.exit(1);
5028
+ } finally {
5029
+ await client.close();
5030
+ }
5031
+ }
5032
+
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
3752
5068
  import { readFileSync } from "fs";
3753
5069
  import { fileURLToPath } from "url";
3754
- import path11 from "path";
3755
- var packageJsonPath = path11.resolve(
3756
- path11.dirname(fileURLToPath(import.meta.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)),
3757
5185
  "../package.json"
3758
5186
  );
3759
- var { version: HARDKAS_VERSION4 } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
3760
- async function main() {
5187
+ var { version: HARDKAS_VERSION6 } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
5188
+ function buildHardkasProgram(options) {
3761
5189
  const program = new Command();
3762
- 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);
3763
5191
  registerInitCommands(program);
3764
5192
  registerTxCommands(program);
3765
5193
  registerArtifactCommands(program);
@@ -3776,20 +5204,36 @@ async function main() {
3776
5204
  registerTestCommands(program);
3777
5205
  registerDoctorCommand(program);
3778
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();
3779
5223
  try {
3780
5224
  await program.parseAsync(process.argv);
3781
5225
  } catch (err) {
3782
- const { maskSecrets } = await import("@hardkas/core");
5226
+ const { maskSecrets: maskSecrets2 } = await import("@hardkas/core");
3783
5227
  console.error(`
3784
- Error: ${maskSecrets(err.message || String(err))}`);
5228
+ Error: ${maskSecrets2(err.message || String(err))}`);
3785
5229
  process.exit(1);
3786
5230
  }
3787
5231
  }
3788
5232
  main().catch(async (err) => {
3789
- const { maskSecrets } = await import("@hardkas/core");
3790
- console.error("Fatal error:", maskSecrets(err.message || String(err)));
5233
+ const { maskSecrets: maskSecrets2 } = await import("@hardkas/core");
5234
+ console.error("Fatal error:", maskSecrets2(err.message || String(err)));
3791
5235
  if (err.stack) {
3792
- console.error(maskSecrets(err.stack));
5236
+ console.error(maskSecrets2(err.stack));
3793
5237
  }
3794
5238
  process.exit(1);
3795
5239
  });