@chaoschain/sdk 0.3.1 → 0.3.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to the ChaosChain TypeScript SDK will be documented in this
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.2] - 2026-03-23
9
+
10
+ ### Added
11
+
12
+ - **Subpath package exports** for edge/serverless: `@chaoschain/sdk/session` and `@chaoschain/sdk/gateway` (lightweight bundles; no ethers / IPFS / heavy Node deps in those entry points)
13
+ - **`src/gateway/index.ts`** — re-exports `GatewayClient`, selected gateway types, and gateway-related exceptions
14
+
15
+ ### Changed
16
+
17
+ - **`src/types.ts`** — `import type` for `ethers` where only types are needed (no runtime change)
18
+
19
+ ### Documentation
20
+
21
+ - README **Edge Runtime / Serverless Usage**: caveat that `GatewayClient.submitWork()` uses `Buffer`; read/poll APIs are safe on strict V8 isolates; binary `submitWork` needs Node compat or a Buffer polyfill
22
+
8
23
  ## [0.3.1] - 2026-03-22
9
24
 
10
25
  ### Added
package/README.md CHANGED
@@ -52,6 +52,55 @@ Storage backends are optional and intended for development/testing. In productio
52
52
  - **Missing RPC URL** → set `rpcUrl` explicitly (recommended for production).
53
53
  - **Using Gateway without config** → pass `gatewayConfig` or `gatewayUrl` to the constructor.
54
54
 
55
+ ## Edge Runtime / Serverless Usage
56
+
57
+ The main entry point (`@chaoschain/sdk`) includes ethers, IPFS, and Node.js-only dependencies that break in edge runtimes. If you are deploying to **Cloudflare Workers**, **Vercel Edge Functions**, **Deno Deploy**, or any V8 isolate environment, use the lightweight subpath imports instead:
58
+
59
+ ```typescript
60
+ import { SessionClient } from '@chaoschain/sdk/session'; // 6.59 KB
61
+ import { GatewayClient } from '@chaoschain/sdk/gateway'; // 20 KB
62
+ ```
63
+
64
+ > **Note:** `GatewayClient.submitWork()` uses `Buffer` internally for binary encoding.
65
+ > For read-only and polling operations (`getWorkflow`, `getPendingWork`, `getWorkEvidence`),
66
+ > the gateway subpath works in all V8 isolate environments.
67
+ > For `submitWork` with binary content, use Node.js compat mode or a Buffer polyfill.
68
+
69
+ | Entry point | Size | What's included |
70
+ |---|---|---|
71
+ | `@chaoschain/sdk` | 490 KB | Everything: ethers, storage backends, IPFS, all modules |
72
+ | `@chaoschain/sdk/session` | 6.59 KB | `SessionClient`, `Session`, session types |
73
+ | `@chaoschain/sdk/gateway` | 20 KB | `GatewayClient`, workflow types, gateway exceptions |
74
+
75
+ Both subpaths are free of ethers, StorageBackends, form-data, and Node.js built-in dependencies. Use them when you only need to interact with the gateway via HTTP (sessions, score submission, workflow polling).
76
+
77
+ **Example: Cloudflare Worker**
78
+
79
+ ```typescript
80
+ import { SessionClient } from '@chaoschain/sdk/session';
81
+ import { GatewayClient } from '@chaoschain/sdk/gateway';
82
+
83
+ export default {
84
+ async fetch(request: Request, env: Env) {
85
+ const client = new SessionClient({
86
+ gatewayUrl: env.GATEWAY_URL,
87
+ apiKey: env.CHAOSCHAIN_API_KEY,
88
+ });
89
+
90
+ const session = await client.start({
91
+ studio_address: env.STUDIO_ADDRESS,
92
+ agent_address: env.AGENT_ADDRESS,
93
+ task_type: 'feature',
94
+ });
95
+
96
+ await session.step('implementing', 'Built feature X');
97
+ const { workflow_id, data_hash } = await session.complete();
98
+
99
+ return Response.json({ workflow_id, data_hash });
100
+ },
101
+ };
102
+ ```
103
+
55
104
  ## Engineering Studio — Session SDK
56
105
 
57
106
  The Session SDK enables AI agents to capture their work sessions without manually constructing event schemas or DAGs. Sessions automatically build a verifiable Evidence DAG that produces trust profiles for reputation and scoring.
@@ -1,4 +1,4 @@
1
- import { n as StorageProvider, U as UploadOptions, i as UploadResult } from './types-BBVtx_jV.js';
1
+ import { S as StorageProvider, U as UploadOptions, a as UploadResult } from './types-C0Ay90UI.js';
2
2
 
3
3
  /**
4
4
  * Local IPFS Storage Provider
@@ -1,4 +1,4 @@
1
- import { n as StorageProvider, U as UploadOptions, i as UploadResult } from './types-BBVtx_jV.cjs';
1
+ import { S as StorageProvider, U as UploadOptions, a as UploadResult } from './types-C0Ay90UI.cjs';
2
2
 
3
3
  /**
4
4
  * Local IPFS Storage Provider
@@ -0,0 +1,591 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var axios__default = /*#__PURE__*/_interopDefault(axios);
8
+
9
+ // src/GatewayClient.ts
10
+
11
+ // src/exceptions.ts
12
+ var ChaosChainSDKError = class _ChaosChainSDKError extends Error {
13
+ details;
14
+ constructor(message, details = {}) {
15
+ super(message);
16
+ this.name = "ChaosChainSDKError";
17
+ this.details = details;
18
+ Object.setPrototypeOf(this, _ChaosChainSDKError.prototype);
19
+ }
20
+ toString() {
21
+ if (Object.keys(this.details).length > 0) {
22
+ return `${this.message} | Details: ${JSON.stringify(this.details)}`;
23
+ }
24
+ return this.message;
25
+ }
26
+ };
27
+ var GatewayError = class _GatewayError extends ChaosChainSDKError {
28
+ statusCode;
29
+ response;
30
+ category;
31
+ retryable;
32
+ constructor(message, details) {
33
+ super(message, details || {});
34
+ this.name = "GatewayError";
35
+ this.statusCode = details?.statusCode;
36
+ this.response = details?.response;
37
+ this.category = details?.category;
38
+ this.retryable = details?.retryable;
39
+ Object.setPrototypeOf(this, _GatewayError.prototype);
40
+ }
41
+ };
42
+ var GatewayConnectionError = class _GatewayConnectionError extends GatewayError {
43
+ constructor(message) {
44
+ super(message);
45
+ this.name = "GatewayConnectionError";
46
+ Object.setPrototypeOf(this, _GatewayConnectionError.prototype);
47
+ }
48
+ };
49
+ var GatewayTimeoutError = class _GatewayTimeoutError extends GatewayError {
50
+ workflowId;
51
+ lastStatus;
52
+ constructor(workflowId, message, lastStatus) {
53
+ super(message);
54
+ this.name = "GatewayTimeoutError";
55
+ this.workflowId = workflowId;
56
+ this.lastStatus = lastStatus;
57
+ Object.setPrototypeOf(this, _GatewayTimeoutError.prototype);
58
+ }
59
+ };
60
+ var WorkflowFailedError = class _WorkflowFailedError extends GatewayError {
61
+ workflowId;
62
+ workflowError;
63
+ constructor(workflowId, error) {
64
+ super(`Workflow ${workflowId} failed at step ${error.step}: ${error.message}`);
65
+ this.name = "WorkflowFailedError";
66
+ this.workflowId = workflowId;
67
+ this.workflowError = error;
68
+ Object.setPrototypeOf(this, _WorkflowFailedError.prototype);
69
+ }
70
+ };
71
+
72
+ // src/GatewayClient.ts
73
+ var GatewayClient = class {
74
+ gatewayUrl;
75
+ timeout;
76
+ maxPollTime;
77
+ pollInterval;
78
+ defaultHeaders;
79
+ auth;
80
+ retryConfig;
81
+ constructor(config) {
82
+ const rawBaseUrl = config.baseUrl ?? config.gatewayUrl ?? "https://gateway.chaoscha.in";
83
+ let parsed;
84
+ try {
85
+ parsed = new URL(rawBaseUrl);
86
+ } catch {
87
+ throw new Error(
88
+ `Invalid gateway baseUrl "${rawBaseUrl}". Provide a valid absolute URL, e.g. https://gateway.chaoscha.in`
89
+ );
90
+ }
91
+ if (!["http:", "https:"].includes(parsed.protocol)) {
92
+ throw new Error(
93
+ `Invalid gateway baseUrl protocol "${parsed.protocol}". Only http/https are supported.`
94
+ );
95
+ }
96
+ this.gatewayUrl = parsed.toString().replace(/\/$/, "");
97
+ this.timeout = this._resolveTimeout(
98
+ config.timeoutMs,
99
+ config.timeoutSeconds,
100
+ config.timeout,
101
+ 3e4
102
+ );
103
+ this.maxPollTime = this._resolveTimeout(
104
+ config.maxPollTimeMs,
105
+ config.maxPollTimeSeconds,
106
+ config.maxPollTime,
107
+ 6e5
108
+ );
109
+ this.pollInterval = this._resolveTimeout(
110
+ config.pollIntervalMs,
111
+ config.pollIntervalSeconds,
112
+ config.pollInterval,
113
+ 2e3
114
+ );
115
+ this.defaultHeaders = config.headers;
116
+ this.auth = config.auth;
117
+ this.retryConfig = config.retry;
118
+ }
119
+ // ===========================================================================
120
+ // Private: HTTP Request
121
+ // ===========================================================================
122
+ // Resolve timeout with explicit ms taking precedence, then seconds, then legacy ms.
123
+ _resolveTimeout(timeoutMs, timeoutSeconds, legacyTimeoutMs, defaultMs) {
124
+ if (typeof timeoutMs === "number") return timeoutMs;
125
+ if (typeof timeoutSeconds === "number") return timeoutSeconds * 1e3;
126
+ if (typeof legacyTimeoutMs === "number") return legacyTimeoutMs;
127
+ return defaultMs ?? 0;
128
+ }
129
+ _resolveAuthMode() {
130
+ if (!this.auth) return void 0;
131
+ if (this.auth.authMode) return this.auth.authMode;
132
+ if (this.auth.apiKey) return "apiKey";
133
+ if (this.auth.signature) return "signature";
134
+ return void 0;
135
+ }
136
+ _buildHeaders() {
137
+ const headers = {
138
+ "Content-Type": "application/json"
139
+ };
140
+ if (this.defaultHeaders) {
141
+ Object.assign(headers, this.defaultHeaders);
142
+ }
143
+ const authMode = this._resolveAuthMode();
144
+ if (authMode === "apiKey" && this.auth?.apiKey) {
145
+ headers["X-API-Key"] = this.auth.apiKey;
146
+ }
147
+ if (authMode === "signature" && this.auth?.signature) {
148
+ const timestamp = this.auth.signature.timestamp ?? Date.now();
149
+ headers["X-Signature"] = this.auth.signature.signature;
150
+ headers["X-Timestamp"] = `${timestamp}`;
151
+ headers["X-Address"] = this.auth.signature.address;
152
+ }
153
+ if (!headers["Content-Type"]) {
154
+ headers["Content-Type"] = "application/json";
155
+ }
156
+ return headers;
157
+ }
158
+ _classifyStatusCode(statusCode) {
159
+ if (statusCode === 401 || statusCode === 403) {
160
+ return { statusCode, category: "auth", retryable: false };
161
+ }
162
+ if (statusCode === 408 || statusCode === 429 || statusCode !== void 0 && statusCode >= 500) {
163
+ return { statusCode, category: "transient", retryable: true };
164
+ }
165
+ if (statusCode !== void 0 && statusCode >= 400) {
166
+ return { statusCode, category: "permanent", retryable: false };
167
+ }
168
+ return { statusCode, category: "unknown", retryable: false };
169
+ }
170
+ _normalizeError(error) {
171
+ if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") {
172
+ const connectionError = new GatewayConnectionError(
173
+ `Failed to connect to Gateway at ${this.gatewayUrl}`
174
+ );
175
+ connectionError.details.category = "transient";
176
+ connectionError.details.retryable = true;
177
+ connectionError.category = "transient";
178
+ connectionError.retryable = true;
179
+ return connectionError;
180
+ }
181
+ if (error.code === "ETIMEDOUT" || error.code === "ECONNABORTED") {
182
+ const timeoutError = new GatewayTimeoutError(
183
+ "timeout",
184
+ `request to Gateway timed out: ${error.message}`
185
+ );
186
+ timeoutError.details.category = "transient";
187
+ timeoutError.details.retryable = true;
188
+ timeoutError.category = "transient";
189
+ timeoutError.retryable = true;
190
+ return timeoutError;
191
+ }
192
+ if (error.response) {
193
+ const data = error.response.data;
194
+ const message = data?.error || data?.message || "Unknown error from Gateway";
195
+ const classification2 = this._classifyStatusCode(error.response.status);
196
+ return new GatewayError(`Gateway returned error: ${message}`, {
197
+ statusCode: error.response.status,
198
+ response: data,
199
+ category: classification2.category,
200
+ retryable: classification2.retryable
201
+ });
202
+ }
203
+ const classification = this._classifyStatusCode(void 0);
204
+ return new GatewayError(`Gateway request failed: ${error.message}`, {
205
+ category: classification.category,
206
+ retryable: classification.retryable
207
+ });
208
+ }
209
+ _getRetryDelayMs(attempt) {
210
+ const initialDelayMs = this.retryConfig?.initialDelayMs ?? 500;
211
+ const backoffFactor = this.retryConfig?.backoffFactor ?? 2;
212
+ const maxDelayMs = this.retryConfig?.maxDelayMs ?? 8e3;
213
+ const jitterEnabled = this.retryConfig?.jitter ?? true;
214
+ const jitterRatio = this.retryConfig?.jitterRatio ?? 0.2;
215
+ let delay = Math.min(maxDelayMs, initialDelayMs * Math.pow(backoffFactor, attempt));
216
+ if (jitterEnabled) {
217
+ const delta = delay * jitterRatio;
218
+ delay = delay + (Math.random() * 2 - 1) * delta;
219
+ delay = Math.max(0, delay);
220
+ }
221
+ return Math.round(delay);
222
+ }
223
+ async _sleep(durationMs) {
224
+ await new Promise((resolve) => setTimeout(resolve, durationMs));
225
+ }
226
+ /**
227
+ * Make HTTP request to Gateway.
228
+ * Handles errors and transforms them to Gateway exceptions.
229
+ */
230
+ async _request(method, path, data) {
231
+ const url = `${this.gatewayUrl}${path}`;
232
+ const maxRetries = this.retryConfig?.maxRetries ?? 3;
233
+ const retriesEnabled = this.retryConfig?.enabled === true;
234
+ let attempt = 0;
235
+ while (true) {
236
+ try {
237
+ const response = await axios__default.default({
238
+ method,
239
+ url,
240
+ data,
241
+ timeout: this.timeout,
242
+ headers: this._buildHeaders()
243
+ });
244
+ return response.data;
245
+ } catch (error) {
246
+ const normalizedError = this._normalizeError(error);
247
+ const category = normalizedError.category;
248
+ const retryable = normalizedError.retryable;
249
+ const shouldRetry = retriesEnabled === true && category === "transient" && retryable === true && attempt < maxRetries;
250
+ if (!shouldRetry) {
251
+ throw normalizedError;
252
+ }
253
+ const delay = this._getRetryDelayMs(attempt);
254
+ attempt += 1;
255
+ await this._sleep(delay);
256
+ }
257
+ }
258
+ }
259
+ /**
260
+ * Parse workflow status from API response.
261
+ */
262
+ _parseWorkflowStatus(data) {
263
+ const progress = {
264
+ arweaveTxId: data.progress?.arweave_tx_id,
265
+ arweaveConfirmed: data.progress?.arweave_confirmed,
266
+ onchainTxHash: data.progress?.onchain_tx_hash,
267
+ onchainConfirmed: data.progress?.onchain_confirmed,
268
+ onchainBlock: data.progress?.onchain_block,
269
+ scoreTxHash: data.progress?.score_tx_hash,
270
+ commitTxHash: data.progress?.commit_tx_hash,
271
+ revealTxHash: data.progress?.reveal_tx_hash
272
+ };
273
+ const error = data.error ? {
274
+ step: data.error.step || "",
275
+ message: data.error.message || "",
276
+ code: data.error.code
277
+ } : void 0;
278
+ return {
279
+ workflowId: data.id,
280
+ workflowType: data.type,
281
+ state: data.state,
282
+ step: data.step,
283
+ createdAt: data.created_at,
284
+ updatedAt: data.updated_at,
285
+ progress,
286
+ error
287
+ };
288
+ }
289
+ // ===========================================================================
290
+ // Health Check
291
+ // ===========================================================================
292
+ async healthCheck() {
293
+ return this._request("GET", "/health");
294
+ }
295
+ async isHealthy() {
296
+ try {
297
+ const result = await this.healthCheck();
298
+ return result.status === "ok";
299
+ } catch (error) {
300
+ return false;
301
+ }
302
+ }
303
+ // ===========================================================================
304
+ // Workflow Submission
305
+ // ===========================================================================
306
+ /**
307
+ * Create a work submission workflow.
308
+ * POST /workflows/work-submission
309
+ *
310
+ * SDK prepares inputs; Gateway handles:
311
+ * - Evidence upload to Arweave
312
+ * - Transaction submission
313
+ * - Confirmation waiting
314
+ *
315
+ * @param studioAddress - Ethereum address of the studio
316
+ * @param epoch - Epoch number
317
+ * @param agentAddress - Ethereum address of the submitting agent
318
+ * @param dataHash - Bytes32 hash of the work (as hex string)
319
+ * @param threadRoot - Bytes32 DKG thread root (as hex string)
320
+ * @param evidenceRoot - Bytes32 evidence Merkle root (as hex string)
321
+ * @param evidenceContent - Raw evidence bytes (will be base64 encoded)
322
+ * @param signerAddress - Ethereum address of the signer (must be registered in Gateway)
323
+ * @returns WorkflowStatus - Initial status of the created workflow
324
+ */
325
+ async submitWork(studioAddress, epoch, agentAddress, dataHash, threadRoot, evidenceRoot, evidenceContent, signerAddress) {
326
+ const evidenceContentBase64 = Buffer.isBuffer(evidenceContent) ? evidenceContent.toString("base64") : Buffer.from(evidenceContent, "utf-8").toString("base64");
327
+ const payload = {
328
+ studio_address: studioAddress,
329
+ epoch,
330
+ agent_address: agentAddress,
331
+ data_hash: dataHash,
332
+ thread_root: threadRoot,
333
+ evidence_root: evidenceRoot,
334
+ evidence_content: evidenceContentBase64,
335
+ signer_address: signerAddress
336
+ };
337
+ const result = await this._request(
338
+ "POST",
339
+ "/workflows/work-submission",
340
+ payload
341
+ );
342
+ return this._parseWorkflowStatus(result);
343
+ }
344
+ /**
345
+ * Create a score submission workflow.
346
+ * POST /workflows/score-submission
347
+ *
348
+ * Supports two modes:
349
+ * - DIRECT (default): Simple direct scoring, requires workerAddress
350
+ * - COMMIT_REVEAL: Commit-reveal pattern, requires salt
351
+ *
352
+ * @param studioAddress - Ethereum address of the studio
353
+ * @param epoch - Epoch number
354
+ * @param validatorAddress - Ethereum address of the validator
355
+ * @param dataHash - Bytes32 hash of the work being scored (as hex string)
356
+ * @param scores - Array of dimension scores (0-10000 basis points)
357
+ * @param signerAddress - Ethereum address of the signer
358
+ * @param options - Additional options (workerAddress, salt, mode)
359
+ */
360
+ async submitScore(studioAddress, epoch, validatorAddress, dataHash, scores, signerAddress, options) {
361
+ const mode = options?.mode ?? "direct" /* DIRECT */;
362
+ if (mode === "direct" /* DIRECT */ && !options?.workerAddress) {
363
+ throw new Error("workerAddress is required for DIRECT score scoring mode");
364
+ }
365
+ if (mode === "commit_reveal" /* COMMIT_REVEAL */ && !options?.salt) {
366
+ throw new Error("salt is required for COMMIT_REVEAL score scoring mode");
367
+ }
368
+ const payload = {
369
+ studio_address: studioAddress,
370
+ epoch,
371
+ validator_address: validatorAddress,
372
+ data_hash: dataHash,
373
+ scores,
374
+ signer_address: signerAddress,
375
+ mode,
376
+ salt: options?.salt ?? "0x" + "0".repeat(64)
377
+ };
378
+ if (options?.workerAddress) {
379
+ payload.worker_address = options.workerAddress;
380
+ }
381
+ const result = await this._request(
382
+ "POST",
383
+ "/workflows/score-submission",
384
+ payload
385
+ );
386
+ return this._parseWorkflowStatus(result);
387
+ }
388
+ /**
389
+ * Create a close epoch workflow.
390
+ * POST /workflows/close-epoch
391
+ *
392
+ * This is economically final — cannot be undone.
393
+ *
394
+ * @param studioAddress - Ethereum address of the studio
395
+ * @param epoch - Epoch number to close
396
+ * @param signerAddress - Ethereum address of the signer
397
+ */
398
+ async closeEpoch(studioAddress, epoch, signerAddress) {
399
+ const payload = {
400
+ studio_address: studioAddress,
401
+ epoch,
402
+ signer_address: signerAddress
403
+ };
404
+ const result = await this._request(
405
+ "POST",
406
+ "/workflows/close-epoch",
407
+ payload
408
+ );
409
+ return this._parseWorkflowStatus(result);
410
+ }
411
+ // ===========================================================================
412
+ // Workflow Status
413
+ // ===========================================================================
414
+ /**
415
+ * Get workflow status by ID.
416
+ * GET /workflows/{id}
417
+ */
418
+ async getWorkflow(workflowId) {
419
+ const result = await this._request("GET", `/workflows/${workflowId}`);
420
+ return this._parseWorkflowStatus(result);
421
+ }
422
+ /**
423
+ * List workflows with optional filters.
424
+ * GET /workflows?studio=&state=&type=
425
+ */
426
+ async listWorkflows(options) {
427
+ const params = [];
428
+ if (options?.studio) params.push(`studio=${options.studio}`);
429
+ if (options?.state) params.push(`state=${options.state}`);
430
+ if (options?.workflowType) params.push(`type=${options.workflowType}`);
431
+ const queryString = params.length > 0 ? `?${params.join("&")}` : "";
432
+ const result = await this._request(
433
+ "GET",
434
+ `/workflows${queryString}`
435
+ );
436
+ return (result.workflows || []).map((w) => this._parseWorkflowStatus(w));
437
+ }
438
+ // ===========================================================================
439
+ // Polling and Waiting
440
+ // ===========================================================================
441
+ /**
442
+ * Poll workflow until it reaches a terminal state.
443
+ *
444
+ * @param workflowId - UUID of the workflow
445
+ * @param options - Polling options
446
+ * @throws WorkflowFailedError - If workflow reaches FAILED state
447
+ * @throws GatewayTimeoutError - If maxWait exceeded
448
+ */
449
+ async waitForCompletion(workflowId, options) {
450
+ const maxWait = options?.maxWait || this.maxPollTime;
451
+ const pollInterval = options?.pollInterval || this.pollInterval;
452
+ const startTime = Date.now();
453
+ while (true) {
454
+ const status = await this.getWorkflow(workflowId);
455
+ if (options?.onProgress) {
456
+ options.onProgress(status);
457
+ }
458
+ if (status.state === "COMPLETED" /* COMPLETED */) {
459
+ return status;
460
+ }
461
+ if (status.state === "FAILED" /* FAILED */) {
462
+ throw new WorkflowFailedError(workflowId, status.error);
463
+ }
464
+ const elapsed = Date.now() - startTime;
465
+ if (elapsed >= maxWait) {
466
+ throw new GatewayTimeoutError(
467
+ workflowId,
468
+ `Workflow ${workflowId} did not complete within ${maxWait} ms.Current state: ${status.state}, step: ${status.step}`,
469
+ status
470
+ );
471
+ }
472
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
473
+ }
474
+ }
475
+ // ===========================================================================
476
+ // Convenience Methods (submit + wait)
477
+ // ===========================================================================
478
+ /**
479
+ * Submit work and wait for completion.
480
+ */
481
+ async submitWorkAndWait(studioAddress, epoch, agentAddress, dataHash, threadRoot, evidenceRoot, evidenceContent, signerAddress, options) {
482
+ const workflow = await this.submitWork(
483
+ studioAddress,
484
+ epoch,
485
+ agentAddress,
486
+ dataHash,
487
+ threadRoot,
488
+ evidenceRoot,
489
+ evidenceContent,
490
+ signerAddress
491
+ );
492
+ return this.waitForCompletion(workflow.workflowId, options);
493
+ }
494
+ /**
495
+ * Submit score and wait for completion.
496
+ */
497
+ async submitScoreAndWait(studioAddress, epoch, validatorAddress, dataHash, scores, signerAddress, options) {
498
+ const workerAddress = options?.workerAddress ?? options?.workAddress;
499
+ const workflow = await this.submitScore(
500
+ studioAddress,
501
+ epoch,
502
+ validatorAddress,
503
+ dataHash,
504
+ scores,
505
+ signerAddress,
506
+ {
507
+ workerAddress,
508
+ salt: options?.salt,
509
+ mode: options?.mode
510
+ }
511
+ );
512
+ return this.waitForCompletion(workflow.workflowId, { onProgress: options?.onProgress });
513
+ }
514
+ /**
515
+ * Close epoch and wait for completion.
516
+ */
517
+ async closeEpochAndWait(studioAddress, epoch, signerAddress, options) {
518
+ const workflow = await this.closeEpoch(studioAddress, epoch, signerAddress);
519
+ return this.waitForCompletion(workflow.workflowId, options);
520
+ }
521
+ // ===========================================================================
522
+ // Read API — Studio Work Discovery
523
+ // ===========================================================================
524
+ /**
525
+ * Fetch pending (unfinalized) work for a studio from the gateway.
526
+ *
527
+ * @param studioAddress - 0x-prefixed studio contract address
528
+ * @param options - Optional limit/offset for pagination
529
+ * @returns Typed pending work response
530
+ */
531
+ async getPendingWork(studioAddress, options) {
532
+ const limit = options?.limit ?? 20;
533
+ const offset = options?.offset ?? 0;
534
+ const url = `${this.gatewayUrl}/v1/studio/${studioAddress}/work?status=pending&limit=${limit}&offset=${offset}`;
535
+ try {
536
+ const response = await axios__default.default.get(url, {
537
+ timeout: this.timeout,
538
+ headers: this._buildHeaders()
539
+ });
540
+ return response.data;
541
+ } catch (error) {
542
+ const axiosErr = error;
543
+ if (axiosErr.code === "ECONNREFUSED" || axiosErr.code === "ENOTFOUND" || !axiosErr.response) {
544
+ throw new GatewayConnectionError(
545
+ `ChaosChain gateway unreachable at ${this.gatewayUrl}. Check GATEWAY_URL.`
546
+ );
547
+ }
548
+ if (axiosErr.response) {
549
+ throw new GatewayError(
550
+ `Gateway returned ${axiosErr.response.status}: ${JSON.stringify(axiosErr.response.data)}`
551
+ );
552
+ }
553
+ throw error;
554
+ }
555
+ }
556
+ /**
557
+ * Fetch full evidence graph for a work submission.
558
+ * Endpoint: GET /v1/work/{hash}/evidence
559
+ */
560
+ async getWorkEvidence(workHash) {
561
+ const url = `${this.gatewayUrl}/v1/work/${workHash}/evidence`;
562
+ try {
563
+ const response = await axios__default.default.get(url, {
564
+ timeout: this.timeout,
565
+ headers: this._buildHeaders()
566
+ });
567
+ return response.data;
568
+ } catch (error) {
569
+ const axiosErr = error;
570
+ if (axiosErr.code === "ECONNREFUSED" || axiosErr.code === "ENOTFOUND" || !axiosErr.response) {
571
+ throw new GatewayConnectionError(
572
+ `ChaosChain gateway unreachable at ${this.gatewayUrl}. Check GATEWAY_URL.`
573
+ );
574
+ }
575
+ if (axiosErr.response) {
576
+ throw new GatewayError(
577
+ `Gateway returned ${axiosErr.response.status}: ${JSON.stringify(axiosErr.response.data)}`
578
+ );
579
+ }
580
+ throw error;
581
+ }
582
+ }
583
+ };
584
+
585
+ exports.GatewayClient = GatewayClient;
586
+ exports.GatewayConnectionError = GatewayConnectionError;
587
+ exports.GatewayError = GatewayError;
588
+ exports.GatewayTimeoutError = GatewayTimeoutError;
589
+ exports.WorkflowFailedError = WorkflowFailedError;
590
+ //# sourceMappingURL=index.cjs.map
591
+ //# sourceMappingURL=index.cjs.map