@hardkas/sdk 0.5.5-alpha → 0.7.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.
@@ -0,0 +1,102 @@
1
+ // src/client.ts
2
+ function createHardkasClient(options = {}) {
3
+ const baseUrl = options.baseUrl || "http://127.0.0.1:7420";
4
+ const defaultHeaders = {
5
+ "Content-Type": "application/json",
6
+ "X-Hardkas-Request": "true"
7
+ };
8
+ async function fetchApi(path, init) {
9
+ try {
10
+ const response = await fetch(`${baseUrl}${path}`, {
11
+ ...init,
12
+ headers: { ...defaultHeaders, ...init?.headers }
13
+ });
14
+ const data = await response.json();
15
+ return data;
16
+ } catch (e) {
17
+ return {
18
+ ok: false,
19
+ error: { code: "FETCH_FAILED", message: e.message },
20
+ warnings: [],
21
+ meta: { workspace: "unknown", network: options.network || "simulated", mode: "unknown" }
22
+ };
23
+ }
24
+ }
25
+ return {
26
+ accounts: {
27
+ list: () => fetchApi("/api/accounts")
28
+ },
29
+ tx: {
30
+ plan: (params) => fetchApi("/api/tx/plan", { method: "POST", body: JSON.stringify(params) }),
31
+ sign: (params) => fetchApi("/api/tx/sign", { method: "POST", body: JSON.stringify(params) }),
32
+ send: (params) => fetchApi("/api/tx/send", { method: "POST", body: JSON.stringify(params) }),
33
+ receipt: (id) => fetchApi(`/api/tx/receipt/${id}`)
34
+ },
35
+ artifacts: {
36
+ explain: (id) => fetchApi(`/api/artifacts/${id}/explain`),
37
+ replay: (id) => fetchApi(`/api/artifacts/${id}/replay`, { method: "POST" }),
38
+ watch: (callback, options2) => {
39
+ const transport = options2?.transport || "sse";
40
+ const intervalMs = options2?.intervalMs || 2e3;
41
+ let active = true;
42
+ let cleanup = () => {
43
+ active = false;
44
+ };
45
+ if (transport === "sse" && typeof EventSource !== "undefined") {
46
+ try {
47
+ let queryParams = "";
48
+ const queryParts = [];
49
+ if (options2?.type) queryParts.push(`type=${options2.type}`);
50
+ if (options2?.lineage) queryParts.push(`lineage=true`);
51
+ if (options2?.replay) queryParts.push(`replay=true`);
52
+ if (queryParts.length > 0) queryParams = `?${queryParts.join("&")}`;
53
+ const es = new EventSource(`${baseUrl}/api/artifacts/stream${queryParams}`);
54
+ es.onmessage = (e) => {
55
+ try {
56
+ callback(JSON.parse(e.data));
57
+ } catch (err) {
58
+ }
59
+ };
60
+ cleanup = () => {
61
+ active = false;
62
+ es.close();
63
+ };
64
+ return cleanup;
65
+ } catch (e) {
66
+ }
67
+ }
68
+ const poll = async () => {
69
+ while (active) {
70
+ await new Promise((r) => setTimeout(r, intervalMs));
71
+ }
72
+ };
73
+ poll();
74
+ return cleanup;
75
+ }
76
+ },
77
+ workflow: {
78
+ transfer: (params) => {
79
+ return fetchApi("/api/tx/send", { method: "POST", body: JSON.stringify(params) });
80
+ }
81
+ },
82
+ localnet: {
83
+ status: () => fetchApi("/api/localnet/status")
84
+ },
85
+ dev: {
86
+ status: () => fetchApi("/api/dev/status")
87
+ },
88
+ session: {
89
+ start: () => fetchApi("/api/session/start", { method: "POST" }),
90
+ snapshot: () => fetchApi("/api/session/snapshot", { method: "POST" }),
91
+ replay: (options2) => fetchApi("/api/session/replay", { method: "POST", body: JSON.stringify(options2 || {}) }),
92
+ diffReplay: (artifactId) => fetchApi(`/api/session/diff-replay/${artifactId}`, { method: "POST" }),
93
+ timeTravel: (artifactId) => fetchApi("/api/session/time-travel", { method: "POST", body: JSON.stringify({ artifactId }) }),
94
+ export: () => fetchApi("/api/session/export"),
95
+ import: (data, force) => fetchApi("/api/session/import", { method: "POST", body: JSON.stringify({ data, force }) })
96
+ }
97
+ };
98
+ }
99
+
100
+ export {
101
+ createHardkasClient
102
+ };
@@ -0,0 +1,84 @@
1
+ interface HardkasClientOptions {
2
+ baseUrl?: string;
3
+ network?: string;
4
+ }
5
+ interface ClientEnvelope<T> {
6
+ ok: boolean;
7
+ data?: T;
8
+ error?: {
9
+ code: string;
10
+ message: string;
11
+ };
12
+ warnings: string[];
13
+ meta: {
14
+ workspace: string;
15
+ network: string;
16
+ mode?: string;
17
+ };
18
+ }
19
+ declare function createHardkasClient(options?: HardkasClientOptions): {
20
+ accounts: {
21
+ list: () => Promise<ClientEnvelope<any[]>>;
22
+ };
23
+ tx: {
24
+ plan: (params: {
25
+ from: string;
26
+ to: string;
27
+ amountSompi: string;
28
+ feeRate?: string;
29
+ }) => Promise<ClientEnvelope<any>>;
30
+ sign: (params: {
31
+ planId: string;
32
+ account: string;
33
+ }) => Promise<ClientEnvelope<any>>;
34
+ send: (params: {
35
+ signedTxId?: string;
36
+ from?: string;
37
+ to?: string;
38
+ amountSompi?: string;
39
+ feeRate?: string;
40
+ allowDevAutoSign?: boolean;
41
+ }) => Promise<ClientEnvelope<any>>;
42
+ receipt: (id: string) => Promise<ClientEnvelope<any>>;
43
+ };
44
+ artifacts: {
45
+ explain: (id: string) => Promise<ClientEnvelope<any>>;
46
+ replay: (id: string) => Promise<ClientEnvelope<any>>;
47
+ watch: (callback: (artifact: any) => void, options?: {
48
+ type?: string;
49
+ lineage?: boolean;
50
+ replay?: boolean;
51
+ transport?: "sse" | "poll";
52
+ intervalMs?: number;
53
+ }) => () => void;
54
+ };
55
+ workflow: {
56
+ transfer: (params: {
57
+ from: string;
58
+ to: string;
59
+ amountSompi: string;
60
+ feeRate?: string;
61
+ allowDevAutoSign?: boolean;
62
+ }) => Promise<ClientEnvelope<any>>;
63
+ };
64
+ localnet: {
65
+ status: () => Promise<ClientEnvelope<any>>;
66
+ };
67
+ dev: {
68
+ status: () => Promise<ClientEnvelope<any>>;
69
+ };
70
+ session: {
71
+ start: () => Promise<ClientEnvelope<any>>;
72
+ snapshot: () => Promise<ClientEnvelope<any>>;
73
+ replay: (options?: {
74
+ untilArtifact?: string;
75
+ strict?: boolean;
76
+ }) => Promise<ClientEnvelope<any>>;
77
+ diffReplay: (artifactId: string) => Promise<ClientEnvelope<any>>;
78
+ timeTravel: (artifactId: string) => Promise<ClientEnvelope<any>>;
79
+ export: () => Promise<ClientEnvelope<any>>;
80
+ import: (data: any, force?: boolean) => Promise<ClientEnvelope<any>>;
81
+ };
82
+ };
83
+
84
+ export { type ClientEnvelope, type HardkasClientOptions, createHardkasClient };
package/dist/client.js ADDED
@@ -0,0 +1,6 @@
1
+ import {
2
+ createHardkasClient
3
+ } from "./chunk-RCCELGL3.js";
4
+ export {
5
+ createHardkasClient
6
+ };
package/dist/index.d.ts CHANGED
@@ -4,12 +4,13 @@ export { defineHardkasConfig } from '@hardkas/config';
4
4
  import { KaspaRpcClient } from '@hardkas/kaspa-rpc';
5
5
  import { NetworkId } from '@hardkas/core';
6
6
  export { ArtifactId, HardkasError, KaspaAddress, LineageId, NetworkId, SOMPI_PER_KAS, TxId, formatSompi, parseKasToSompi } from '@hardkas/core';
7
- import { TxPlanArtifact, SignedTxArtifact, TxReceiptArtifact } from '@hardkas/artifacts';
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';
9
9
  import { HardkasAccount } from '@hardkas/accounts';
10
10
  export { signTxPlanArtifact } from '@hardkas/accounts';
11
11
  import { L2NetworkProfile } from '@hardkas/l2';
12
12
  export { buildPaymentPlan } from '@hardkas/tx-builder';
13
+ export { ClientEnvelope, HardkasClientOptions, createHardkasClient } from './client.js';
13
14
 
14
15
  /**
15
16
  * HardKAS Accounts Module
@@ -52,9 +53,21 @@ declare class HardkasTx {
52
53
  */
53
54
  sign(plan: TxPlanArtifact, account?: HardkasAccount | string): Promise<SignedTxArtifact>;
54
55
  /**
55
- * Sends a signed transaction.
56
+ * Simulates a transaction on the local state without broadcasting to a real Kaspa node.
57
+ * Modifies the local deterministic state and outputs receipt/trace artifacts.
56
58
  */
57
- send(signed: SignedTxArtifact): Promise<TxReceiptArtifact>;
59
+ simulate(signedArtifact: SignedTxArtifact): Promise<{
60
+ receipt: TxReceiptArtifact;
61
+ receiptPath: string;
62
+ tracePath: string;
63
+ }>;
64
+ /**
65
+ * Sends a signed transaction to the real RPC network.
66
+ */
67
+ send(signedArtifact: SignedTxArtifact, url?: string): Promise<{
68
+ receipt: TxReceiptArtifact;
69
+ receiptPath: string;
70
+ }>;
58
71
  }
59
72
 
60
73
  /**
@@ -109,6 +122,113 @@ declare class HardkasLocalnet {
109
122
  reset(): Promise<void>;
110
123
  }
111
124
 
125
+ interface ReplayVerifyOptions {
126
+ path?: string;
127
+ workflowId?: string;
128
+ }
129
+ interface ReplayVerifyResult {
130
+ passed: boolean;
131
+ artifactsScanned: number;
132
+ lineage: "valid" | "invalid";
133
+ determinism: "verified" | "failed";
134
+ contamination: "clean" | "contaminated";
135
+ report: any;
136
+ error?: string;
137
+ }
138
+ declare class HardkasReplay {
139
+ private sdk;
140
+ constructor(sdk: Hardkas);
141
+ /**
142
+ * Verifies the deterministic artifact lineage of a transaction replay
143
+ * against the mathematically reconstructed localnet state.
144
+ */
145
+ verify(options: ReplayVerifyOptions): Promise<ReplayVerifyResult>;
146
+ }
147
+
148
+ /**
149
+ * Deterministic Workspace Abstraction.
150
+ * Encapsulates all filesystem boundary interactions and isolates paths
151
+ * from the global process.cwd(), ensuring agent/script replayability.
152
+ */
153
+ declare class HardkasWorkspace {
154
+ readonly root: string;
155
+ constructor(cwd: string);
156
+ get hardkasDir(): string;
157
+ get artifactsDir(): string;
158
+ get localnetStatePath(): string;
159
+ get keystoreDir(): string;
160
+ /**
161
+ * Safely resolves a path relative to the workspace root.
162
+ */
163
+ resolvePath(...segments: string[]): string;
164
+ /**
165
+ * Safely builds a relative path from the workspace root to the target.
166
+ */
167
+ relativeFromRoot(absolutePath: string): string;
168
+ /**
169
+ * Ensures the core .hardkas directory exists.
170
+ */
171
+ ensureHardkasDir(): void;
172
+ }
173
+
174
+ interface WriteArtifactOptions {
175
+ /**
176
+ * Explicitly override the canonical artifacts directory.
177
+ * By default, it writes to sdk.workspace.artifactsDir.
178
+ */
179
+ outputDir?: string;
180
+ /**
181
+ * Explicitly override the default filename.
182
+ * By default, it generates `${schema}-${contentHash}.json`
183
+ */
184
+ fileName?: string;
185
+ /**
186
+ * If true, verifies integrity and schema but does not touch the filesystem.
187
+ * Useful for Agent planning or previews.
188
+ */
189
+ dryRun?: boolean;
190
+ /** Telemetry for Event Sourcing */
191
+ workflowId?: string;
192
+ correlationId?: string;
193
+ networkId?: string;
194
+ }
195
+ interface WriteArtifactResult {
196
+ absolutePath?: string;
197
+ dryRun: boolean;
198
+ contentHash: string;
199
+ }
200
+ /**
201
+ * Deterministic Artifact I/O boundary.
202
+ */
203
+ declare class HardkasArtifactsManager {
204
+ private workspace;
205
+ constructor(workspace: HardkasWorkspace);
206
+ /**
207
+ * Writes a valid artifact to disk (canonical or custom path).
208
+ */
209
+ write(artifact: HardkasArtifactBase, options?: WriteArtifactOptions): Promise<WriteArtifactResult>;
210
+ /**
211
+ * Reads an artifact by path or ID/hash from the workspace.
212
+ */
213
+ read(id: string): Promise<any>;
214
+ }
215
+
216
+ interface WorkflowRunOptions {
217
+ steps: Array<{
218
+ type: string;
219
+ [key: string]: any;
220
+ }>;
221
+ dryRun?: boolean;
222
+ }
223
+ declare class HardkasWorkflow {
224
+ private readonly sdk;
225
+ constructor(sdk: Hardkas);
226
+ /**
227
+ * Executes a sequence of declarative steps and returns a definitive WorkflowArtifact.
228
+ */
229
+ run(options: WorkflowRunOptions): Promise<WorkflowArtifact>;
230
+ }
231
+
112
232
  interface TaskArgs {
113
233
  [key: string]: string | boolean | undefined;
114
234
  }
@@ -129,6 +249,13 @@ declare const defineTask: {
129
249
  interface HardkasOptions {
130
250
  cwd?: string;
131
251
  configPath?: string;
252
+ mode?: "developer" | "agent";
253
+ policy?: {
254
+ allowNetwork?: boolean;
255
+ allowMainnet?: boolean;
256
+ allowExternalWallet?: boolean;
257
+ requireDryRun?: boolean;
258
+ };
132
259
  }
133
260
  /**
134
261
  * HardKAS SDK - Main Entry Point
@@ -138,11 +265,17 @@ interface HardkasOptions {
138
265
  */
139
266
  declare class Hardkas {
140
267
  readonly config: LoadedHardkasConfig;
268
+ readonly workspace: HardkasWorkspace;
269
+ readonly artifacts: HardkasArtifactsManager;
141
270
  readonly accounts: HardkasAccounts;
142
271
  readonly tx: HardkasTx;
143
272
  readonly l2: HardkasL2;
144
273
  readonly query: HardkasQuery;
145
274
  readonly localnet: HardkasLocalnet;
275
+ readonly replay: HardkasReplay;
276
+ readonly workflow: HardkasWorkflow;
277
+ readonly mode: "developer" | "agent";
278
+ readonly policy: Required<NonNullable<HardkasOptions["policy"]>>;
146
279
  readonly rpc: KaspaRpcClient;
147
280
  private constructor();
148
281
  private resolveRpcUrl;
@@ -160,6 +293,11 @@ declare class Hardkas {
160
293
  get network(): NetworkId;
161
294
  get sdkConfig(): _hardkas_config.HardkasConfig;
162
295
  get cwd(): string;
296
+ /**
297
+ * Validates an action against the active security policy.
298
+ * Throws HardkasError if the policy is violated.
299
+ */
300
+ enforcePolicy(action: "network" | "mainnet" | "external-wallet" | "mutation", context?: string): void;
163
301
  }
164
302
 
165
- export { Hardkas, HardkasAccounts, HardkasL2, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasTx, type TaskArgs, type TaskContext, defineTask };
303
+ export { Hardkas, HardkasAccounts, HardkasArtifactsManager, HardkasL2, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasReplay, HardkasTx, HardkasWorkspace, type TaskArgs, type TaskContext, defineTask };
package/dist/index.js CHANGED
@@ -1,6 +1,11 @@
1
+ import {
2
+ createHardkasClient
3
+ } from "./chunk-RCCELGL3.js";
4
+
1
5
  // src/index.ts
2
6
  import { loadHardkasConfig as loadConfig } from "@hardkas/config";
3
7
  import { JsonWrpcKaspaClient } from "@hardkas/kaspa-rpc";
8
+ import { HardkasError as HardkasError2 } from "@hardkas/core";
4
9
 
5
10
  // src/accounts.ts
6
11
  import {
@@ -37,17 +42,23 @@ var HardkasAccounts = class {
37
42
  };
38
43
 
39
44
  // src/tx.ts
45
+ import { systemRuntimeContext } from "@hardkas/core";
40
46
  import {
41
- buildPaymentPlan
47
+ buildPaymentPlan,
48
+ verifySignedTxSemantics
42
49
  } from "@hardkas/tx-builder";
43
50
  import {
44
51
  HARDKAS_VERSION,
52
+ ARTIFACT_SCHEMAS,
53
+ ARTIFACT_VERSION,
54
+ CURRENT_HASH_VERSION,
45
55
  getBroadcastableSignedTransaction,
46
56
  writeArtifact,
47
57
  getDefaultReceiptPath,
48
58
  createTxPlanArtifact,
49
59
  calculateContentHash
50
60
  } from "@hardkas/artifacts";
61
+ import { coreEvents } from "@hardkas/core";
51
62
  import { signTxPlanArtifact } from "@hardkas/accounts";
52
63
  import { parseKasToSompi } from "@hardkas/core";
53
64
  var HardkasTx = class {
@@ -63,7 +74,7 @@ var HardkasTx = class {
63
74
  const toAccount = typeof options.to === "string" ? await this.sdk.accounts.resolve(options.to) : options.to;
64
75
  if (!fromAccount.address) throw new Error(`From account ${fromAccount.name} has no address.`);
65
76
  if (!toAccount.address) throw new Error(`To account ${toAccount.name} has no address.`);
66
- const amountSompi = typeof options.amount === "string" ? parseKasToSompi(options.amount) : options.amount;
77
+ const amountSompi = typeof options.amount === "string" ? parseKasToSompi(options.amount) : typeof options.amount === "number" ? BigInt(options.amount) : options.amount;
67
78
  const rpcUtxos = await this.sdk.rpc.getUtxosByAddress(fromAccount.address);
68
79
  const builderUtxos = rpcUtxos.map((u) => ({
69
80
  outpoint: {
@@ -96,7 +107,8 @@ var HardkasTx = class {
96
107
  address: toAccount.address
97
108
  },
98
109
  amountSompi,
99
- plan: builderPlan
110
+ plan: builderPlan,
111
+ ctx: systemRuntimeContext
100
112
  });
101
113
  }
102
114
  /**
@@ -119,36 +131,135 @@ var HardkasTx = class {
119
131
  });
120
132
  }
121
133
  /**
122
- * Sends a signed transaction.
134
+ * Simulates a transaction on the local state without broadcasting to a real Kaspa node.
135
+ * Modifies the local deterministic state and outputs receipt/trace artifacts.
123
136
  */
124
- async send(signed) {
125
- const broadcastable = getBroadcastableSignedTransaction(signed);
126
- const result = await this.sdk.rpc.submitTransaction(broadcastable.rawTransaction);
127
- const txId = result.transactionId;
128
- if (!txId) throw new Error("Broadcast failed: RPC returned no transaction ID.");
129
- const receipt = {
130
- schema: "hardkas.txReceipt",
137
+ async simulate(signedArtifact) {
138
+ const {
139
+ loadOrCreateLocalnetState,
140
+ saveLocalnetState,
141
+ applySimulatedPayment,
142
+ saveSimulatedReceipt,
143
+ saveSimulatedTrace
144
+ } = await import("@hardkas/localnet");
145
+ const path4 = await import("path");
146
+ const state = await loadOrCreateLocalnetState();
147
+ const startTime = Date.now();
148
+ const events = [
149
+ { type: "phase.started", phase: "send", timestamp: startTime }
150
+ ];
151
+ const simResult = applySimulatedPayment(state, {
152
+ from: signedArtifact.from.input || signedArtifact.from.address,
153
+ to: signedArtifact.to.input || signedArtifact.to.address,
154
+ amountSompi: BigInt(signedArtifact.amountSompi)
155
+ }, systemRuntimeContext);
156
+ coreEvents.normalizeAndEmit({
157
+ kind: "workflow.submitted",
158
+ txId: simResult.receipt.txId,
159
+ endpoint: "simulated://local"
160
+ });
161
+ events.push({ type: "phase.completed", phase: "send", timestamp: Date.now() });
162
+ await saveLocalnetState(simResult.state);
163
+ const receiptPath = await saveSimulatedReceipt(simResult.receipt);
164
+ const tracePath = receiptPath.replace(".json", ".trace.json");
165
+ const receiptBase = {
166
+ schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
131
167
  hardkasVersion: HARDKAS_VERSION,
132
- version: "1.0.0-alpha",
133
- networkId: signed.networkId,
134
- mode: signed.mode,
135
- status: "accepted",
168
+ version: ARTIFACT_VERSION,
169
+ hashVersion: CURRENT_HASH_VERSION,
170
+ networkId: this.sdk.network,
171
+ mode: "simulated",
136
172
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
173
+ status: "confirmed",
174
+ txId: simResult.receipt.txId,
175
+ sourceSignedId: signedArtifact.signedId,
176
+ from: { address: signedArtifact.from.address },
177
+ to: { address: signedArtifact.to.address },
178
+ amountSompi: signedArtifact.amountSompi,
179
+ feeSompi: simResult.receipt.feeSompi?.toString() || "0",
180
+ changeSompi: simResult.receipt.changeSompi?.toString() || "0",
181
+ spentUtxoIds: simResult.receipt.spentUtxoIds,
182
+ createdUtxoIds: simResult.receipt.createdUtxoIds,
183
+ daaScore: simResult.receipt.daaScore?.toString() || "0",
184
+ preStateHash: simResult.receipt.preStateHash,
185
+ postStateHash: simResult.receipt.postStateHash,
186
+ submittedAt: simResult.receipt.createdAt,
187
+ confirmedAt: simResult.receipt.createdAt,
188
+ rpcUrl: "simulated://local",
189
+ tracePath
190
+ };
191
+ receiptBase.contentHash = calculateContentHash(receiptBase, CURRENT_HASH_VERSION);
192
+ const receipt = Object.freeze(receiptBase);
193
+ const traceSteps = events.map((ev) => ({
194
+ phase: ev.phase || ev.message || "unknown",
195
+ status: ev.type.includes("completed") ? "completed" : ev.type.includes("failed") ? "failed" : "started",
196
+ timestamp: new Date(ev.timestamp).toISOString(),
197
+ details: ev.type === "note" ? { message: ev.message } : void 0
198
+ }));
199
+ const traceBase = {
200
+ schema: ARTIFACT_SCHEMAS.TX_TRACE,
201
+ hardkasVersion: HARDKAS_VERSION,
202
+ version: ARTIFACT_VERSION,
203
+ hashVersion: CURRENT_HASH_VERSION,
204
+ createdAt: receipt.createdAt,
205
+ txId: receipt.txId,
206
+ mode: "simulated",
207
+ networkId: this.sdk.network,
208
+ steps: traceSteps
209
+ };
210
+ traceBase.contentHash = calculateContentHash(traceBase, CURRENT_HASH_VERSION);
211
+ await saveSimulatedTrace({
212
+ ...traceBase,
213
+ events,
214
+ receiptPath
215
+ });
216
+ return {
217
+ receipt,
218
+ receiptPath,
219
+ tracePath
220
+ };
221
+ }
222
+ /**
223
+ * Sends a signed transaction to the real RPC network.
224
+ */
225
+ async send(signedArtifact, url) {
226
+ const verification = verifySignedTxSemantics(signedArtifact);
227
+ if (!verification.ok) {
228
+ throw new Error(`Pre-broadcast semantic verification failed: ${verification.issues.map((i) => i.message).join(", ")}`);
229
+ }
230
+ const broadcastable = getBroadcastableSignedTransaction(signedArtifact);
231
+ const broadcastRecord = broadcastable.rawTransaction;
232
+ const txId = broadcastRecord.id || "unknown";
233
+ coreEvents.normalizeAndEmit({
234
+ kind: "workflow.submitted",
137
235
  txId,
138
- from: {
139
- address: signed.from.address
140
- },
141
- to: {
142
- address: signed.to.address
143
- },
144
- amountSompi: String(signed.amountSompi),
145
- feeSompi: String(signed.estimatedFeeSompi || "0")
236
+ endpoint: url || "real"
237
+ });
238
+ const result = await this.sdk.rpc.submitTransaction(broadcastable.rawTransaction);
239
+ const realReceiptBase = {
240
+ schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
241
+ hardkasVersion: HARDKAS_VERSION,
242
+ version: ARTIFACT_VERSION,
243
+ hashVersion: CURRENT_HASH_VERSION,
244
+ networkId: this.sdk.network,
245
+ mode: "real",
246
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
247
+ status: result.accepted ? "submitted" : "failed",
248
+ txId: result.transactionId || "failed",
249
+ sourceSignedId: signedArtifact.signedId,
250
+ from: { address: signedArtifact.from.address },
251
+ to: { address: signedArtifact.to.address },
252
+ amountSompi: signedArtifact.amountSompi,
253
+ feeSompi: signedArtifact.metadata?.estimatedFeeSompi || "0",
254
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
255
+ ...url ? { rpcUrl: url } : {}
146
256
  };
147
- receipt.contentHash = calculateContentHash(receipt);
148
- const receiptPath = getDefaultReceiptPath(txId, this.sdk.config.cwd);
257
+ realReceiptBase.contentHash = calculateContentHash(realReceiptBase, CURRENT_HASH_VERSION);
258
+ const receipt = realReceiptBase;
259
+ const receiptPath = getDefaultReceiptPath(receipt.txId, this.sdk.config.cwd);
149
260
  await writeArtifact(receiptPath, receipt);
150
261
  return {
151
- ...receipt,
262
+ receipt,
152
263
  receiptPath
153
264
  };
154
265
  }
@@ -226,6 +337,463 @@ var HardkasLocalnet = class {
226
337
  }
227
338
  };
228
339
 
340
+ // src/replay.ts
341
+ import fs from "fs";
342
+ import path from "path";
343
+ import {
344
+ readTxPlanArtifact,
345
+ readTxReceiptArtifact as readTxReceiptArtifact2,
346
+ verifyArtifactIntegrity,
347
+ writeArtifact as writeArtifact2
348
+ } from "@hardkas/artifacts";
349
+ var HardkasReplay = class {
350
+ constructor(sdk) {
351
+ this.sdk = sdk;
352
+ }
353
+ sdk;
354
+ /**
355
+ * Verifies the deterministic artifact lineage of a transaction replay
356
+ * against the mathematically reconstructed localnet state.
357
+ */
358
+ async verify(options) {
359
+ const artifactDir = options.path ? path.resolve(this.sdk.config.cwd, options.path) : this.sdk.config.cwd;
360
+ if (options.path && !fs.existsSync(path.join(artifactDir, "hardkas.config.ts"))) {
361
+ throw new Error(`Workspace not found at ${options.path}`);
362
+ }
363
+ const planPath = path.join(artifactDir, "tx-plan.json");
364
+ const receiptPath = path.join(artifactDir, "tx-receipt.json");
365
+ const canonicalDirs = [
366
+ path.join(artifactDir, ".hardkas", "receipts"),
367
+ path.join(artifactDir, ".hardkas", "traces"),
368
+ path.join(artifactDir, ".hardkas", "deployments")
369
+ ];
370
+ const files = [];
371
+ for (const dir of canonicalDirs) {
372
+ if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
373
+ const list = fs.readdirSync(dir);
374
+ for (const f of list) {
375
+ if (f.endsWith(".json")) {
376
+ files.push(path.join(dir, f));
377
+ }
378
+ }
379
+ }
380
+ }
381
+ if (fs.existsSync(artifactDir) && fs.statSync(artifactDir).isDirectory()) {
382
+ const rootFiles = fs.readdirSync(artifactDir);
383
+ for (const f of rootFiles) {
384
+ if (f.startsWith("tx-") && f.endsWith(".json")) {
385
+ files.push(path.join(artifactDir, f));
386
+ }
387
+ }
388
+ }
389
+ let artifactCount = 0;
390
+ let lineageOk = true;
391
+ let determinismOk = true;
392
+ let contaminationOk = true;
393
+ const isContaminated = (artifact) => {
394
+ if (artifact.networkId && artifact.networkId !== "simnet" && artifact.networkId !== "simulated") {
395
+ const str = JSON.stringify(artifact);
396
+ if (str.includes("kaspa:sim_")) {
397
+ return true;
398
+ }
399
+ }
400
+ return false;
401
+ };
402
+ for (const file of files) {
403
+ try {
404
+ const content = fs.readFileSync(file, "utf-8");
405
+ const json = JSON.parse(content);
406
+ if (json && json.schema && typeof json.schema === "string" && json.schema.startsWith("hardkas.")) {
407
+ artifactCount++;
408
+ if (isContaminated(json)) contaminationOk = false;
409
+ const isCoreArtifact = ["hardkas.txPlan", "hardkas.signedTx", "hardkas.txReceipt", "hardkas.snapshot"].includes(json.schema);
410
+ if (isCoreArtifact) {
411
+ const integrity = await verifyArtifactIntegrity(json);
412
+ if (!integrity.ok) determinismOk = false;
413
+ }
414
+ } else {
415
+ lineageOk = false;
416
+ }
417
+ } catch (e) {
418
+ lineageOk = false;
419
+ determinismOk = false;
420
+ }
421
+ }
422
+ let plan;
423
+ let receipt;
424
+ let verifyErrorMsg;
425
+ let report = null;
426
+ if (options.workflowId) {
427
+ try {
428
+ const wfArtifactPath = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(options.workflowId) && f.endsWith(".json"));
429
+ if (!wfArtifactPath) throw new Error("Workflow artifact not found");
430
+ const wfArtifactStr = fs.readFileSync(path.join(this.sdk.workspace.artifactsDir, wfArtifactPath), "utf-8");
431
+ const wfArtifact = JSON.parse(wfArtifactStr);
432
+ if (wfArtifact.schema !== "hardkas.workflow.v1") {
433
+ throw new Error(`Artifact ${options.workflowId} is not a workflow artifact`);
434
+ }
435
+ const childArtifacts = wfArtifact.producedArtifacts || [];
436
+ for (const childId of childArtifacts) {
437
+ const childFile = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(childId) && f.endsWith(".json"));
438
+ if (!childFile) throw new Error(`Child artifact ${childId} not found`);
439
+ const childStr = fs.readFileSync(path.join(this.sdk.workspace.artifactsDir, childFile), "utf-8");
440
+ const child = JSON.parse(childStr);
441
+ const integrity = await verifyArtifactIntegrity(child);
442
+ if (!integrity.ok) {
443
+ determinismOk = false;
444
+ verifyErrorMsg = `Child artifact ${childId} failed cryptographic determinism check: ${JSON.stringify(integrity.issues)}`;
445
+ break;
446
+ }
447
+ if (isContaminated(child)) {
448
+ contaminationOk = false;
449
+ verifyErrorMsg = `Child artifact ${childId} is contaminated with simulated signatures in a real run`;
450
+ break;
451
+ }
452
+ artifactCount++;
453
+ }
454
+ report = { invariantsOk: determinismOk && contaminationOk };
455
+ } catch (e) {
456
+ verifyErrorMsg = `Workflow Replay failed: ${e.message}`;
457
+ lineageOk = false;
458
+ determinismOk = false;
459
+ }
460
+ } else if (options.path) {
461
+ try {
462
+ if (!fs.existsSync(planPath)) throw new Error(`Transaction plan artifact is missing at: ${planPath}`);
463
+ if (!fs.existsSync(receiptPath)) throw new Error(`Transaction receipt artifact is missing at: ${receiptPath}`);
464
+ plan = await readTxPlanArtifact(planPath);
465
+ receipt = await readTxReceiptArtifact2(receiptPath);
466
+ } catch (err) {
467
+ verifyErrorMsg = err.message;
468
+ }
469
+ if (!verifyErrorMsg && plan && receipt) {
470
+ try {
471
+ const { loadOrCreateLocalnetState, reconstructStateAtDaa, verifyReplay } = await import("@hardkas/localnet");
472
+ const { systemRuntimeContext: systemRuntimeContext2 } = await import("@hardkas/core");
473
+ let state = await loadOrCreateLocalnetState();
474
+ if (receipt.mode === "simulated" && receipt.daaScore) {
475
+ const receiptDaa = BigInt(receipt.daaScore);
476
+ const targetDaa = receiptDaa - 1n;
477
+ state = reconstructStateAtDaa(state, targetDaa);
478
+ }
479
+ report = verifyReplay(state, plan, receipt, systemRuntimeContext2);
480
+ const reportFilename = `${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}-${receipt.txId}.replay.json`;
481
+ const reportPath = path.join(this.sdk.workspace.artifactsDir, reportFilename);
482
+ await writeArtifact2(reportPath, report);
483
+ } catch (err) {
484
+ verifyErrorMsg = `Replay execution failed: ${err.message}`;
485
+ }
486
+ }
487
+ } else {
488
+ verifyErrorMsg = "No path or workflowId provided for replay verification";
489
+ }
490
+ const invariantsOk = report ? report.invariantsOk : false;
491
+ const passed = lineageOk && determinismOk && contaminationOk && invariantsOk && !verifyErrorMsg;
492
+ return {
493
+ passed,
494
+ artifactsScanned: artifactCount,
495
+ lineage: lineageOk ? "valid" : "invalid",
496
+ determinism: determinismOk ? "verified" : "failed",
497
+ contamination: contaminationOk ? "clean" : "contaminated",
498
+ report,
499
+ ...verifyErrorMsg ? { error: verifyErrorMsg } : {}
500
+ };
501
+ }
502
+ };
503
+
504
+ // src/workspace.ts
505
+ import path2 from "path";
506
+ import fs2 from "fs";
507
+ var HardkasWorkspace = class {
508
+ root;
509
+ constructor(cwd) {
510
+ this.root = path2.resolve(cwd);
511
+ }
512
+ get hardkasDir() {
513
+ return path2.join(this.root, ".hardkas");
514
+ }
515
+ get artifactsDir() {
516
+ return path2.join(this.hardkasDir, "artifacts");
517
+ }
518
+ get localnetStatePath() {
519
+ return path2.join(this.hardkasDir, "localnet-state.json");
520
+ }
521
+ get keystoreDir() {
522
+ return path2.join(this.hardkasDir, "keystore");
523
+ }
524
+ /**
525
+ * Safely resolves a path relative to the workspace root.
526
+ */
527
+ resolvePath(...segments) {
528
+ return path2.resolve(this.root, ...segments);
529
+ }
530
+ /**
531
+ * Safely builds a relative path from the workspace root to the target.
532
+ */
533
+ relativeFromRoot(absolutePath) {
534
+ return path2.relative(this.root, absolutePath);
535
+ }
536
+ /**
537
+ * Ensures the core .hardkas directory exists.
538
+ */
539
+ ensureHardkasDir() {
540
+ if (!fs2.existsSync(this.hardkasDir)) {
541
+ fs2.mkdirSync(this.hardkasDir, { recursive: true });
542
+ }
543
+ }
544
+ };
545
+
546
+ // src/artifacts-manager.ts
547
+ import path3 from "path";
548
+ import fs3 from "fs";
549
+ var HardkasArtifactsManager = class {
550
+ constructor(workspace) {
551
+ this.workspace = workspace;
552
+ }
553
+ workspace;
554
+ /**
555
+ * Writes a valid artifact to disk (canonical or custom path).
556
+ */
557
+ async write(artifact, options = {}) {
558
+ const record = artifact;
559
+ const hash = record.contentHash || "unknown";
560
+ if (options.dryRun) {
561
+ return {
562
+ dryRun: true,
563
+ contentHash: hash
564
+ };
565
+ }
566
+ const outputDir = options.outputDir || this.workspace.artifactsDir;
567
+ if (!fs3.existsSync(outputDir)) {
568
+ fs3.mkdirSync(outputDir, { recursive: true });
569
+ }
570
+ const schema = record.schema || "artifact";
571
+ const shortSchema = schema.replace("hardkas.", "");
572
+ const fileName = options.fileName || `${shortSchema}-${hash}.json`;
573
+ const absolutePath = path3.join(outputDir, fileName);
574
+ const { writeArtifact: writeArtifact4 } = await import("@hardkas/artifacts");
575
+ await writeArtifact4(absolutePath, artifact);
576
+ const {
577
+ coreEvents: coreEvents2,
578
+ createEventEnvelope,
579
+ asWorkflowId,
580
+ asCorrelationId,
581
+ asNetworkId,
582
+ asArtifactId,
583
+ asEventSequence
584
+ } = await import("@hardkas/core");
585
+ const wId = options.workflowId || "wf_unknown_standalone";
586
+ const cId = options.correlationId || wId;
587
+ const netId = options.networkId || record.networkId || "unknown";
588
+ const artifactId = record.artifactId || hash;
589
+ coreEvents2.emit(createEventEnvelope({
590
+ kind: "artifact.written",
591
+ domain: "integrity",
592
+ workflowId: asWorkflowId(wId),
593
+ correlationId: asCorrelationId(cId),
594
+ networkId: asNetworkId(netId),
595
+ payload: { artifactId: asArtifactId(artifactId), path: absolutePath },
596
+ sequenceNumber: asEventSequence(1),
597
+ globalOffset: 0,
598
+ sourceSubsystem: "sdk:artifacts-manager",
599
+ artifactId: asArtifactId(artifactId)
600
+ }));
601
+ return {
602
+ absolutePath,
603
+ dryRun: false,
604
+ contentHash: hash
605
+ };
606
+ }
607
+ /**
608
+ * Reads an artifact by path or ID/hash from the workspace.
609
+ */
610
+ async read(id) {
611
+ const { readArtifact } = await import("@hardkas/artifacts");
612
+ let filePath = id;
613
+ if (!fs3.existsSync(filePath)) {
614
+ filePath = path3.join(this.workspace.artifactsDir, `${id}.json`);
615
+ if (!fs3.existsSync(filePath)) {
616
+ if (fs3.existsSync(this.workspace.artifactsDir)) {
617
+ const files = fs3.readdirSync(this.workspace.artifactsDir);
618
+ const found = files.find((f) => f.includes(id) || f.endsWith(`${id}.json`));
619
+ if (found) {
620
+ filePath = path3.join(this.workspace.artifactsDir, found);
621
+ } else {
622
+ throw new Error(`Artifact ${id} not found in workspace.`);
623
+ }
624
+ } else {
625
+ throw new Error(`Artifact ${id} not found in workspace.`);
626
+ }
627
+ }
628
+ }
629
+ return readArtifact(filePath);
630
+ }
631
+ };
632
+
633
+ // src/workflow.ts
634
+ import { HARDKAS_VERSION as HARDKAS_VERSION2 } from "@hardkas/artifacts";
635
+ import { HardkasError } from "@hardkas/core";
636
+ var HardkasWorkflow = class {
637
+ constructor(sdk) {
638
+ this.sdk = sdk;
639
+ }
640
+ sdk;
641
+ /**
642
+ * Executes a sequence of declarative steps and returns a definitive WorkflowArtifact.
643
+ */
644
+ async run(options) {
645
+ const { calculateContentHash: calculateContentHash2 } = await import("@hardkas/artifacts");
646
+ const intentPayload = {
647
+ type: "hardkas.workflow.intent",
648
+ schemaVersion: "v1",
649
+ workflowSpec: options.steps,
650
+ normalizedInputs: {},
651
+ parentArtifacts: [],
652
+ // In v1, workflows do not accept explicit parent inputs yet
653
+ policySnapshot: {
654
+ allowNetwork: this.sdk.policy.allowNetwork,
655
+ allowMainnet: this.sdk.policy.allowMainnet,
656
+ allowExternalWallet: this.sdk.policy.allowExternalWallet,
657
+ requireDryRun: this.sdk.policy.requireDryRun
658
+ },
659
+ capabilitySnapshot: {
660
+ mode: this.sdk.mode,
661
+ network: this.sdk.network
662
+ },
663
+ runtimeVersion: HARDKAS_VERSION2,
664
+ workspaceSchemaVersion: "hardkas.workflow.v1"
665
+ };
666
+ const intentHash = calculateContentHash2(intentPayload);
667
+ const workflowId = `wf_${intentHash.slice(0, 16)}`;
668
+ const artifactSteps = [];
669
+ const producedArtifacts = [];
670
+ const parentArtifacts = [];
671
+ const generationStart = Date.now().toString();
672
+ let status = "completed";
673
+ let errorEnvelope = void 0;
674
+ let lastPlan = null;
675
+ let lastSigned = null;
676
+ for (const step of options.steps) {
677
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
678
+ try {
679
+ if (step.type === "simulate-failure") {
680
+ if (this.sdk.mode === "agent") {
681
+ throw new HardkasError("POLICY_DENIED", "simulate-failure is strictly prohibited in agent mode");
682
+ }
683
+ throw new HardkasError("MOCKED_FAIL", "Simulated failure for contract tests");
684
+ }
685
+ let producedArtifactId = void 0;
686
+ if (step.type === "network.switch") {
687
+ const targetNetwork = step.args?.network || step.network;
688
+ if (targetNetwork === "mainnet") {
689
+ this.sdk.enforcePolicy("mainnet", "Workflow requested network switch to mainnet");
690
+ }
691
+ } else if (step.type === "tx.plan") {
692
+ this.sdk.enforcePolicy("network", "Workflow requested transaction planning");
693
+ lastPlan = await this.sdk.tx.plan({
694
+ from: step.args?.from || step.from,
695
+ to: step.args?.to || step.to,
696
+ amount: step.args?.amount || step.amount
697
+ });
698
+ if (!options.dryRun) {
699
+ await this.sdk.artifacts.write(lastPlan);
700
+ }
701
+ const planRecord = lastPlan;
702
+ producedArtifactId = lastPlan.artifactId || planRecord.contentHash;
703
+ if (producedArtifactId) producedArtifacts.push(producedArtifactId);
704
+ } else if (step.type === "tx.simulate" || step.type === "tx.send") {
705
+ if (!lastPlan) throw new Error("Cannot sign or send without a prior tx.plan step");
706
+ if (step.type === "tx.send") {
707
+ this.sdk.enforcePolicy("mutation", "Workflow requested real broadcast via tx.send");
708
+ }
709
+ lastSigned = await this.sdk.tx.sign(lastPlan);
710
+ if (!options.dryRun) {
711
+ await this.sdk.artifacts.write(lastSigned);
712
+ }
713
+ const signedRecord = lastSigned;
714
+ const signedId = lastSigned.artifactId || signedRecord.contentHash;
715
+ if (signedId) producedArtifacts.push(signedId);
716
+ if (step.type === "tx.simulate") {
717
+ const { receipt } = await this.sdk.tx.simulate(lastSigned);
718
+ if (!options.dryRun) await this.sdk.artifacts.write(receipt);
719
+ const receiptRecord = receipt;
720
+ producedArtifactId = receiptRecord.artifactId || receiptRecord.contentHash;
721
+ if (producedArtifactId) producedArtifacts.push(producedArtifactId);
722
+ } else {
723
+ const { receipt } = await this.sdk.tx.send(lastSigned);
724
+ if (!options.dryRun) await this.sdk.artifacts.write(receipt);
725
+ const receiptRecord = receipt;
726
+ producedArtifactId = receiptRecord.artifactId || receiptRecord.contentHash;
727
+ if (producedArtifactId) producedArtifacts.push(producedArtifactId);
728
+ }
729
+ }
730
+ const stepRecord = {
731
+ type: step.type,
732
+ status: "success",
733
+ startedAt,
734
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
735
+ // hardkas-determinism-allow: step completion timestamp
736
+ };
737
+ if (producedArtifactId) stepRecord.producedArtifactId = producedArtifactId;
738
+ artifactSteps.push(stepRecord);
739
+ } catch (e) {
740
+ console.error("DEBUG WORKFLOW ERROR:", e.stack);
741
+ status = "failed";
742
+ errorEnvelope = {
743
+ code: e.code || "WORKFLOW_STEP_FAILED",
744
+ message: e.message,
745
+ redacted: false
746
+ };
747
+ artifactSteps.push({
748
+ type: step.type,
749
+ status: "failed",
750
+ startedAt,
751
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
752
+ // hardkas-determinism-allow: step failed timestamp
753
+ error: e.message
754
+ });
755
+ break;
756
+ }
757
+ }
758
+ const executionMode = this.sdk.network === "simulated" ? "simulated" : "real";
759
+ const artifact = {
760
+ schema: "hardkas.workflow.v1",
761
+ version: "1.0.0-alpha",
762
+ hardkasVersion: HARDKAS_VERSION2,
763
+ networkId: this.sdk.network,
764
+ mode: executionMode,
765
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
766
+ // hardkas-determinism-allow: workflow artifact creation timestamp
767
+ workflowId,
768
+ artifactId: workflowId,
769
+ status,
770
+ steps: artifactSteps,
771
+ parentArtifacts,
772
+ producedArtifacts,
773
+ generationRange: {
774
+ start: generationStart,
775
+ end: Date.now().toString()
776
+ // hardkas-determinism-allow: ambient end generation clock
777
+ },
778
+ policy: {
779
+ allowNetwork: this.sdk.policy.allowNetwork,
780
+ allowMainnet: this.sdk.policy.allowMainnet,
781
+ allowExternalWallet: this.sdk.policy.allowExternalWallet,
782
+ requireDryRun: this.sdk.policy.requireDryRun
783
+ }
784
+ };
785
+ if (errorEnvelope) {
786
+ artifact.errorEnvelope = errorEnvelope;
787
+ }
788
+ artifact.contentHash = calculateContentHash2(artifact, 1);
789
+ if (!options.dryRun) {
790
+ this.sdk.enforcePolicy("mutation", "Workflow Runtime saving artifact");
791
+ await this.sdk.artifacts.write(artifact, { fileName: `workflow.v1-${workflowId}.json` });
792
+ }
793
+ return artifact;
794
+ }
795
+ };
796
+
229
797
  // src/index.ts
230
798
  import { defineHardkasConfig as defineHardkasConfig2 } from "@hardkas/config";
231
799
 
@@ -261,35 +829,52 @@ var defineTask = taskRegistry.defineTask.bind(taskRegistry);
261
829
  import { buildPaymentPlan as buildPaymentPlan2 } from "@hardkas/tx-builder";
262
830
  import { signTxPlanArtifact as signTxPlanArtifact2 } from "@hardkas/accounts";
263
831
  import {
264
- writeArtifact as writeArtifact2,
832
+ writeArtifact as writeArtifact3,
265
833
  createTxPlanArtifact as createTxPlanArtifact2,
266
- ARTIFACT_SCHEMAS,
267
- HARDKAS_VERSION as HARDKAS_VERSION2
834
+ ARTIFACT_SCHEMAS as ARTIFACT_SCHEMAS2,
835
+ HARDKAS_VERSION as HARDKAS_VERSION3
268
836
  } from "@hardkas/artifacts";
269
837
  import {
270
838
  SOMPI_PER_KAS,
271
- HardkasError,
839
+ HardkasError as HardkasError3,
272
840
  parseKasToSompi as parseKasToSompi2,
273
841
  formatSompi as formatSompi2
274
842
  } from "@hardkas/core";
275
843
  var Hardkas = class _Hardkas {
276
- constructor(config, rpc) {
844
+ constructor(config, options, rpc) {
277
845
  this.config = config;
846
+ this.mode = options?.mode || "developer";
847
+ this.policy = {
848
+ allowNetwork: options?.policy?.allowNetwork ?? this.mode === "developer",
849
+ allowMainnet: options?.policy?.allowMainnet ?? false,
850
+ allowExternalWallet: options?.policy?.allowExternalWallet ?? this.mode === "developer",
851
+ requireDryRun: options?.policy?.requireDryRun ?? this.mode === "agent"
852
+ };
278
853
  this.rpc = rpc || new JsonWrpcKaspaClient({
279
854
  rpcUrl: this.resolveRpcUrl()
280
855
  });
856
+ this.workspace = new HardkasWorkspace(this.config.cwd);
857
+ this.artifacts = new HardkasArtifactsManager(this.workspace);
281
858
  this.accounts = new HardkasAccounts(this);
282
859
  this.tx = new HardkasTx(this);
283
860
  this.l2 = new HardkasL2();
284
861
  this.query = new HardkasQuery(this);
285
862
  this.localnet = new HardkasLocalnet(this);
863
+ this.replay = new HardkasReplay(this);
864
+ this.workflow = new HardkasWorkflow(this);
286
865
  }
287
866
  config;
867
+ workspace;
868
+ artifacts;
288
869
  accounts;
289
870
  tx;
290
871
  l2;
291
872
  query;
292
873
  localnet;
874
+ replay;
875
+ workflow;
876
+ mode;
877
+ policy;
293
878
  rpc;
294
879
  resolveRpcUrl() {
295
880
  const networkId = this.config.config.defaultNetwork || "simnet";
@@ -305,7 +890,7 @@ var Hardkas = class _Hardkas {
305
890
  static async open(dirOrOptions = ".") {
306
891
  const options = typeof dirOrOptions === "string" ? { cwd: dirOrOptions } : dirOrOptions;
307
892
  const loaded = await loadConfig(options);
308
- return new _Hardkas(loaded);
893
+ return new _Hardkas(loaded, options);
309
894
  }
310
895
  /**
311
896
  * Alias for open(). Used in most examples.
@@ -325,24 +910,50 @@ var Hardkas = class _Hardkas {
325
910
  get cwd() {
326
911
  return this.config.cwd;
327
912
  }
913
+ /**
914
+ * Validates an action against the active security policy.
915
+ * Throws HardkasError if the policy is violated.
916
+ */
917
+ enforcePolicy(action, context) {
918
+ if (this.mode === "developer") return;
919
+ const msg = (policy) => `Agent Mode Policy Violation: '${action}' is restricted by policy '${policy}'. ${context || ""}`;
920
+ switch (action) {
921
+ case "network":
922
+ if (!this.policy.allowNetwork) throw new HardkasError2("POLICY_VIOLATION", msg("allowNetwork"));
923
+ break;
924
+ case "mainnet":
925
+ if (!this.policy.allowMainnet) throw new HardkasError2("POLICY_VIOLATION", msg("allowMainnet"));
926
+ break;
927
+ case "external-wallet":
928
+ if (!this.policy.allowExternalWallet) throw new HardkasError2("POLICY_VIOLATION", msg("allowExternalWallet"));
929
+ break;
930
+ case "mutation":
931
+ if (this.policy.requireDryRun) throw new HardkasError2("POLICY_VIOLATION", msg("requireDryRun"));
932
+ break;
933
+ }
934
+ }
328
935
  };
329
936
  export {
330
- ARTIFACT_SCHEMAS,
331
- HARDKAS_VERSION2 as HARDKAS_VERSION,
937
+ ARTIFACT_SCHEMAS2 as ARTIFACT_SCHEMAS,
938
+ HARDKAS_VERSION3 as HARDKAS_VERSION,
332
939
  Hardkas,
333
940
  HardkasAccounts,
334
- HardkasError,
941
+ HardkasArtifactsManager,
942
+ HardkasError3 as HardkasError,
335
943
  HardkasL2,
336
944
  HardkasLocalnet,
337
945
  HardkasQuery,
946
+ HardkasReplay,
338
947
  HardkasTx,
948
+ HardkasWorkspace,
339
949
  SOMPI_PER_KAS,
340
950
  buildPaymentPlan2 as buildPaymentPlan,
951
+ createHardkasClient,
341
952
  createTxPlanArtifact2 as createTxPlanArtifact,
342
953
  defineHardkasConfig2 as defineHardkasConfig,
343
954
  defineTask,
344
955
  formatSompi2 as formatSompi,
345
956
  parseKasToSompi2 as parseKasToSompi,
346
957
  signTxPlanArtifact2 as signTxPlanArtifact,
347
- writeArtifact2 as writeArtifact
958
+ writeArtifact3 as writeArtifact
348
959
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/sdk",
3
- "version": "0.5.5-alpha",
3
+ "version": "0.7.0-alpha",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -9,24 +9,31 @@
9
9
  ],
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
12
+ "sideEffects": false,
12
13
  "exports": {
13
14
  ".": {
14
15
  "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js"
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./client": {
20
+ "types": "./dist/client.d.ts",
21
+ "import": "./dist/client.js",
22
+ "default": "./dist/client.js"
16
23
  }
17
24
  },
18
25
  "dependencies": {
19
- "@hardkas/accounts": "0.5.5-alpha",
20
- "@hardkas/artifacts": "0.5.5-alpha",
21
- "@hardkas/kaspa-rpc": "0.5.5-alpha",
22
- "@hardkas/core": "0.5.5-alpha",
23
- "@hardkas/config": "0.5.5-alpha",
24
- "@hardkas/query": "0.5.5-alpha",
25
- "@hardkas/l2": "0.5.5-alpha",
26
- "@hardkas/simulator": "0.5.5-alpha",
27
- "@hardkas/localnet": "0.5.5-alpha",
28
- "@hardkas/tx-builder": "0.5.5-alpha",
29
- "@hardkas/wallet-adapter": "0.5.5-alpha"
26
+ "@hardkas/config": "0.7.0-alpha",
27
+ "@hardkas/core": "0.7.0-alpha",
28
+ "@hardkas/l2": "0.7.0-alpha",
29
+ "@hardkas/accounts": "0.7.0-alpha",
30
+ "@hardkas/artifacts": "0.7.0-alpha",
31
+ "@hardkas/query": "0.7.0-alpha",
32
+ "@hardkas/kaspa-rpc": "0.7.0-alpha",
33
+ "@hardkas/localnet": "0.7.0-alpha",
34
+ "@hardkas/tx-builder": "0.7.0-alpha",
35
+ "@hardkas/wallet-adapter": "0.7.0-alpha",
36
+ "@hardkas/simulator": "0.7.0-alpha"
30
37
  },
31
38
  "devDependencies": {
32
39
  "tsup": "^8.3.5",
@@ -45,7 +52,7 @@
45
52
  },
46
53
  "homepage": "https://github.com/KasLabDevs/HardKas/tree/main/packages/sdk#readme",
47
54
  "scripts": {
48
- "build": "tsup src/index.ts --format esm --dts --clean",
55
+ "build": "tsup src/index.ts src/client.ts --format esm --dts --clean",
49
56
  "test": "vitest run --passWithNoTests",
50
57
  "typecheck": "tsc --noEmit",
51
58
  "lint": "eslint ."