@hardkas/sdk 0.7.6-alpha → 0.7.8-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.
Files changed (3) hide show
  1. package/dist/index.d.ts +92 -3
  2. package/dist/index.js +287 -35
  3. package/package.json +15 -14
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _hardkas_config from '@hardkas/config';
2
2
  import { LoadedHardkasConfig } from '@hardkas/config';
3
3
  export { defineHardkasConfig } from '@hardkas/config';
4
4
  import { KaspaRpcClient } from '@hardkas/kaspa-rpc';
5
- import { NetworkId } from '@hardkas/core';
5
+ import { EventEnvelope, NetworkId } from '@hardkas/core';
6
6
  export { ArtifactId, HardkasError, KaspaAddress, LineageId, NetworkId, SOMPI_PER_KAS, TxId, formatSompi, parseKasToSompi } from '@hardkas/core';
7
7
  import { TxPlanArtifact, SignedTxArtifact, TxReceiptArtifact, HardkasArtifactBase, WorkflowArtifact } from '@hardkas/artifacts';
8
8
  export { ARTIFACT_SCHEMAS, HARDKAS_VERSION, SignedTxArtifact, TxPlanArtifact, TxReceiptArtifact, TxTraceArtifact, createTxPlanArtifact, writeArtifact } from '@hardkas/artifacts';
@@ -30,6 +30,24 @@ declare class HardkasAccounts {
30
30
  sompi: bigint;
31
31
  formatted: string;
32
32
  }>;
33
+ /**
34
+ * Alias for getBalance.
35
+ */
36
+ balance(accountNameOrAddress: string): Promise<{
37
+ sompi: bigint;
38
+ formatted: string;
39
+ }>;
40
+ /**
41
+ * Lists all configured account names.
42
+ */
43
+ list(): Promise<string[]>;
44
+ /**
45
+ * Funds an account from another account (defaults to 'default' account).
46
+ */
47
+ fund(accountNameOrAddress: string, options?: {
48
+ from?: string;
49
+ amount?: string | bigint;
50
+ }): Promise<any>;
33
51
  }
34
52
 
35
53
  /**
@@ -73,6 +91,14 @@ declare class HardkasTx {
73
91
  receipt: TxReceiptArtifact;
74
92
  receiptPath: string;
75
93
  }>;
94
+ /**
95
+ * Explicitly appends a signature to a partially signed transaction.
96
+ */
97
+ appendSignature(plan: SignedTxArtifact, account?: HardkasAccount | string): Promise<SignedTxArtifact>;
98
+ /**
99
+ * Fetches the current status of a transaction by ID.
100
+ */
101
+ status(txId: string): Promise<any>;
76
102
  }
77
103
 
78
104
  /**
@@ -105,6 +131,21 @@ declare class HardkasQuery {
105
131
  * Internal lazy-loaded query engine.
106
132
  */
107
133
  private getEngine;
134
+ /**
135
+ * Synchronizes the query store with the filesystem artifacts.
136
+ */
137
+ sync(options?: {
138
+ force?: boolean;
139
+ }): Promise<any>;
140
+ /**
141
+ * Fetches events from the query store.
142
+ */
143
+ events(filter?: {
144
+ domain?: string;
145
+ kind?: string;
146
+ correlationId?: string;
147
+ artifactId?: string;
148
+ }): Promise<readonly EventEnvelope[]>;
108
149
  }
109
150
 
110
151
  /**
@@ -147,7 +188,28 @@ declare class HardkasReplay {
147
188
  * Verifies the deterministic artifact lineage of a transaction replay
148
189
  * against the mathematically reconstructed localnet state.
149
190
  */
150
- verify(options: ReplayVerifyOptions): Promise<ReplayVerifyResult>;
191
+ verify(targetOrOptions?: string | {
192
+ schema?: string;
193
+ artifactId?: string;
194
+ } | ReplayVerifyOptions, options?: ReplayVerifyOptions): Promise<ReplayVerifyResult>;
195
+ }
196
+
197
+ /**
198
+ * HardKAS Lineage Module
199
+ * @alpha
200
+ */
201
+ declare class HardkasLineage {
202
+ private sdk;
203
+ constructor(sdk: Hardkas);
204
+ /**
205
+ * Traces the lineage of an artifact, identifying ancestors and descendants.
206
+ */
207
+ trace(target: string | {
208
+ artifactId?: string;
209
+ contentHash?: string;
210
+ }, options?: {
211
+ direction?: "ancestors" | "descendants";
212
+ }): Promise<any>;
151
213
  }
152
214
 
153
215
  /**
@@ -217,6 +279,23 @@ declare class HardkasArtifactsManager {
217
279
  * Reads an artifact by path or ID/hash from the workspace.
218
280
  */
219
281
  read(id: string): Promise<any>;
282
+ /**
283
+ * Alias for read().
284
+ */
285
+ get(id: string): Promise<any>;
286
+ /**
287
+ * Lists all artifacts in the workspace.
288
+ */
289
+ list(): Promise<any[]>;
290
+ /**
291
+ * Cryptographically verifies the determinism and integrity of an artifact.
292
+ * Throws an error with details if corruption or mismatch is found.
293
+ */
294
+ verify(target: string | {
295
+ schema?: string;
296
+ artifactId?: string;
297
+ contentHash?: string;
298
+ }): Promise<any>;
220
299
  }
221
300
 
222
301
  interface WorkflowRunOptions {
@@ -256,6 +335,15 @@ interface HardkasOptions {
256
335
  cwd?: string;
257
336
  configPath?: string;
258
337
  mode?: "developer" | "agent";
338
+ network?: string;
339
+ autoBootstrap?: boolean;
340
+ logger?: {
341
+ info: (msg: string) => void;
342
+ warn: (msg: string) => void;
343
+ error: (msg: string) => void;
344
+ debug: (msg: string) => void;
345
+ [key: string]: any;
346
+ };
259
347
  policy?: {
260
348
  allowNetwork?: boolean;
261
349
  allowMainnet?: boolean;
@@ -279,6 +367,7 @@ declare class Hardkas {
279
367
  readonly query: HardkasQuery;
280
368
  readonly localnet: HardkasLocalnet;
281
369
  readonly replay: HardkasReplay;
370
+ readonly lineage: HardkasLineage;
282
371
  readonly workflow: HardkasWorkflow;
283
372
  readonly mode: "developer" | "agent";
284
373
  readonly policy: Required<NonNullable<HardkasOptions["policy"]>>;
@@ -306,4 +395,4 @@ declare class Hardkas {
306
395
  enforcePolicy(action: "network" | "mainnet" | "external-wallet" | "mutation", context?: string): void;
307
396
  }
308
397
 
309
- export { Hardkas, HardkasAccounts, HardkasArtifactsManager, HardkasL2, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasReplay, HardkasTx, HardkasWorkspace, type TaskArgs, type TaskContext, defineTask };
398
+ export { Hardkas, HardkasAccounts, HardkasArtifactsManager, HardkasL2, HardkasLineage, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasReplay, HardkasTx, HardkasWorkspace, type TaskArgs, type TaskContext, defineTask };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  loadHardkasConfig as loadConfig
8
8
  } from "@hardkas/config";
9
9
  import { JsonWrpcKaspaClient } from "@hardkas/kaspa-rpc";
10
- import { HardkasError as HardkasError2 } from "@hardkas/core";
10
+ import { HardkasError as HardkasError3 } from "@hardkas/core";
11
11
 
12
12
  // src/accounts.ts
13
13
  import { resolveHardkasAccount } from "@hardkas/accounts";
@@ -40,10 +40,55 @@ var HardkasAccounts = class {
40
40
  formatted: formatSompi(sompi)
41
41
  };
42
42
  }
43
+ /**
44
+ * Alias for getBalance.
45
+ */
46
+ async balance(accountNameOrAddress) {
47
+ return this.getBalance(accountNameOrAddress);
48
+ }
49
+ /**
50
+ * Lists all configured account names.
51
+ */
52
+ async list() {
53
+ return Object.keys(this.sdk.config.config.accounts || {});
54
+ }
55
+ /**
56
+ * Funds an account from another account (defaults to 'default' account).
57
+ */
58
+ async fund(accountNameOrAddress, options) {
59
+ let from = options?.from;
60
+ const amount = options?.amount || "1000000000";
61
+ if (!from) {
62
+ const accounts = await this.list();
63
+ if (accounts.includes("faucet")) {
64
+ from = "faucet";
65
+ } else if (accounts.includes("simulated_faucet")) {
66
+ from = "simulated_faucet";
67
+ } else if (this.sdk.network === "simulated" && accounts.includes("alice")) {
68
+ from = "alice";
69
+ } else {
70
+ throw new Error("No funding account available.\nFor simulated mode, run Hardkas.create({ network: 'simulated', autoBootstrap: true })\nor call accounts.fund(target, { from: 'alice' }).");
71
+ }
72
+ }
73
+ if (from === accountNameOrAddress) {
74
+ throw new Error(`Cannot fund account '${accountNameOrAddress}' from itself.`);
75
+ }
76
+ const plan = await this.sdk.tx.plan({
77
+ from,
78
+ to: accountNameOrAddress,
79
+ amount
80
+ });
81
+ const signed = await this.sdk.tx.sign(plan, from);
82
+ if (this.sdk.network === "simulated") {
83
+ return this.sdk.tx.simulate(signed);
84
+ } else {
85
+ return this.sdk.tx.send(signed);
86
+ }
87
+ }
43
88
  };
44
89
 
45
90
  // src/tx.ts
46
- import { systemRuntimeContext } from "@hardkas/core";
91
+ import { systemRuntimeContext, deterministicCompare } from "@hardkas/core";
47
92
  import {
48
93
  buildPaymentPlan,
49
94
  verifySignedTxSemantics
@@ -78,8 +123,12 @@ var HardkasTx = class {
78
123
  if (!toAccount.address)
79
124
  throw new Error(`To account ${toAccount.name} has no address.`);
80
125
  const amountSompi = typeof options.amount === "string" ? parseKasToSompi(options.amount) : typeof options.amount === "number" ? BigInt(options.amount) : options.amount;
126
+ if (amountSompi === 0n) {
127
+ throw new Error("Kaspa value-transfer outputs require amount > 0.\nFor metadata/notary/DID marker transactions use --amount 1.\nFuture: hardkas tx anchor.");
128
+ }
81
129
  let builderUtxos = [];
82
- if (this.sdk.network === "simulated") {
130
+ const activeNetwork = this.sdk.config.config.defaultNetwork || "simnet";
131
+ if (activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated") {
83
132
  const { loadOrCreateLocalnetState, getSpendableUtxos } = await import("@hardkas/localnet");
84
133
  const localState = await loadOrCreateLocalnetState({
85
134
  cwd: this.sdk.workspace.root
@@ -119,9 +168,10 @@ var HardkasTx = class {
119
168
  ],
120
169
  feeRateSompiPerMass: options.feeRate ?? 1n
121
170
  });
171
+ const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
122
172
  return createTxPlanArtifact({
123
- networkId: this.sdk.network,
124
- mode: "simulated",
173
+ networkId: activeNetwork,
174
+ mode: isSimulated ? "simulated" : "real",
125
175
  from: {
126
176
  input: fromAccount.name || fromAccount.address,
127
177
  address: fromAccount.address,
@@ -160,6 +210,9 @@ var HardkasTx = class {
160
210
  "Cannot append signature to an already completed signed transaction."
161
211
  );
162
212
  }
213
+ if (options?.append === void 0 && plan.status === "partially_signed") {
214
+ options = { ...options, append: true };
215
+ }
163
216
  if (!options?.append) {
164
217
  throw new Error(
165
218
  "Input file is a partially signed transaction. Use the --append flag to add your signature."
@@ -192,7 +245,7 @@ var HardkasTx = class {
192
245
  signature: `simulated-signature-of-${signerAddress}`
193
246
  };
194
247
  const newSignatures = [...sigs, signatureEntry].sort(
195
- (a, b) => a.signer.localeCompare(b.signer)
248
+ (a, b) => deterministicCompare(a.signer, b.signer)
196
249
  );
197
250
  const newMeta = [
198
251
  ...partialTx.signatureMetadata || [],
@@ -247,7 +300,12 @@ var HardkasTx = class {
247
300
  if (!signerAddress) {
248
301
  throw new Error(`Signer account '${resolvedAccount.name}' has no address.`);
249
302
  }
250
- const requiredSigners = options?.requiredSigners || [signerAddress];
303
+ const requiredSignersList = options?.requiredSigners || [signerAddress];
304
+ const requiredSigners = [];
305
+ for (const r of requiredSignersList) {
306
+ const acc = await this.sdk.accounts.resolve(r);
307
+ requiredSigners.push(acc.address || r);
308
+ }
251
309
  if (!requiredSigners.includes(signerAddress)) {
252
310
  throw new Error(
253
311
  `Signer '${signerAddress}' is not an authorized signer for this transaction.`
@@ -258,7 +316,7 @@ var HardkasTx = class {
258
316
  signature: `simulated-signature-of-${signerAddress}`
259
317
  };
260
318
  const signatures = [signatureEntry].sort(
261
- (a, b) => a.signer.localeCompare(b.signer)
319
+ (a, b) => deterministicCompare(a.signer, b.signer)
262
320
  );
263
321
  const signatureMetadata = [
264
322
  {
@@ -387,14 +445,16 @@ var HardkasTx = class {
387
445
  { cwd: this.sdk.workspace.root }
388
446
  );
389
447
  const tracePath = receiptPath.replace(".json", ".trace.json");
448
+ const activeNetwork = this.sdk.config.config.defaultNetwork || "simnet";
449
+ const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
390
450
  const receiptBase = {
391
451
  schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
392
452
  schemaVersion: "hardkas.receipt.v1",
393
453
  hardkasVersion: HARDKAS_VERSION,
394
454
  version: ARTIFACT_VERSION,
395
455
  hashVersion: CURRENT_HASH_VERSION,
396
- networkId: this.sdk.network,
397
- mode: "simulated",
456
+ networkId: activeNetwork,
457
+ mode: isSimulated ? "simulated" : "real",
398
458
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
399
459
  status: "confirmed",
400
460
  txId: simResult.receipt.txId,
@@ -429,8 +489,8 @@ var HardkasTx = class {
429
489
  hashVersion: CURRENT_HASH_VERSION,
430
490
  createdAt: receipt.createdAt,
431
491
  txId: receipt.txId,
432
- mode: "simulated",
433
- networkId: this.sdk.network,
492
+ mode: isSimulated ? "simulated" : "real",
493
+ networkId: activeNetwork,
434
494
  steps: traceSteps
435
495
  };
436
496
  traceBase.contentHash = calculateContentHash(traceBase, CURRENT_HASH_VERSION);
@@ -514,6 +574,23 @@ var HardkasTx = class {
514
574
  receiptPath
515
575
  };
516
576
  }
577
+ /**
578
+ * Explicitly appends a signature to a partially signed transaction.
579
+ */
580
+ async appendSignature(plan, account) {
581
+ return this.sign(plan, account, { append: true });
582
+ }
583
+ /**
584
+ * Fetches the current status of a transaction by ID.
585
+ */
586
+ async status(txId) {
587
+ const isLocal = txId.startsWith("simulated-");
588
+ if (isLocal) {
589
+ return { status: "simulated_confirmed" };
590
+ }
591
+ const result = await this.sdk.rpc.getTransaction(txId);
592
+ return result;
593
+ }
517
594
  };
518
595
 
519
596
  // src/l2.ts
@@ -546,11 +623,62 @@ var HardkasQuery = class {
546
623
  async getEngine() {
547
624
  if (this._engine) return this._engine;
548
625
  const { QueryEngine } = await import("@hardkas/query");
549
- this._engine = new QueryEngine({
550
- artifactDir: this.sdk.config.cwd
626
+ this._engine = await QueryEngine.create({
627
+ artifactDir: this.sdk.workspace.root,
628
+ autoSync: false
629
+ // We don't auto-sync on every getter
551
630
  });
552
631
  return this._engine;
553
632
  }
633
+ /**
634
+ * Synchronizes the query store with the filesystem artifacts.
635
+ */
636
+ async sync(options) {
637
+ const { HardkasStore, HardkasIndexer } = await import("@hardkas/query-store");
638
+ const { withLock } = await import("@hardkas/core");
639
+ const path4 = await import("path");
640
+ const dbPath = path4.join(this.sdk.workspace.root, ".hardkas", "store.db");
641
+ const store = new HardkasStore({ dbPath });
642
+ let stats;
643
+ await withLock(
644
+ {
645
+ rootDir: this.sdk.workspace.root,
646
+ name: "query-store",
647
+ command: "query-sync",
648
+ wait: true
649
+ },
650
+ async () => {
651
+ store.connect({ autoMigrate: true });
652
+ const indexer = new HardkasIndexer(store.getDatabase());
653
+ if (options?.force) {
654
+ stats = await indexer.rebuild();
655
+ } else {
656
+ stats = await indexer.sync();
657
+ }
658
+ }
659
+ );
660
+ return stats;
661
+ }
662
+ /**
663
+ * Fetches events from the query store.
664
+ */
665
+ async events(filter) {
666
+ const engine = await this.getEngine();
667
+ const { createQueryRequest } = await import("@hardkas/query");
668
+ const filters = [];
669
+ if (filter) {
670
+ for (const [k, v] of Object.entries(filter)) {
671
+ if (v) filters.push({ field: k, op: "eq", value: v });
672
+ }
673
+ }
674
+ const request = createQueryRequest({
675
+ domain: "events",
676
+ op: "list",
677
+ filters
678
+ });
679
+ const result = await engine.execute(request);
680
+ return result.items;
681
+ }
554
682
  };
555
683
 
556
684
  // src/localnet.ts
@@ -594,6 +722,7 @@ import {
594
722
  verifyArtifactIntegrity,
595
723
  writeArtifact as writeArtifact2
596
724
  } from "@hardkas/artifacts";
725
+ import { deterministicCompare as deterministicCompare2 } from "@hardkas/core";
597
726
  function resolveReplayTargets(cwd, options) {
598
727
  if (options.path) {
599
728
  const fullPath = path.resolve(cwd, options.path);
@@ -670,7 +799,7 @@ function resolveFromDirectory(dir, source) {
670
799
  Hint: Run a transaction first: hardkas tx send --from alice --to bob --amount 10 --network simulated --yes`
671
800
  );
672
801
  }
673
- plans.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
802
+ plans.sort((a, b) => deterministicCompare2(b.createdAt, a.createdAt));
674
803
  for (const plan of plans) {
675
804
  const matchingReceipt = receipts.find(
676
805
  (r) => (
@@ -721,16 +850,24 @@ var HardkasReplay = class {
721
850
  * Verifies the deterministic artifact lineage of a transaction replay
722
851
  * against the mathematically reconstructed localnet state.
723
852
  */
724
- async verify(options) {
725
- const targets = resolveReplayTargets(this.sdk.config.cwd, options);
853
+ async verify(targetOrOptions, options) {
854
+ let opts = options || {};
855
+ if (typeof targetOrOptions === "string") {
856
+ opts.path = targetOrOptions;
857
+ } else if (targetOrOptions && "artifactId" in targetOrOptions) {
858
+ opts.path = targetOrOptions.artifactId;
859
+ } else if (targetOrOptions) {
860
+ opts = { ...opts, ...targetOrOptions };
861
+ }
862
+ const targets = resolveReplayTargets(this.sdk.config.cwd, opts);
726
863
  let { planPath, receiptPath, artifactDir } = targets;
727
864
  if (!fs.existsSync(path.join(this.sdk.config.cwd, "hardkas.config.ts"))) {
728
865
  throw new Error(`Workspace not found at ${this.sdk.config.cwd}`);
729
866
  }
730
867
  const canonicalDirs = [
731
- path.join(artifactDir, ".hardkas", "receipts"),
732
- path.join(artifactDir, ".hardkas", "traces"),
733
- path.join(artifactDir, ".hardkas", "deployments")
868
+ path.join(this.sdk.workspace.hardkasDir, "receipts"),
869
+ path.join(this.sdk.workspace.hardkasDir, "traces"),
870
+ path.join(this.sdk.workspace.hardkasDir, "deployments")
734
871
  ];
735
872
  const files = [];
736
873
  for (const dir of canonicalDirs) {
@@ -793,9 +930,9 @@ var HardkasReplay = class {
793
930
  let receipt;
794
931
  let verifyErrorMsg;
795
932
  let report = null;
796
- if (options.workflowId) {
933
+ if (opts.workflowId) {
797
934
  try {
798
- const wfArtifactPath = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(options.workflowId) && f.endsWith(".json"));
935
+ const wfArtifactPath = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(opts.workflowId) && f.endsWith(".json"));
799
936
  if (!wfArtifactPath) throw new Error("Workflow artifact not found");
800
937
  const wfArtifactStr = fs.readFileSync(
801
938
  path.join(this.sdk.workspace.artifactsDir, wfArtifactPath),
@@ -803,7 +940,7 @@ var HardkasReplay = class {
803
940
  );
804
941
  const wfArtifact = JSON.parse(wfArtifactStr);
805
942
  if (wfArtifact.schema !== "hardkas.workflow.v1") {
806
- throw new Error(`Artifact ${options.workflowId} is not a workflow artifact`);
943
+ throw new Error(`Artifact ${opts.workflowId} is not a workflow artifact`);
807
944
  }
808
945
  const childArtifacts = wfArtifact.producedArtifacts || [];
809
946
  for (const childId of childArtifacts) {
@@ -882,6 +1019,33 @@ var HardkasReplay = class {
882
1019
  }
883
1020
  };
884
1021
 
1022
+ // src/lineage.ts
1023
+ var HardkasLineage = class {
1024
+ constructor(sdk) {
1025
+ this.sdk = sdk;
1026
+ }
1027
+ sdk;
1028
+ /**
1029
+ * Traces the lineage of an artifact, identifying ancestors and descendants.
1030
+ */
1031
+ async trace(target, options) {
1032
+ const anchor = typeof target === "string" ? target : target.artifactId || target.contentHash || "";
1033
+ if (!anchor) throw new Error("No anchor target provided for lineage trace.");
1034
+ const { createQueryRequest, QueryEngine } = await import("@hardkas/query");
1035
+ const engine = await QueryEngine.create({
1036
+ artifactDir: this.sdk.workspace.root,
1037
+ autoSync: false
1038
+ });
1039
+ const request = createQueryRequest({
1040
+ domain: "lineage",
1041
+ op: "chain",
1042
+ params: { anchor, direction: options?.direction || "ancestors" }
1043
+ });
1044
+ const result = await engine.execute(request);
1045
+ return result.items[0];
1046
+ }
1047
+ };
1048
+
885
1049
  // src/workspace.ts
886
1050
  import path2 from "path";
887
1051
  import fs2 from "fs";
@@ -897,7 +1061,7 @@ var HardkasWorkspace = class {
897
1061
  return path2.join(this.hardkasDir, "artifacts");
898
1062
  }
899
1063
  get localnetStatePath() {
900
- return path2.join(this.hardkasDir, "localnet-state.json");
1064
+ return path2.join(this.hardkasDir, "localnet.json");
901
1065
  }
902
1066
  get keystoreDir() {
903
1067
  return path2.join(this.hardkasDir, "keystore");
@@ -927,6 +1091,7 @@ var HardkasWorkspace = class {
927
1091
  // src/artifacts-manager.ts
928
1092
  import path3 from "path";
929
1093
  import fs3 from "fs";
1094
+ import { HardkasError } from "@hardkas/core";
930
1095
  var HardkasArtifactsManager = class {
931
1096
  constructor(workspace) {
932
1097
  this.workspace = workspace;
@@ -1001,6 +1166,15 @@ var HardkasArtifactsManager = class {
1001
1166
  }
1002
1167
  const { readArtifact } = await import("@hardkas/artifacts");
1003
1168
  let filePath = id;
1169
+ let resolvedPath = path3.resolve(this.workspace.root, filePath);
1170
+ if (fs3.existsSync(resolvedPath)) {
1171
+ resolvedPath = fs3.realpathSync(resolvedPath);
1172
+ }
1173
+ const rootRel = path3.relative(this.workspace.root, resolvedPath);
1174
+ const artifactsRel = path3.relative(this.workspace.artifactsDir, resolvedPath);
1175
+ if ((rootRel.startsWith("..") || path3.isAbsolute(rootRel)) && (artifactsRel.startsWith("..") || path3.isAbsolute(artifactsRel))) {
1176
+ throw new HardkasError("PATH_TRAVERSAL", "Artifact path escapes workspace boundary");
1177
+ }
1004
1178
  if (!fs3.existsSync(filePath)) {
1005
1179
  filePath = path3.join(this.workspace.artifactsDir, `${id}.json`);
1006
1180
  if (!fs3.existsSync(filePath)) {
@@ -1019,11 +1193,53 @@ var HardkasArtifactsManager = class {
1019
1193
  }
1020
1194
  return readArtifact(filePath);
1021
1195
  }
1196
+ /**
1197
+ * Alias for read().
1198
+ */
1199
+ async get(id) {
1200
+ return this.read(id);
1201
+ }
1202
+ /**
1203
+ * Lists all artifacts in the workspace.
1204
+ */
1205
+ async list() {
1206
+ if (!fs3.existsSync(this.workspace.artifactsDir)) {
1207
+ return [];
1208
+ }
1209
+ const { readArtifact } = await import("@hardkas/artifacts");
1210
+ const files = fs3.readdirSync(this.workspace.artifactsDir);
1211
+ const artifacts = [];
1212
+ for (const file of files) {
1213
+ if (file.endsWith(".json")) {
1214
+ try {
1215
+ const artifact = await readArtifact(path3.join(this.workspace.artifactsDir, file));
1216
+ artifacts.push(artifact);
1217
+ } catch (e) {
1218
+ }
1219
+ }
1220
+ }
1221
+ return artifacts;
1222
+ }
1223
+ /**
1224
+ * Cryptographically verifies the determinism and integrity of an artifact.
1225
+ * Throws an error with details if corruption or mismatch is found.
1226
+ */
1227
+ async verify(target) {
1228
+ const id = typeof target === "string" ? target : target.artifactId || target.contentHash || "";
1229
+ if (!id) throw new Error("No artifact target provided for verification.");
1230
+ const artifact = await this.read(id);
1231
+ const { verifyArtifactIntegrity: verifyArtifactIntegrity2 } = await import("@hardkas/artifacts");
1232
+ const result = await verifyArtifactIntegrity2(artifact);
1233
+ if (!result.ok) {
1234
+ throw new Error(`Artifact ${id} corrupted or invalid: ` + JSON.stringify(result.issues, null, 2));
1235
+ }
1236
+ return result;
1237
+ }
1022
1238
  };
1023
1239
 
1024
1240
  // src/workflow.ts
1025
1241
  import { HARDKAS_VERSION as HARDKAS_VERSION2 } from "@hardkas/artifacts";
1026
- import { HardkasError, deterministicCompare } from "@hardkas/core";
1242
+ import { HardkasError as HardkasError2, deterministicCompare as deterministicCompare3 } from "@hardkas/core";
1027
1243
  var HardkasWorkflow = class {
1028
1244
  constructor(sdk) {
1029
1245
  this.sdk = sdk;
@@ -1070,12 +1286,12 @@ var HardkasWorkflow = class {
1070
1286
  try {
1071
1287
  if (step.type === "simulate-failure") {
1072
1288
  if (this.sdk.mode === "agent") {
1073
- throw new HardkasError(
1289
+ throw new HardkasError2(
1074
1290
  "POLICY_DENIED",
1075
1291
  "simulate-failure is strictly prohibited in agent mode"
1076
1292
  );
1077
1293
  }
1078
- throw new HardkasError("MOCKED_FAIL", "Simulated failure for contract tests");
1294
+ throw new HardkasError2("MOCKED_FAIL", "Simulated failure for contract tests");
1079
1295
  }
1080
1296
  let producedArtifactId = void 0;
1081
1297
  let result = void 0;
@@ -1236,9 +1452,9 @@ var HardkasWorkflow = class {
1236
1452
  artifactId: workflowId,
1237
1453
  status,
1238
1454
  steps: artifactSteps,
1239
- parentArtifacts: parentArtifacts.sort(deterministicCompare),
1455
+ parentArtifacts: parentArtifacts.sort(deterministicCompare3),
1240
1456
  producedArtifacts: Array.from(new Set(producedArtifacts)).sort(
1241
- deterministicCompare
1457
+ deterministicCompare3
1242
1458
  ),
1243
1459
  generationRange: {
1244
1460
  start: generationStart,
@@ -1308,7 +1524,7 @@ import {
1308
1524
  } from "@hardkas/artifacts";
1309
1525
  import {
1310
1526
  SOMPI_PER_KAS,
1311
- HardkasError as HardkasError3,
1527
+ HardkasError as HardkasError4,
1312
1528
  parseKasToSompi as parseKasToSompi2,
1313
1529
  formatSompi as formatSompi2
1314
1530
  } from "@hardkas/core";
@@ -1333,6 +1549,7 @@ var Hardkas = class _Hardkas {
1333
1549
  this.query = new HardkasQuery(this);
1334
1550
  this.localnet = new HardkasLocalnet(this);
1335
1551
  this.replay = new HardkasReplay(this);
1552
+ this.lineage = new HardkasLineage(this);
1336
1553
  this.workflow = new HardkasWorkflow(this);
1337
1554
  }
1338
1555
  config;
@@ -1344,6 +1561,7 @@ var Hardkas = class _Hardkas {
1344
1561
  query;
1345
1562
  localnet;
1346
1563
  replay;
1564
+ lineage;
1347
1565
  workflow;
1348
1566
  mode;
1349
1567
  policy;
@@ -1362,6 +1580,39 @@ var Hardkas = class _Hardkas {
1362
1580
  static async open(dirOrOptions = ".") {
1363
1581
  const options = typeof dirOrOptions === "string" ? { cwd: dirOrOptions } : dirOrOptions;
1364
1582
  const loaded = await loadConfig(options);
1583
+ const activeNetwork = options.network || loaded.config.defaultNetwork || "simnet";
1584
+ const isSimulated = activeNetwork === "simulated" || loaded.config.networks?.[activeNetwork]?.kind === "simulated";
1585
+ const autoBootstrap = options.autoBootstrap ?? (isSimulated ? true : false);
1586
+ const fs4 = await import("fs");
1587
+ const path4 = await import("path");
1588
+ const cwd = options.cwd || process.cwd();
1589
+ const hardkasDir = path4.join(cwd, ".hardkas");
1590
+ if (autoBootstrap) {
1591
+ if (!isSimulated) {
1592
+ if (options.logger) {
1593
+ options.logger.warn("[HardKAS] autoBootstrap ignored for non-simulated network");
1594
+ }
1595
+ } else {
1596
+ if (!fs4.existsSync(hardkasDir)) {
1597
+ if (options.logger) {
1598
+ options.logger.info("[HardKAS] Auto-bootstrapping simulated workspace");
1599
+ }
1600
+ fs4.mkdirSync(hardkasDir, { recursive: true });
1601
+ }
1602
+ try {
1603
+ const { loadOrCreateLocalnetState } = await import("@hardkas/localnet");
1604
+ await loadOrCreateLocalnetState({ cwd });
1605
+ } catch {
1606
+ }
1607
+ }
1608
+ } else {
1609
+ if (!fs4.existsSync(hardkasDir)) {
1610
+ throw new HardkasError3("NOT_INITIALIZED", "Workspace not initialized. Run npx hardkas init . or pass autoBootstrap: true.");
1611
+ }
1612
+ }
1613
+ if (options.network) {
1614
+ loaded.config.defaultNetwork = options.network;
1615
+ }
1365
1616
  return new _Hardkas(loaded, options);
1366
1617
  }
1367
1618
  /**
@@ -1392,19 +1643,19 @@ var Hardkas = class _Hardkas {
1392
1643
  switch (action) {
1393
1644
  case "network":
1394
1645
  if (!this.policy.allowNetwork)
1395
- throw new HardkasError2("POLICY_VIOLATION", msg("allowNetwork"));
1646
+ throw new HardkasError3("POLICY_VIOLATION", msg("allowNetwork"));
1396
1647
  break;
1397
1648
  case "mainnet":
1398
1649
  if (!this.policy.allowMainnet)
1399
- throw new HardkasError2("POLICY_VIOLATION", msg("allowMainnet"));
1650
+ throw new HardkasError3("POLICY_VIOLATION", msg("allowMainnet"));
1400
1651
  break;
1401
1652
  case "external-wallet":
1402
1653
  if (!this.policy.allowExternalWallet)
1403
- throw new HardkasError2("POLICY_VIOLATION", msg("allowExternalWallet"));
1654
+ throw new HardkasError3("POLICY_VIOLATION", msg("allowExternalWallet"));
1404
1655
  break;
1405
1656
  case "mutation":
1406
1657
  if (this.policy.requireDryRun)
1407
- throw new HardkasError2("POLICY_VIOLATION", msg("requireDryRun"));
1658
+ throw new HardkasError3("POLICY_VIOLATION", msg("requireDryRun"));
1408
1659
  break;
1409
1660
  }
1410
1661
  }
@@ -1415,8 +1666,9 @@ export {
1415
1666
  Hardkas,
1416
1667
  HardkasAccounts,
1417
1668
  HardkasArtifactsManager,
1418
- HardkasError3 as HardkasError,
1669
+ HardkasError4 as HardkasError,
1419
1670
  HardkasL2,
1671
+ HardkasLineage,
1420
1672
  HardkasLocalnet,
1421
1673
  HardkasQuery,
1422
1674
  HardkasReplay,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/sdk",
3
- "version": "0.7.6-alpha",
3
+ "version": "0.7.8-alpha",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,23 +23,24 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@hardkas/config": "0.7.6-alpha",
27
- "@hardkas/artifacts": "0.7.6-alpha",
28
- "@hardkas/accounts": "0.7.6-alpha",
29
- "@hardkas/core": "0.7.6-alpha",
30
- "@hardkas/l2": "0.7.6-alpha",
31
- "@hardkas/simulator": "0.7.6-alpha",
32
- "@hardkas/localnet": "0.7.6-alpha",
33
- "@hardkas/query": "0.7.6-alpha",
34
- "@hardkas/tx-builder": "0.7.6-alpha",
35
- "@hardkas/wallet-adapter": "0.7.6-alpha",
36
- "@hardkas/kaspa-rpc": "0.7.6-alpha"
26
+ "@hardkas/artifacts": "0.7.8-alpha",
27
+ "@hardkas/core": "0.7.8-alpha",
28
+ "@hardkas/config": "0.7.8-alpha",
29
+ "@hardkas/kaspa-rpc": "0.7.8-alpha",
30
+ "@hardkas/l2": "0.7.8-alpha",
31
+ "@hardkas/localnet": "0.7.8-alpha",
32
+ "@hardkas/accounts": "0.7.8-alpha",
33
+ "@hardkas/query": "0.7.8-alpha",
34
+ "@hardkas/tx-builder": "0.7.8-alpha",
35
+ "@hardkas/simulator": "0.7.8-alpha",
36
+ "@hardkas/query-store": "0.7.8-alpha",
37
+ "@hardkas/wallet-adapter": "0.7.8-alpha"
37
38
  },
38
39
  "devDependencies": {
39
40
  "tsup": "^8.3.5",
41
+ "@types/node": "^20.12.7",
40
42
  "typescript": "^5.7.2",
41
- "vitest": "^2.1.8",
42
- "@hardkas/query-store": "0.7.6-alpha"
43
+ "vitest": "^2.1.8"
43
44
  },
44
45
  "license": "MIT",
45
46
  "author": "Javier Rodriguez",