@singularity-layer/grid 0.2.0 → 0.4.0

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/README.md CHANGED
@@ -32,7 +32,7 @@ import OpenAI from "openai";
32
32
 
33
33
  const client = new OpenAI({
34
34
  baseURL:
35
- "https://sgl-network-orchestrator.ivaavimusicproductions.workers.dev/v1",
35
+ "https://grid.x402compute.cc/v1",
36
36
  apiKey: "sgl-anonymous",
37
37
  });
38
38
 
package/dist/index.d.mts CHANGED
@@ -80,6 +80,26 @@ interface ChatCompletionRequest {
80
80
  temperature?: number;
81
81
  max_tokens?: number;
82
82
  stream?: boolean;
83
+ /** Pin a specific provider node (else the grid routes). See `providers()`. */
84
+ node?: string;
85
+ /** Restrict to a cluster's nodes (cluster slug or id). */
86
+ cluster?: string;
87
+ /** Pay in the cluster's token instead of USDC (cluster requests only). */
88
+ pay_in_coin?: boolean;
89
+ }
90
+ /** A node serving a model, with its effective per-token price. From `providers()`. */
91
+ interface ProviderInfo {
92
+ node_id: string;
93
+ input_per_m: number;
94
+ output_per_m: number;
95
+ blended_per_1k: number;
96
+ /** true if the operator set a custom price; false = platform reference. */
97
+ is_custom: boolean;
98
+ reputation?: number;
99
+ load?: number;
100
+ max_concurrent_jobs?: number;
101
+ tee_type?: string;
102
+ online: boolean;
83
103
  }
84
104
  interface ChatChoice {
85
105
  index: number;
@@ -97,6 +117,24 @@ interface ChatCompletionResponse {
97
117
  completion_tokens: number;
98
118
  total_tokens: number;
99
119
  };
120
+ /** Confidential-compute attestation of the serving node (E2E path). */
121
+ attestation?: Attestation;
122
+ }
123
+ /** Confidential-compute attestation of the node that served the request. */
124
+ interface Attestation {
125
+ nodeId: string;
126
+ teeType: string | null;
127
+ verified: boolean;
128
+ }
129
+ /** Response from POST /v1/reserve — the node + the X25519 key to seal the prompt to. */
130
+ interface ReserveResponse {
131
+ reservation_token: string;
132
+ node_id: string;
133
+ node_x25519_pubkey: string;
134
+ node_ed25519_pubkey: string | null;
135
+ tee_type?: string | null;
136
+ attestation_verified?: boolean;
137
+ expires_in_ms: number;
100
138
  }
101
139
  interface DeployProcessorOptions {
102
140
  name: string;
@@ -197,13 +235,39 @@ declare class GridClient {
197
235
  capacity(): Promise<CapacityResponse>;
198
236
  models(): Promise<ModelInfo[]>;
199
237
  pricing(): Promise<PricingInfo[]>;
238
+ /**
239
+ * List the nodes serving a model with each node's effective per-token price
240
+ * (operator's custom price if set, else the platform reference), cheapest
241
+ * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,
242
+ * or omit to let the grid route. Optional `cluster` filter (slug or id).
243
+ */
244
+ providers(model: string, options?: {
245
+ cluster?: string;
246
+ }): Promise<ProviderInfo[]>;
200
247
  submitJob(model: string, input: Record<string, unknown>, options?: {
201
248
  submitterWallet?: string;
202
249
  submitterChain?: string;
203
250
  }): Promise<JobResponse>;
204
251
  getJob(jobId: string): Promise<JobResult>;
205
252
  getAttestation(jobId: string): Promise<AttestationProof>;
253
+ /** Reserve a node + learn its X25519 key so we can seal the prompt to it.
254
+ * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,
255
+ * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */
256
+ private reserve;
257
+ /**
258
+ * End-to-end encrypted chat completion. The prompt is sealed in this client to
259
+ * the serving node's key and only decrypts inside its TEE — the orchestrator
260
+ * only relays ciphertext. Requires an `apiKey` (credits); without one the grid
261
+ * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).
262
+ */
206
263
  chatCompletions(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
264
+ /**
265
+ * Streaming end-to-end encrypted chat completion. Yields decoded text as it
266
+ * arrives; each chunk is decrypted and its ordering + termination verified (a
267
+ * truncated stream throws). Requires `apiKey` (credits). If the server isn't
268
+ * streaming (toggle off), the whole reply is yielded as a single chunk.
269
+ */
270
+ chatCompletionStream(request: ChatCompletionRequest): AsyncGenerator<string, void, unknown>;
207
271
  private requestWithWalletAuth;
208
272
  private requestWithPayment;
209
273
  deployProcessor(wallet: WalletAuth, options: DeployProcessorOptions): Promise<ProcessorDeployResult>;
@@ -245,4 +309,4 @@ declare class SGLConnectionError extends SGLError {
245
309
  constructor(message: string);
246
310
  }
247
311
 
248
- export { type AttestationProof, type CapacityResponse, type ChatChoice, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, DEFAULT_BASE_URL, type DeployProcessorOptions, GridClient, type GridClientOptions, type JobResponse, type JobResult, type JobSubmission, type ModelInfo, type ModelPricing, type PricingInfo, type ProcessorDeployResult, type ProcessorInfo, type ProcessorInvokeResult, type ProcessorListResponse, type ProcessorLogEntry, type ProcessorLogsResponse, SGLAPIError, SGLAuthError, SGLConnectionError, SGLError, SGLNotFoundError, type TeeCapacity, type WalletAuth };
312
+ export { type Attestation, type AttestationProof, type CapacityResponse, type ChatChoice, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, DEFAULT_BASE_URL, type DeployProcessorOptions, GridClient, type GridClientOptions, type JobResponse, type JobResult, type JobSubmission, type ModelInfo, type ModelPricing, type PricingInfo, type ProcessorDeployResult, type ProcessorInfo, type ProcessorInvokeResult, type ProcessorListResponse, type ProcessorLogEntry, type ProcessorLogsResponse, type ReserveResponse, SGLAPIError, SGLAuthError, SGLConnectionError, SGLError, SGLNotFoundError, type TeeCapacity, type WalletAuth };
package/dist/index.d.ts CHANGED
@@ -80,6 +80,26 @@ interface ChatCompletionRequest {
80
80
  temperature?: number;
81
81
  max_tokens?: number;
82
82
  stream?: boolean;
83
+ /** Pin a specific provider node (else the grid routes). See `providers()`. */
84
+ node?: string;
85
+ /** Restrict to a cluster's nodes (cluster slug or id). */
86
+ cluster?: string;
87
+ /** Pay in the cluster's token instead of USDC (cluster requests only). */
88
+ pay_in_coin?: boolean;
89
+ }
90
+ /** A node serving a model, with its effective per-token price. From `providers()`. */
91
+ interface ProviderInfo {
92
+ node_id: string;
93
+ input_per_m: number;
94
+ output_per_m: number;
95
+ blended_per_1k: number;
96
+ /** true if the operator set a custom price; false = platform reference. */
97
+ is_custom: boolean;
98
+ reputation?: number;
99
+ load?: number;
100
+ max_concurrent_jobs?: number;
101
+ tee_type?: string;
102
+ online: boolean;
83
103
  }
84
104
  interface ChatChoice {
85
105
  index: number;
@@ -97,6 +117,24 @@ interface ChatCompletionResponse {
97
117
  completion_tokens: number;
98
118
  total_tokens: number;
99
119
  };
120
+ /** Confidential-compute attestation of the serving node (E2E path). */
121
+ attestation?: Attestation;
122
+ }
123
+ /** Confidential-compute attestation of the node that served the request. */
124
+ interface Attestation {
125
+ nodeId: string;
126
+ teeType: string | null;
127
+ verified: boolean;
128
+ }
129
+ /** Response from POST /v1/reserve — the node + the X25519 key to seal the prompt to. */
130
+ interface ReserveResponse {
131
+ reservation_token: string;
132
+ node_id: string;
133
+ node_x25519_pubkey: string;
134
+ node_ed25519_pubkey: string | null;
135
+ tee_type?: string | null;
136
+ attestation_verified?: boolean;
137
+ expires_in_ms: number;
100
138
  }
101
139
  interface DeployProcessorOptions {
102
140
  name: string;
@@ -197,13 +235,39 @@ declare class GridClient {
197
235
  capacity(): Promise<CapacityResponse>;
198
236
  models(): Promise<ModelInfo[]>;
199
237
  pricing(): Promise<PricingInfo[]>;
238
+ /**
239
+ * List the nodes serving a model with each node's effective per-token price
240
+ * (operator's custom price if set, else the platform reference), cheapest
241
+ * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,
242
+ * or omit to let the grid route. Optional `cluster` filter (slug or id).
243
+ */
244
+ providers(model: string, options?: {
245
+ cluster?: string;
246
+ }): Promise<ProviderInfo[]>;
200
247
  submitJob(model: string, input: Record<string, unknown>, options?: {
201
248
  submitterWallet?: string;
202
249
  submitterChain?: string;
203
250
  }): Promise<JobResponse>;
204
251
  getJob(jobId: string): Promise<JobResult>;
205
252
  getAttestation(jobId: string): Promise<AttestationProof>;
253
+ /** Reserve a node + learn its X25519 key so we can seal the prompt to it.
254
+ * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,
255
+ * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */
256
+ private reserve;
257
+ /**
258
+ * End-to-end encrypted chat completion. The prompt is sealed in this client to
259
+ * the serving node's key and only decrypts inside its TEE — the orchestrator
260
+ * only relays ciphertext. Requires an `apiKey` (credits); without one the grid
261
+ * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).
262
+ */
206
263
  chatCompletions(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
264
+ /**
265
+ * Streaming end-to-end encrypted chat completion. Yields decoded text as it
266
+ * arrives; each chunk is decrypted and its ordering + termination verified (a
267
+ * truncated stream throws). Requires `apiKey` (credits). If the server isn't
268
+ * streaming (toggle off), the whole reply is yielded as a single chunk.
269
+ */
270
+ chatCompletionStream(request: ChatCompletionRequest): AsyncGenerator<string, void, unknown>;
207
271
  private requestWithWalletAuth;
208
272
  private requestWithPayment;
209
273
  deployProcessor(wallet: WalletAuth, options: DeployProcessorOptions): Promise<ProcessorDeployResult>;
@@ -245,4 +309,4 @@ declare class SGLConnectionError extends SGLError {
245
309
  constructor(message: string);
246
310
  }
247
311
 
248
- export { type AttestationProof, type CapacityResponse, type ChatChoice, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, DEFAULT_BASE_URL, type DeployProcessorOptions, GridClient, type GridClientOptions, type JobResponse, type JobResult, type JobSubmission, type ModelInfo, type ModelPricing, type PricingInfo, type ProcessorDeployResult, type ProcessorInfo, type ProcessorInvokeResult, type ProcessorListResponse, type ProcessorLogEntry, type ProcessorLogsResponse, SGLAPIError, SGLAuthError, SGLConnectionError, SGLError, SGLNotFoundError, type TeeCapacity, type WalletAuth };
312
+ export { type Attestation, type AttestationProof, type CapacityResponse, type ChatChoice, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, DEFAULT_BASE_URL, type DeployProcessorOptions, GridClient, type GridClientOptions, type JobResponse, type JobResult, type JobSubmission, type ModelInfo, type ModelPricing, type PricingInfo, type ProcessorDeployResult, type ProcessorInfo, type ProcessorInvokeResult, type ProcessorListResponse, type ProcessorLogEntry, type ProcessorLogsResponse, type ReserveResponse, SGLAPIError, SGLAuthError, SGLConnectionError, SGLError, SGLNotFoundError, type TeeCapacity, type WalletAuth };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -64,6 +74,78 @@ var SGLConnectionError = class extends SGLError {
64
74
  }
65
75
  };
66
76
 
77
+ // src/e2e.ts
78
+ var import_ed25519 = require("@noble/curves/ed25519");
79
+ var import_chacha = require("@noble/ciphers/chacha");
80
+ var import_sha256 = require("@noble/hashes/sha256");
81
+ var import_hkdf = require("@noble/hashes/hkdf");
82
+ var import_bs58 = __toESM(require("bs58"));
83
+ var ALGO_V2 = "x25519-xchacha20poly1305-hkdf-v2";
84
+ var HKDF_SALT = new TextEncoder().encode("sgl-e2e-v2-salt");
85
+ var HKDF_INFO_INPUT = new TextEncoder().encode("sgl-e2e-v2-input");
86
+ var HKDF_INFO_OUTPUT = new TextEncoder().encode("sgl-e2e-v2-output");
87
+ function v2Key(shared, info) {
88
+ return (0, import_hkdf.hkdf)(import_sha256.sha256, shared, HKDF_SALT, info, 32);
89
+ }
90
+ function aadInput(nodeB58, ephB58, respB58) {
91
+ return new TextEncoder().encode(`sgl-aad/v2/input|node=${nodeB58}|eph=${ephB58}|resp=${respB58}`);
92
+ }
93
+ function aadOutput(respB58, ephB58) {
94
+ return new TextEncoder().encode(`sgl-aad/v2/output|resp=${respB58}|eph=${ephB58}`);
95
+ }
96
+ function aadStream(respB58, ephB58, nonceB58, seq, isFinal) {
97
+ return new TextEncoder().encode(
98
+ `sgl-aad/v2/stream|resp=${respB58}|eph=${ephB58}|nonce=${nonceB58}|seq=${seq}|final=${isFinal ? 1 : 0}`
99
+ );
100
+ }
101
+ function b58enc(u) {
102
+ return import_bs58.default.encode(u);
103
+ }
104
+ function b58dec(s) {
105
+ return import_bs58.default.decode(s);
106
+ }
107
+ function randomBytes(n) {
108
+ return crypto.getRandomValues(new Uint8Array(n));
109
+ }
110
+ function newResponseKeypair() {
111
+ const secret = import_ed25519.x25519.utils.randomPrivateKey();
112
+ return { secret, pubB58: b58enc(import_ed25519.x25519.getPublicKey(secret)) };
113
+ }
114
+ function randomNonceB58() {
115
+ return b58enc(randomBytes(16));
116
+ }
117
+ function sealInputV2(nodePubB58, respPubB58, plaintext) {
118
+ const nodePub = b58dec(nodePubB58);
119
+ const ephSecret = import_ed25519.x25519.utils.randomPrivateKey();
120
+ const ephPub = import_ed25519.x25519.getPublicKey(ephSecret);
121
+ const ephB58 = b58enc(ephPub);
122
+ const shared = import_ed25519.x25519.getSharedSecret(ephSecret, nodePub);
123
+ const key = v2Key(shared, HKDF_INFO_INPUT);
124
+ const aad = aadInput(nodePubB58, ephB58, respPubB58);
125
+ const nonce = randomBytes(24);
126
+ const ct = (0, import_chacha.xchacha20poly1305)(key, nonce, aad).encrypt(plaintext);
127
+ const out = new Uint8Array(24 + ct.length);
128
+ out.set(nonce, 0);
129
+ out.set(ct, 24);
130
+ return { ciphertext: b58enc(out), ephemeralPub: ephB58 };
131
+ }
132
+ function openOutputV2(respSecret, respPubB58, nodeEphB58, ciphertextB58) {
133
+ const shared = import_ed25519.x25519.getSharedSecret(respSecret, b58dec(nodeEphB58));
134
+ const key = v2Key(shared, HKDF_INFO_OUTPUT);
135
+ const aad = aadOutput(respPubB58, nodeEphB58);
136
+ const blob = b58dec(ciphertextB58);
137
+ return (0, import_chacha.xchacha20poly1305)(key, blob.slice(0, 24), aad).decrypt(blob.slice(24));
138
+ }
139
+ function streamOutKey(respSecret, nodeStreamEphB58) {
140
+ const shared = import_ed25519.x25519.getSharedSecret(respSecret, b58dec(nodeStreamEphB58));
141
+ return v2Key(shared, HKDF_INFO_OUTPUT);
142
+ }
143
+ function openStreamChunk(outKey, respPubB58, streamEphB58, reqNonceB58, seq, isFinal, ctB58) {
144
+ const aad = aadStream(respPubB58, streamEphB58, reqNonceB58, seq, isFinal);
145
+ const blob = b58dec(ctB58);
146
+ return (0, import_chacha.xchacha20poly1305)(outKey, blob.slice(0, 24), aad).decrypt(blob.slice(24));
147
+ }
148
+
67
149
  // src/client.ts
68
150
  var DEFAULT_BASE_URL = "https://grid.x402compute.cc";
69
151
  var DEFAULT_TIMEOUT = 6e4;
@@ -74,6 +156,7 @@ var GridClient = class {
74
156
  this.headers = { Accept: "application/json", "Content-Type": "application/json" };
75
157
  if (options.apiKey) {
76
158
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
159
+ this.headers["X-API-Key"] = options.apiKey;
77
160
  }
78
161
  }
79
162
  async request(method, path, body) {
@@ -132,6 +215,21 @@ var GridClient = class {
132
215
  const data = await this.request("GET", "/grid/pricing");
133
216
  return data.pricing ?? [];
134
217
  }
218
+ /**
219
+ * List the nodes serving a model with each node's effective per-token price
220
+ * (operator's custom price if set, else the platform reference), cheapest
221
+ * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,
222
+ * or omit to let the grid route. Optional `cluster` filter (slug or id).
223
+ */
224
+ async providers(model, options) {
225
+ const params = new URLSearchParams({ model });
226
+ if (options?.cluster) params.set("cluster", options.cluster);
227
+ const data = await this.request(
228
+ "GET",
229
+ `/v1/providers?${params.toString()}`
230
+ );
231
+ return data.providers ?? [];
232
+ }
135
233
  // -- Authenticated endpoints ---------------------------------------------
136
234
  async submitJob(model, input, options) {
137
235
  const body = { model, input };
@@ -145,13 +243,228 @@ var GridClient = class {
145
243
  async getAttestation(jobId) {
146
244
  return this.request("GET", `/grid/jobs/${jobId}/attestation`);
147
245
  }
148
- // -- OpenAI-compatible ---------------------------------------------------
246
+ // -- OpenAI-compatible (end-to-end encrypted) ----------------------------
247
+ /** Reserve a node + learn its X25519 key so we can seal the prompt to it.
248
+ * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,
249
+ * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */
250
+ async reserve(req) {
251
+ const body = { model: req.model };
252
+ if (req.node) body.node = req.node;
253
+ if (req.cluster) body.cluster = req.cluster;
254
+ if (req.pay_in_coin) body.pay_in_coin = req.pay_in_coin;
255
+ const res = await this.request("POST", "/v1/reserve", body);
256
+ if (!res.node_x25519_pubkey) {
257
+ throw new SGLAPIError(503, "Reserved node does not support E2E encryption");
258
+ }
259
+ return res;
260
+ }
261
+ /**
262
+ * End-to-end encrypted chat completion. The prompt is sealed in this client to
263
+ * the serving node's key and only decrypts inside its TEE — the orchestrator
264
+ * only relays ciphertext. Requires an `apiKey` (credits); without one the grid
265
+ * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).
266
+ */
149
267
  async chatCompletions(request) {
150
- return this.request(
151
- "POST",
152
- "/v1/chat/completions",
153
- request
268
+ if (request.stream) {
269
+ let content = "";
270
+ for await (const delta of this.chatCompletionStream(request)) content += delta;
271
+ return {
272
+ id: "",
273
+ object: "chat.completion",
274
+ created: 0,
275
+ model: request.model,
276
+ choices: [{ index: 0, message: { role: "assistant", content }, finish_reason: "stop" }]
277
+ };
278
+ }
279
+ const reservation = await this.reserve(request);
280
+ const { secret, pubB58 } = newResponseKeypair();
281
+ const maxTokens = request.max_tokens ?? 512;
282
+ const sealed = sealInputV2(
283
+ reservation.node_x25519_pubkey,
284
+ pubB58,
285
+ new TextEncoder().encode(JSON.stringify({
286
+ messages: request.messages,
287
+ temperature: request.temperature ?? 0.7,
288
+ max_tokens: maxTokens
289
+ }))
290
+ );
291
+ const body = {
292
+ reservation_token: reservation.reservation_token,
293
+ max_tokens: maxTokens,
294
+ // cleartext, only used to quote the x402 price
295
+ enc: {
296
+ ciphertext: sealed.ciphertext,
297
+ client_ephemeral_pubkey: sealed.ephemeralPub,
298
+ client_response_pubkey: pubB58,
299
+ algorithm: ALGO_V2
300
+ }
301
+ };
302
+ let data;
303
+ try {
304
+ data = await this.request("POST", "/v1/chat/completions", body);
305
+ } catch (err) {
306
+ if (err instanceof SGLAPIError && err.statusCode === 402) {
307
+ throw new SGLAPIError(402, "Payment required \u2014 pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.");
308
+ }
309
+ throw err;
310
+ }
311
+ if (!data.sealed_result) throw new SGLAPIError(500, "No sealed result returned");
312
+ const plain = openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);
313
+ const parsed = JSON.parse(new TextDecoder().decode(plain));
314
+ return {
315
+ id: data.id ?? "",
316
+ object: "chat.completion",
317
+ created: data.created ?? 0,
318
+ model: request.model,
319
+ choices: [{ index: 0, message: { role: "assistant", content: parsed.content ?? "" }, finish_reason: "stop" }],
320
+ usage: data.usage ?? parsed.usage,
321
+ attestation: {
322
+ nodeId: reservation.node_id,
323
+ teeType: reservation.tee_type ?? null,
324
+ verified: !!reservation.attestation_verified
325
+ }
326
+ };
327
+ }
328
+ /**
329
+ * Streaming end-to-end encrypted chat completion. Yields decoded text as it
330
+ * arrives; each chunk is decrypted and its ordering + termination verified (a
331
+ * truncated stream throws). Requires `apiKey` (credits). If the server isn't
332
+ * streaming (toggle off), the whole reply is yielded as a single chunk.
333
+ */
334
+ async *chatCompletionStream(request) {
335
+ const reservation = await this.reserve(request);
336
+ const { secret, pubB58 } = newResponseKeypair();
337
+ const nonce = randomNonceB58();
338
+ const maxTokens = request.max_tokens ?? 512;
339
+ const sealed = sealInputV2(
340
+ reservation.node_x25519_pubkey,
341
+ pubB58,
342
+ new TextEncoder().encode(JSON.stringify({
343
+ messages: request.messages,
344
+ temperature: request.temperature ?? 0.7,
345
+ max_tokens: maxTokens,
346
+ stream: true,
347
+ nonce
348
+ }))
154
349
  );
350
+ const body = {
351
+ reservation_token: reservation.reservation_token,
352
+ stream: true,
353
+ max_tokens: maxTokens,
354
+ enc: {
355
+ ciphertext: sealed.ciphertext,
356
+ client_ephemeral_pubkey: sealed.ephemeralPub,
357
+ client_response_pubkey: pubB58,
358
+ algorithm: ALGO_V2
359
+ }
360
+ };
361
+ const controller = new AbortController();
362
+ const overall = setTimeout(() => controller.abort(), this.timeout);
363
+ let resp;
364
+ try {
365
+ resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
366
+ method: "POST",
367
+ headers: this.headers,
368
+ body: JSON.stringify(body),
369
+ signal: controller.signal
370
+ });
371
+ } catch (err) {
372
+ clearTimeout(overall);
373
+ throw new SGLConnectionError(
374
+ `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`
375
+ );
376
+ }
377
+ if (!resp.ok) {
378
+ clearTimeout(overall);
379
+ if (resp.status === 402) {
380
+ throw new SGLAPIError(402, "Payment required \u2014 pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.");
381
+ }
382
+ let message = resp.statusText;
383
+ try {
384
+ const j = await resp.json();
385
+ if (typeof j.error === "string") message = j.error;
386
+ else if (j.error && typeof j.error === "object" && "message" in j.error) message = String(j.error.message);
387
+ } catch {
388
+ }
389
+ throw new SGLAPIError(resp.status, message);
390
+ }
391
+ const ctype = resp.headers.get("content-type") ?? "";
392
+ if (!ctype.includes("text/event-stream") || !resp.body) {
393
+ clearTimeout(overall);
394
+ const data = await resp.json();
395
+ if (!data.sealed_result) throw new SGLAPIError(500, "No sealed result returned");
396
+ const plain = openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);
397
+ const content = JSON.parse(new TextDecoder().decode(plain)).content ?? "";
398
+ if (content) yield content;
399
+ return;
400
+ }
401
+ const reader = resp.body.getReader();
402
+ const decoder = new TextDecoder();
403
+ const INACTIVITY_MS = 6e4;
404
+ const readChunk = async () => {
405
+ let t;
406
+ const timeout = new Promise((_, reject) => {
407
+ t = setTimeout(() => reject(new SGLConnectionError("stream timed out (no tokens)")), INACTIVITY_MS);
408
+ });
409
+ try {
410
+ return await Promise.race([reader.read(), timeout]);
411
+ } finally {
412
+ if (t) clearTimeout(t);
413
+ }
414
+ };
415
+ let buf = "";
416
+ let expectedSeq = 0;
417
+ let outKey = null;
418
+ let streamEph = null;
419
+ let sawFinal = false;
420
+ try {
421
+ for (; ; ) {
422
+ if (sawFinal) break;
423
+ const { value, done } = await readChunk();
424
+ if (done) break;
425
+ buf += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
426
+ let idx;
427
+ while ((idx = buf.indexOf("\n\n")) !== -1) {
428
+ const raw = buf.slice(0, idx);
429
+ buf = buf.slice(idx + 2);
430
+ if (raw.includes("event: error")) throw new SGLAPIError(502, "stream aborted by server");
431
+ const dataStr = raw.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
432
+ if (!dataStr || dataStr === "[DONE]") continue;
433
+ let chunk;
434
+ try {
435
+ chunk = JSON.parse(dataStr);
436
+ } catch {
437
+ throw new SGLAPIError(502, "malformed stream chunk");
438
+ }
439
+ if (typeof chunk.seq !== "number" || !chunk.ct) {
440
+ throw new SGLAPIError(502, "invalid stream chunk (missing seq/ciphertext)");
441
+ }
442
+ if (chunk.seq !== expectedSeq) throw new SGLAPIError(502, `stream out of order (expected ${expectedSeq}, got ${chunk.seq})`);
443
+ if (chunk.seq === 0) {
444
+ if (!chunk.eph) throw new SGLAPIError(502, "stream chunk 0 missing ephemeral key");
445
+ streamEph = chunk.eph;
446
+ outKey = streamOutKey(secret, streamEph);
447
+ }
448
+ const isFinal = chunk.final === true;
449
+ const text = new TextDecoder().decode(
450
+ openStreamChunk(outKey, pubB58, streamEph, nonce, chunk.seq, isFinal, chunk.ct)
451
+ );
452
+ if (text) yield text;
453
+ expectedSeq++;
454
+ if (isFinal) {
455
+ sawFinal = true;
456
+ break;
457
+ }
458
+ }
459
+ }
460
+ } finally {
461
+ clearTimeout(overall);
462
+ try {
463
+ await reader.cancel();
464
+ } catch {
465
+ }
466
+ }
467
+ if (!sawFinal) throw new SGLAPIError(502, "stream ended before final chunk (truncated)");
155
468
  }
156
469
  // -- Processor helpers ----------------------------------------------------
157
470
  async requestWithWalletAuth(method, path, wallet, body) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export { GridClient, DEFAULT_BASE_URL } from \"./client.js\";\nexport {\n SGLError,\n SGLAPIError,\n SGLAuthError,\n SGLNotFoundError,\n SGLConnectionError,\n} from \"./errors.js\";\nexport type {\n AttestationProof,\n CapacityResponse,\n ChatChoice,\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatMessage,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n JobSubmission,\n ModelInfo,\n ModelPricing,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n TeeCapacity,\n WalletAuth,\n} from \"./types.js\";\n","export class SGLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SGLError\";\n }\n}\n\nexport class SGLAPIError extends SGLError {\n readonly statusCode: number;\n readonly body?: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(`HTTP ${statusCode}: ${message}`);\n this.name = \"SGLAPIError\";\n this.statusCode = statusCode;\n this.body = body;\n }\n}\n\nexport class SGLAuthError extends SGLAPIError {\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(statusCode, message, body);\n this.name = \"SGLAuthError\";\n }\n}\n\nexport class SGLNotFoundError extends SGLAPIError {\n constructor(message: string, body?: Record<string, unknown>) {\n super(404, message, body);\n this.name = \"SGLNotFoundError\";\n }\n}\n\nexport class SGLConnectionError extends SGLError {\n constructor(message: string) {\n super(message);\n this.name = \"SGLConnectionError\";\n }\n}\n","import { SGLAPIError, SGLAuthError, SGLConnectionError, SGLNotFoundError } from \"./errors.js\";\nimport type {\n AttestationProof,\n CapacityResponse,\n ChatCompletionRequest,\n ChatCompletionResponse,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n ModelInfo,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n WalletAuth,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://grid.x402compute.cc\";\n\nconst DEFAULT_TIMEOUT = 60_000;\n\nexport class GridClient {\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n\n constructor(options: GridClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.headers = { Accept: \"application/json\", \"Content-Type\": \"application/json\" };\n if (options.apiKey) {\n this.headers[\"Authorization\"] = `Bearer ${options.apiKey}`;\n }\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n // -- Public endpoints (no auth) ------------------------------------------\n\n async capacity(): Promise<CapacityResponse> {\n return this.request<CapacityResponse>(\"GET\", \"/grid/capacity\");\n }\n\n async models(): Promise<ModelInfo[]> {\n const data = await this.request<{ models: ModelInfo[] }>(\"GET\", \"/grid/models\");\n return data.models ?? [];\n }\n\n async pricing(): Promise<PricingInfo[]> {\n const data = await this.request<{ pricing: PricingInfo[] }>(\"GET\", \"/grid/pricing\");\n return data.pricing ?? [];\n }\n\n // -- Authenticated endpoints ---------------------------------------------\n\n async submitJob(\n model: string,\n input: Record<string, unknown>,\n options?: { submitterWallet?: string; submitterChain?: string },\n ): Promise<JobResponse> {\n const body: Record<string, unknown> = { model, input };\n if (options?.submitterWallet) body.submitter_wallet = options.submitterWallet;\n if (options?.submitterChain) body.submitter_chain = options.submitterChain;\n return this.request<JobResponse>(\"POST\", \"/grid/jobs\", body);\n }\n\n async getJob(jobId: string): Promise<JobResult> {\n return this.request<JobResult>(\"GET\", `/grid/jobs/${jobId}`);\n }\n\n async getAttestation(jobId: string): Promise<AttestationProof> {\n return this.request<AttestationProof>(\"GET\", `/grid/jobs/${jobId}/attestation`);\n }\n\n // -- OpenAI-compatible ---------------------------------------------------\n\n async chatCompletions(\n request: ChatCompletionRequest,\n ): Promise<ChatCompletionResponse> {\n return this.request<ChatCompletionResponse>(\n \"POST\",\n \"/v1/chat/completions\",\n request,\n );\n }\n\n // -- Processor helpers ----------------------------------------------------\n\n private async requestWithWalletAuth<T>(\n method: string,\n path: string,\n wallet: WalletAuth,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = {\n ...this.headers,\n \"X-Auth-Address\": wallet.address,\n \"X-Auth-Chain\": wallet.chain ?? \"solana\",\n \"X-Auth-Signature\": wallet.signature,\n \"X-Auth-Timestamp\": wallet.timestamp,\n \"X-Auth-Nonce\": wallet.nonce,\n };\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n private async requestWithPayment<T>(\n method: string,\n path: string,\n body?: unknown,\n paymentHeader?: string,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = { ...this.headers };\n if (paymentHeader) {\n reqHeaders[\"X-Payment\"] = paymentHeader;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (response.status === 402) {\n const requirements = (await response.json()) as Record<string, unknown>;\n throw new SGLAPIError(402, \"Payment required\", requirements);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n } catch {\n /* body not JSON */\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n return (await response.json()) as T;\n }\n\n // -- Processors -----------------------------------------------------------\n\n async deployProcessor(\n wallet: WalletAuth,\n options: DeployProcessorOptions,\n ): Promise<ProcessorDeployResult> {\n return this.requestWithWalletAuth<ProcessorDeployResult>(\n \"POST\",\n \"/grid/processors\",\n wallet,\n options,\n );\n }\n\n async invokeProcessor(\n processorName: string,\n input: Record<string, unknown>,\n options?: { paymentHeader?: string; paymentToken?: \"USDC\" | \"SGL\" },\n ): Promise<ProcessorInvokeResult> {\n const body: Record<string, unknown> = { input };\n if (options?.paymentToken) body.payment_token = options.paymentToken;\n return this.requestWithPayment<ProcessorInvokeResult>(\n \"POST\",\n `/grid/processors/${encodeURIComponent(processorName)}/invoke`,\n body,\n options?.paymentHeader,\n );\n }\n\n async listProcessors(options?: {\n owner?: string;\n page?: number;\n limit?: number;\n }): Promise<ProcessorListResponse> {\n const params = new URLSearchParams();\n if (options?.owner) params.set(\"owner\", options.owner);\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<ProcessorListResponse>(\n \"GET\",\n `/grid/processors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async getProcessor(processorId: string): Promise<ProcessorInfo> {\n return this.request<ProcessorInfo>(\"GET\", `/grid/processors/${processorId}`);\n }\n\n async deleteProcessor(\n processorId: string,\n wallet: WalletAuth,\n ): Promise<{ deleted: boolean; id: string }> {\n return this.requestWithWalletAuth<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/grid/processors/${processorId}`,\n wallet,\n );\n }\n\n async getProcessorLogs(\n processorId: string,\n wallet: WalletAuth,\n options?: { page?: number; limit?: number },\n ): Promise<ProcessorLogsResponse> {\n const params = new URLSearchParams();\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.requestWithWalletAuth<ProcessorLogsResponse>(\n \"GET\",\n `/grid/processors/${processorId}/logs${qs ? `?${qs}` : \"\"}`,\n wallet,\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/e2e.ts","../src/client.ts"],"sourcesContent":["export { GridClient, DEFAULT_BASE_URL } from \"./client.js\";\nexport {\n SGLError,\n SGLAPIError,\n SGLAuthError,\n SGLNotFoundError,\n SGLConnectionError,\n} from \"./errors.js\";\nexport type {\n Attestation,\n AttestationProof,\n CapacityResponse,\n ChatChoice,\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatMessage,\n ReserveResponse,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n JobSubmission,\n ModelInfo,\n ModelPricing,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n TeeCapacity,\n WalletAuth,\n} from \"./types.js\";\n","export class SGLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SGLError\";\n }\n}\n\nexport class SGLAPIError extends SGLError {\n readonly statusCode: number;\n readonly body?: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(`HTTP ${statusCode}: ${message}`);\n this.name = \"SGLAPIError\";\n this.statusCode = statusCode;\n this.body = body;\n }\n}\n\nexport class SGLAuthError extends SGLAPIError {\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(statusCode, message, body);\n this.name = \"SGLAuthError\";\n }\n}\n\nexport class SGLNotFoundError extends SGLAPIError {\n constructor(message: string, body?: Record<string, unknown>) {\n super(404, message, body);\n this.name = \"SGLNotFoundError\";\n }\n}\n\nexport class SGLConnectionError extends SGLError {\n constructor(message: string) {\n super(message);\n this.name = \"SGLConnectionError\";\n }\n}\n","/**\n * End-to-end encryption for the SGL grid (client side).\n *\n * Must match sgl-node/src/encryption.rs, the orchestrator, and the browser/Python\n * clients byte-for-byte: X25519 ECDH -> HKDF-SHA256 -> XChaCha20-Poly1305 (24-byte\n * nonce), AAD-bound. Sealed blob layout: nonce(24) || ciphertext, base58.\n *\n * The orchestrator only ever relays ciphertext — it never sees the prompt or reply.\n */\n\nimport { x25519 } from \"@noble/curves/ed25519\";\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { hkdf } from \"@noble/hashes/hkdf\";\nimport bs58 from \"bs58\";\n\nexport const ALGO_V2 = \"x25519-xchacha20poly1305-hkdf-v2\";\nexport const ALGO_V2_STREAM = \"x25519-xchacha20poly1305-hkdf-v2-stream\";\n\nconst HKDF_SALT = new TextEncoder().encode(\"sgl-e2e-v2-salt\");\nconst HKDF_INFO_INPUT = new TextEncoder().encode(\"sgl-e2e-v2-input\");\nconst HKDF_INFO_OUTPUT = new TextEncoder().encode(\"sgl-e2e-v2-output\");\n\nfunction v2Key(shared: Uint8Array, info: Uint8Array): Uint8Array {\n return hkdf(sha256, shared, HKDF_SALT, info, 32);\n}\nfunction aadInput(nodeB58: string, ephB58: string, respB58: string): Uint8Array {\n return new TextEncoder().encode(`sgl-aad/v2/input|node=${nodeB58}|eph=${ephB58}|resp=${respB58}`);\n}\nfunction aadOutput(respB58: string, ephB58: string): Uint8Array {\n return new TextEncoder().encode(`sgl-aad/v2/output|resp=${respB58}|eph=${ephB58}`);\n}\nfunction aadStream(respB58: string, ephB58: string, nonceB58: string, seq: number, isFinal: boolean): Uint8Array {\n return new TextEncoder().encode(\n `sgl-aad/v2/stream|resp=${respB58}|eph=${ephB58}|nonce=${nonceB58}|seq=${seq}|final=${isFinal ? 1 : 0}`,\n );\n}\n\nfunction b58enc(u: Uint8Array): string {\n return bs58.encode(u);\n}\nfunction b58dec(s: string): Uint8Array {\n return bs58.decode(s);\n}\nfunction randomBytes(n: number): Uint8Array {\n return crypto.getRandomValues(new Uint8Array(n));\n}\n\nexport interface ResponseKeypair {\n secret: Uint8Array;\n pubB58: string;\n}\n\n/** The caller's response keypair — the node seals its reply to this. */\nexport function newResponseKeypair(): ResponseKeypair {\n const secret = x25519.utils.randomPrivateKey();\n return { secret, pubB58: b58enc(x25519.getPublicKey(secret)) };\n}\n\n/** A per-request nonce bound into every stream chunk's AAD. */\nexport function randomNonceB58(): string {\n return b58enc(randomBytes(16));\n}\n\n/** Seal the prompt to the node's X25519 key. */\nexport function sealInputV2(\n nodePubB58: string,\n respPubB58: string,\n plaintext: Uint8Array,\n): { ciphertext: string; ephemeralPub: string } {\n const nodePub = b58dec(nodePubB58);\n const ephSecret = x25519.utils.randomPrivateKey();\n const ephPub = x25519.getPublicKey(ephSecret);\n const ephB58 = b58enc(ephPub);\n const shared = x25519.getSharedSecret(ephSecret, nodePub);\n const key = v2Key(shared, HKDF_INFO_INPUT);\n const aad = aadInput(nodePubB58, ephB58, respPubB58);\n const nonce = randomBytes(24);\n const ct = xchacha20poly1305(key, nonce, aad).encrypt(plaintext);\n const out = new Uint8Array(24 + ct.length);\n out.set(nonce, 0);\n out.set(ct, 24);\n return { ciphertext: b58enc(out), ephemeralPub: ephB58 };\n}\n\n/** Open the node's (non-stream) reply sealed to our response key. */\nexport function openOutputV2(\n respSecret: Uint8Array,\n respPubB58: string,\n nodeEphB58: string,\n ciphertextB58: string,\n): Uint8Array {\n const shared = x25519.getSharedSecret(respSecret, b58dec(nodeEphB58));\n const key = v2Key(shared, HKDF_INFO_OUTPUT);\n const aad = aadOutput(respPubB58, nodeEphB58);\n const blob = b58dec(ciphertextB58);\n return xchacha20poly1305(key, blob.slice(0, 24), aad).decrypt(blob.slice(24));\n}\n\n/** Derive the stream output key once from the node's stream ephemeral (chunk 0). */\nexport function streamOutKey(respSecret: Uint8Array, nodeStreamEphB58: string): Uint8Array {\n const shared = x25519.getSharedSecret(respSecret, b58dec(nodeStreamEphB58));\n return v2Key(shared, HKDF_INFO_OUTPUT);\n}\n\n/** Open one stream chunk with the precomputed key + nonce/seq/final-bound AAD. */\nexport function openStreamChunk(\n outKey: Uint8Array,\n respPubB58: string,\n streamEphB58: string,\n reqNonceB58: string,\n seq: number,\n isFinal: boolean,\n ctB58: string,\n): Uint8Array {\n const aad = aadStream(respPubB58, streamEphB58, reqNonceB58, seq, isFinal);\n const blob = b58dec(ctB58);\n return xchacha20poly1305(outKey, blob.slice(0, 24), aad).decrypt(blob.slice(24));\n}\n","import { SGLAPIError, SGLAuthError, SGLConnectionError, SGLNotFoundError } from \"./errors.js\";\nimport * as e2e from \"./e2e.js\";\nimport type {\n AttestationProof,\n CapacityResponse,\n ChatCompletionRequest,\n ChatCompletionResponse,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n ModelInfo,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n ProviderInfo,\n ProvidersResponse,\n ReserveResponse,\n WalletAuth,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://grid.x402compute.cc\";\n\nconst DEFAULT_TIMEOUT = 60_000;\n\nexport class GridClient {\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n\n constructor(options: GridClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.headers = { Accept: \"application/json\", \"Content-Type\": \"application/json\" };\n if (options.apiKey) {\n this.headers[\"Authorization\"] = `Bearer ${options.apiKey}`;\n // Grid credit billing reads X-API-Key; send both so reserve + chat resolve\n // the paying wallet (credits mode) rather than falling back to anonymous x402.\n this.headers[\"X-API-Key\"] = options.apiKey;\n }\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n // -- Public endpoints (no auth) ------------------------------------------\n\n async capacity(): Promise<CapacityResponse> {\n return this.request<CapacityResponse>(\"GET\", \"/grid/capacity\");\n }\n\n async models(): Promise<ModelInfo[]> {\n const data = await this.request<{ models: ModelInfo[] }>(\"GET\", \"/grid/models\");\n return data.models ?? [];\n }\n\n async pricing(): Promise<PricingInfo[]> {\n const data = await this.request<{ pricing: PricingInfo[] }>(\"GET\", \"/grid/pricing\");\n return data.pricing ?? [];\n }\n\n /**\n * List the nodes serving a model with each node's effective per-token price\n * (operator's custom price if set, else the platform reference), cheapest\n * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,\n * or omit to let the grid route. Optional `cluster` filter (slug or id).\n */\n async providers(\n model: string,\n options?: { cluster?: string },\n ): Promise<ProviderInfo[]> {\n const params = new URLSearchParams({ model });\n if (options?.cluster) params.set(\"cluster\", options.cluster);\n const data = await this.request<ProvidersResponse>(\n \"GET\",\n `/v1/providers?${params.toString()}`,\n );\n return data.providers ?? [];\n }\n\n // -- Authenticated endpoints ---------------------------------------------\n\n async submitJob(\n model: string,\n input: Record<string, unknown>,\n options?: { submitterWallet?: string; submitterChain?: string },\n ): Promise<JobResponse> {\n const body: Record<string, unknown> = { model, input };\n if (options?.submitterWallet) body.submitter_wallet = options.submitterWallet;\n if (options?.submitterChain) body.submitter_chain = options.submitterChain;\n return this.request<JobResponse>(\"POST\", \"/grid/jobs\", body);\n }\n\n async getJob(jobId: string): Promise<JobResult> {\n return this.request<JobResult>(\"GET\", `/grid/jobs/${jobId}`);\n }\n\n async getAttestation(jobId: string): Promise<AttestationProof> {\n return this.request<AttestationProof>(\"GET\", `/grid/jobs/${jobId}/attestation`);\n }\n\n // -- OpenAI-compatible (end-to-end encrypted) ----------------------------\n\n /** Reserve a node + learn its X25519 key so we can seal the prompt to it.\n * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,\n * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */\n private async reserve(req: {\n model: string;\n node?: string;\n cluster?: string;\n pay_in_coin?: boolean;\n }): Promise<ReserveResponse> {\n const body: Record<string, unknown> = { model: req.model };\n if (req.node) body.node = req.node;\n if (req.cluster) body.cluster = req.cluster;\n if (req.pay_in_coin) body.pay_in_coin = req.pay_in_coin;\n const res = await this.request<ReserveResponse>(\"POST\", \"/v1/reserve\", body);\n if (!res.node_x25519_pubkey) {\n throw new SGLAPIError(503, \"Reserved node does not support E2E encryption\");\n }\n return res;\n }\n\n /**\n * End-to-end encrypted chat completion. The prompt is sealed in this client to\n * the serving node's key and only decrypts inside its TEE — the orchestrator\n * only relays ciphertext. Requires an `apiKey` (credits); without one the grid\n * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).\n */\n async chatCompletions(\n request: ChatCompletionRequest,\n ): Promise<ChatCompletionResponse> {\n if (request.stream) {\n // Collapse the stream into a single response for the non-streaming API.\n let content = \"\";\n for await (const delta of this.chatCompletionStream(request)) content += delta;\n return {\n id: \"\", object: \"chat.completion\", created: 0, model: request.model,\n choices: [{ index: 0, message: { role: \"assistant\", content }, finish_reason: \"stop\" }],\n };\n }\n\n const reservation = await this.reserve(request);\n const { secret, pubB58 } = e2e.newResponseKeypair();\n const maxTokens = request.max_tokens ?? 512;\n const sealed = e2e.sealInputV2(\n reservation.node_x25519_pubkey,\n pubB58,\n new TextEncoder().encode(JSON.stringify({\n messages: request.messages,\n temperature: request.temperature ?? 0.7,\n max_tokens: maxTokens,\n })),\n );\n const body = {\n reservation_token: reservation.reservation_token,\n max_tokens: maxTokens, // cleartext, only used to quote the x402 price\n enc: {\n ciphertext: sealed.ciphertext,\n client_ephemeral_pubkey: sealed.ephemeralPub,\n client_response_pubkey: pubB58,\n algorithm: e2e.ALGO_V2,\n },\n };\n\n let data: {\n id?: string; created?: number; sealed_result?: { ephemeral_public_key: string; ciphertext: string };\n usage?: ChatCompletionResponse[\"usage\"];\n };\n try {\n data = await this.request(\"POST\", \"/v1/chat/completions\", body);\n } catch (err) {\n if (err instanceof SGLAPIError && err.statusCode === 402) {\n throw new SGLAPIError(402, \"Payment required — pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.\");\n }\n throw err;\n }\n\n if (!data.sealed_result) throw new SGLAPIError(500, \"No sealed result returned\");\n const plain = e2e.openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);\n const parsed = JSON.parse(new TextDecoder().decode(plain)) as { content?: string; usage?: ChatCompletionResponse[\"usage\"] };\n\n return {\n id: data.id ?? \"\",\n object: \"chat.completion\",\n created: data.created ?? 0,\n model: request.model,\n choices: [{ index: 0, message: { role: \"assistant\", content: parsed.content ?? \"\" }, finish_reason: \"stop\" }],\n usage: data.usage ?? parsed.usage,\n attestation: {\n nodeId: reservation.node_id,\n teeType: reservation.tee_type ?? null,\n verified: !!reservation.attestation_verified,\n },\n };\n }\n\n /**\n * Streaming end-to-end encrypted chat completion. Yields decoded text as it\n * arrives; each chunk is decrypted and its ordering + termination verified (a\n * truncated stream throws). Requires `apiKey` (credits). If the server isn't\n * streaming (toggle off), the whole reply is yielded as a single chunk.\n */\n async *chatCompletionStream(\n request: ChatCompletionRequest,\n ): AsyncGenerator<string, void, unknown> {\n const reservation = await this.reserve(request);\n const { secret, pubB58 } = e2e.newResponseKeypair();\n const nonce = e2e.randomNonceB58();\n const maxTokens = request.max_tokens ?? 512;\n const sealed = e2e.sealInputV2(\n reservation.node_x25519_pubkey,\n pubB58,\n new TextEncoder().encode(JSON.stringify({\n messages: request.messages,\n temperature: request.temperature ?? 0.7,\n max_tokens: maxTokens,\n stream: true,\n nonce,\n })),\n );\n const body = {\n reservation_token: reservation.reservation_token,\n stream: true,\n max_tokens: maxTokens,\n enc: {\n ciphertext: sealed.ciphertext,\n client_ephemeral_pubkey: sealed.ephemeralPub,\n client_response_pubkey: pubB58,\n algorithm: e2e.ALGO_V2,\n },\n };\n\n const controller = new AbortController();\n const overall = setTimeout(() => controller.abort(), this.timeout);\n let resp: Response;\n try {\n resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(overall);\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (!resp.ok) {\n clearTimeout(overall);\n if (resp.status === 402) {\n throw new SGLAPIError(402, \"Payment required — pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.\");\n }\n let message = resp.statusText;\n try {\n const j = (await resp.json()) as { error?: unknown };\n if (typeof j.error === \"string\") message = j.error;\n else if (j.error && typeof j.error === \"object\" && \"message\" in j.error) message = String((j.error as { message: unknown }).message);\n } catch { /* ignore */ }\n throw new SGLAPIError(resp.status, message);\n }\n\n const ctype = resp.headers.get(\"content-type\") ?? \"\";\n if (!ctype.includes(\"text/event-stream\") || !resp.body) {\n clearTimeout(overall);\n const data = (await resp.json()) as { sealed_result?: { ephemeral_public_key: string; ciphertext: string } };\n if (!data.sealed_result) throw new SGLAPIError(500, \"No sealed result returned\");\n const plain = e2e.openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);\n const content = (JSON.parse(new TextDecoder().decode(plain)) as { content?: string }).content ?? \"\";\n if (content) yield content;\n return;\n }\n\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n const INACTIVITY_MS = 60_000;\n const readChunk = async (): Promise<ReadableStreamReadResult<Uint8Array>> => {\n let t: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n t = setTimeout(() => reject(new SGLConnectionError(\"stream timed out (no tokens)\")), INACTIVITY_MS);\n });\n try {\n return (await Promise.race([reader.read(), timeout])) as ReadableStreamReadResult<Uint8Array>;\n } finally {\n if (t) clearTimeout(t);\n }\n };\n\n let buf = \"\";\n let expectedSeq = 0;\n let outKey: Uint8Array | null = null;\n let streamEph: string | null = null;\n let sawFinal = false;\n try {\n for (;;) {\n if (sawFinal) break;\n const { value, done } = await readChunk();\n if (done) break;\n // Normalize CRLF so \\n\\n event framing works regardless of line endings.\n buf += decoder.decode(value, { stream: true }).replace(/\\r\\n/g, \"\\n\");\n let idx: number;\n while ((idx = buf.indexOf(\"\\n\\n\")) !== -1) {\n const raw = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n if (raw.includes(\"event: error\")) throw new SGLAPIError(502, \"stream aborted by server\");\n const dataStr = raw.split(\"\\n\").filter((l) => l.startsWith(\"data:\")).map((l) => l.slice(5).trim()).join(\"\\n\");\n if (!dataStr || dataStr === \"[DONE]\") continue;\n // Fail closed: a malformed or non-chunk data event is a protocol error.\n let chunk: { seq?: number; final?: boolean; eph?: string; ct?: string };\n try {\n chunk = JSON.parse(dataStr);\n } catch {\n throw new SGLAPIError(502, \"malformed stream chunk\");\n }\n if (typeof chunk.seq !== \"number\" || !chunk.ct) {\n throw new SGLAPIError(502, \"invalid stream chunk (missing seq/ciphertext)\");\n }\n if (chunk.seq !== expectedSeq) throw new SGLAPIError(502, `stream out of order (expected ${expectedSeq}, got ${chunk.seq})`);\n if (chunk.seq === 0) {\n if (!chunk.eph) throw new SGLAPIError(502, \"stream chunk 0 missing ephemeral key\");\n streamEph = chunk.eph;\n outKey = e2e.streamOutKey(secret, streamEph);\n }\n const isFinal = chunk.final === true;\n const text = new TextDecoder().decode(\n e2e.openStreamChunk(outKey as Uint8Array, pubB58, streamEph as string, nonce, chunk.seq, isFinal, chunk.ct),\n );\n if (text) yield text;\n expectedSeq++;\n if (isFinal) { sawFinal = true; break; }\n }\n }\n } finally {\n clearTimeout(overall);\n try { await reader.cancel(); } catch { /* ignore */ }\n }\n if (!sawFinal) throw new SGLAPIError(502, \"stream ended before final chunk (truncated)\");\n }\n\n // -- Processor helpers ----------------------------------------------------\n\n private async requestWithWalletAuth<T>(\n method: string,\n path: string,\n wallet: WalletAuth,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = {\n ...this.headers,\n \"X-Auth-Address\": wallet.address,\n \"X-Auth-Chain\": wallet.chain ?? \"solana\",\n \"X-Auth-Signature\": wallet.signature,\n \"X-Auth-Timestamp\": wallet.timestamp,\n \"X-Auth-Nonce\": wallet.nonce,\n };\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n private async requestWithPayment<T>(\n method: string,\n path: string,\n body?: unknown,\n paymentHeader?: string,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = { ...this.headers };\n if (paymentHeader) {\n reqHeaders[\"X-Payment\"] = paymentHeader;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (response.status === 402) {\n const requirements = (await response.json()) as Record<string, unknown>;\n throw new SGLAPIError(402, \"Payment required\", requirements);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n } catch {\n /* body not JSON */\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n return (await response.json()) as T;\n }\n\n // -- Processors -----------------------------------------------------------\n\n async deployProcessor(\n wallet: WalletAuth,\n options: DeployProcessorOptions,\n ): Promise<ProcessorDeployResult> {\n return this.requestWithWalletAuth<ProcessorDeployResult>(\n \"POST\",\n \"/grid/processors\",\n wallet,\n options,\n );\n }\n\n async invokeProcessor(\n processorName: string,\n input: Record<string, unknown>,\n options?: { paymentHeader?: string; paymentToken?: \"USDC\" | \"SGL\" },\n ): Promise<ProcessorInvokeResult> {\n const body: Record<string, unknown> = { input };\n if (options?.paymentToken) body.payment_token = options.paymentToken;\n return this.requestWithPayment<ProcessorInvokeResult>(\n \"POST\",\n `/grid/processors/${encodeURIComponent(processorName)}/invoke`,\n body,\n options?.paymentHeader,\n );\n }\n\n async listProcessors(options?: {\n owner?: string;\n page?: number;\n limit?: number;\n }): Promise<ProcessorListResponse> {\n const params = new URLSearchParams();\n if (options?.owner) params.set(\"owner\", options.owner);\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<ProcessorListResponse>(\n \"GET\",\n `/grid/processors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async getProcessor(processorId: string): Promise<ProcessorInfo> {\n return this.request<ProcessorInfo>(\"GET\", `/grid/processors/${processorId}`);\n }\n\n async deleteProcessor(\n processorId: string,\n wallet: WalletAuth,\n ): Promise<{ deleted: boolean; id: string }> {\n return this.requestWithWalletAuth<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/grid/processors/${processorId}`,\n wallet,\n );\n }\n\n async getProcessorLogs(\n processorId: string,\n wallet: WalletAuth,\n options?: { page?: number; limit?: number },\n ): Promise<ProcessorLogsResponse> {\n const params = new URLSearchParams();\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.requestWithWalletAuth<ProcessorLogsResponse>(\n \"GET\",\n `/grid/processors/${processorId}/logs${qs ? `?${qs}` : \"\"}`,\n wallet,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,cAAN,cAA0B,SAAS;AAAA,EAIxC,YACE,YACA,SACA,MACA;AACA,UAAM,QAAQ,UAAU,KAAK,OAAO,EAAE;AACtC,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,YAAY;AAAA,EAC5C,YACE,YACA,SACA,MACA;AACA,UAAM,YAAY,SAAS,IAAI;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,mBAAN,cAA+B,YAAY;AAAA,EAChD,YAAY,SAAiB,MAAgC;AAC3D,UAAM,KAAK,SAAS,IAAI;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,SAAS;AAAA,EAC/C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACpCA,qBAAuB;AACvB,oBAAkC;AAClC,oBAAuB;AACvB,kBAAqB;AACrB,kBAAiB;AAEV,IAAM,UAAU;AAGvB,IAAM,YAAY,IAAI,YAAY,EAAE,OAAO,iBAAiB;AAC5D,IAAM,kBAAkB,IAAI,YAAY,EAAE,OAAO,kBAAkB;AACnE,IAAM,mBAAmB,IAAI,YAAY,EAAE,OAAO,mBAAmB;AAErE,SAAS,MAAM,QAAoB,MAA8B;AAC/D,aAAO,kBAAK,sBAAQ,QAAQ,WAAW,MAAM,EAAE;AACjD;AACA,SAAS,SAAS,SAAiB,QAAgB,SAA6B;AAC9E,SAAO,IAAI,YAAY,EAAE,OAAO,yBAAyB,OAAO,QAAQ,MAAM,SAAS,OAAO,EAAE;AAClG;AACA,SAAS,UAAU,SAAiB,QAA4B;AAC9D,SAAO,IAAI,YAAY,EAAE,OAAO,0BAA0B,OAAO,QAAQ,MAAM,EAAE;AACnF;AACA,SAAS,UAAU,SAAiB,QAAgB,UAAkB,KAAa,SAA8B;AAC/G,SAAO,IAAI,YAAY,EAAE;AAAA,IACvB,0BAA0B,OAAO,QAAQ,MAAM,UAAU,QAAQ,QAAQ,GAAG,UAAU,UAAU,IAAI,CAAC;AAAA,EACvG;AACF;AAEA,SAAS,OAAO,GAAuB;AACrC,SAAO,YAAAA,QAAK,OAAO,CAAC;AACtB;AACA,SAAS,OAAO,GAAuB;AACrC,SAAO,YAAAA,QAAK,OAAO,CAAC;AACtB;AACA,SAAS,YAAY,GAAuB;AAC1C,SAAO,OAAO,gBAAgB,IAAI,WAAW,CAAC,CAAC;AACjD;AAQO,SAAS,qBAAsC;AACpD,QAAM,SAAS,sBAAO,MAAM,iBAAiB;AAC7C,SAAO,EAAE,QAAQ,QAAQ,OAAO,sBAAO,aAAa,MAAM,CAAC,EAAE;AAC/D;AAGO,SAAS,iBAAyB;AACvC,SAAO,OAAO,YAAY,EAAE,CAAC;AAC/B;AAGO,SAAS,YACd,YACA,YACA,WAC8C;AAC9C,QAAM,UAAU,OAAO,UAAU;AACjC,QAAM,YAAY,sBAAO,MAAM,iBAAiB;AAChD,QAAM,SAAS,sBAAO,aAAa,SAAS;AAC5C,QAAM,SAAS,OAAO,MAAM;AAC5B,QAAM,SAAS,sBAAO,gBAAgB,WAAW,OAAO;AACxD,QAAM,MAAM,MAAM,QAAQ,eAAe;AACzC,QAAM,MAAM,SAAS,YAAY,QAAQ,UAAU;AACnD,QAAM,QAAQ,YAAY,EAAE;AAC5B,QAAM,SAAK,iCAAkB,KAAK,OAAO,GAAG,EAAE,QAAQ,SAAS;AAC/D,QAAM,MAAM,IAAI,WAAW,KAAK,GAAG,MAAM;AACzC,MAAI,IAAI,OAAO,CAAC;AAChB,MAAI,IAAI,IAAI,EAAE;AACd,SAAO,EAAE,YAAY,OAAO,GAAG,GAAG,cAAc,OAAO;AACzD;AAGO,SAAS,aACd,YACA,YACA,YACA,eACY;AACZ,QAAM,SAAS,sBAAO,gBAAgB,YAAY,OAAO,UAAU,CAAC;AACpE,QAAM,MAAM,MAAM,QAAQ,gBAAgB;AAC1C,QAAM,MAAM,UAAU,YAAY,UAAU;AAC5C,QAAM,OAAO,OAAO,aAAa;AACjC,aAAO,iCAAkB,KAAK,KAAK,MAAM,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC;AAC9E;AAGO,SAAS,aAAa,YAAwB,kBAAsC;AACzF,QAAM,SAAS,sBAAO,gBAAgB,YAAY,OAAO,gBAAgB,CAAC;AAC1E,SAAO,MAAM,QAAQ,gBAAgB;AACvC;AAGO,SAAS,gBACd,QACA,YACA,cACA,aACA,KACA,SACA,OACY;AACZ,QAAM,MAAM,UAAU,YAAY,cAAc,aAAa,KAAK,OAAO;AACzE,QAAM,OAAO,OAAO,KAAK;AACzB,aAAO,iCAAkB,QAAQ,KAAK,MAAM,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC;AACjF;;;AC7FO,IAAM,mBAAmB;AAEhC,IAAM,kBAAkB;AAEjB,IAAM,aAAN,MAAiB;AAAA,EAKtB,YAAY,UAA6B,CAAC,GAAG;AAC3C,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACvE,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,UAAU,EAAE,QAAQ,oBAAoB,gBAAgB,mBAAmB;AAChF,QAAI,QAAQ,QAAQ;AAClB,WAAK,QAAQ,eAAe,IAAI,UAAU,QAAQ,MAAM;AAGxD,WAAK,QAAQ,WAAW,IAAI,QAAQ;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS,KAAK;AAAA,QACd,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,iBAC9B,OAAO,OAAO,QAAQ,YAAY,aAAa;AACtD,oBAAU,OAAQ,IAA6B,OAAO;AAAA,MAC1D,QAAQ;AAAA,MAER;AAEA,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,MAC5D;AACA,UAAI,SAAS,WAAW,KAAK;AAC3B,cAAM,IAAI,iBAAiB,SAAS,SAAS;AAAA,MAC/C;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,QAAI,SAAS,WAAW,IAAK,QAAO,CAAC;AACrC,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA,EAIA,MAAM,WAAsC;AAC1C,WAAO,KAAK,QAA0B,OAAO,gBAAgB;AAAA,EAC/D;AAAA,EAEA,MAAM,SAA+B;AACnC,UAAM,OAAO,MAAM,KAAK,QAAiC,OAAO,cAAc;AAC9E,WAAO,KAAK,UAAU,CAAC;AAAA,EACzB;AAAA,EAEA,MAAM,UAAkC;AACtC,UAAM,OAAO,MAAM,KAAK,QAAoC,OAAO,eAAe;AAClF,WAAO,KAAK,WAAW,CAAC;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UACJ,OACA,SACyB;AACzB,UAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,CAAC;AAC5C,QAAI,SAAS,QAAS,QAAO,IAAI,WAAW,QAAQ,OAAO;AAC3D,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,iBAAiB,OAAO,SAAS,CAAC;AAAA,IACpC;AACA,WAAO,KAAK,aAAa,CAAC;AAAA,EAC5B;AAAA;AAAA,EAIA,MAAM,UACJ,OACA,OACA,SACsB;AACtB,UAAM,OAAgC,EAAE,OAAO,MAAM;AACrD,QAAI,SAAS,gBAAiB,MAAK,mBAAmB,QAAQ;AAC9D,QAAI,SAAS,eAAgB,MAAK,kBAAkB,QAAQ;AAC5D,WAAO,KAAK,QAAqB,QAAQ,cAAc,IAAI;AAAA,EAC7D;AAAA,EAEA,MAAM,OAAO,OAAmC;AAC9C,WAAO,KAAK,QAAmB,OAAO,cAAc,KAAK,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,eAAe,OAA0C;AAC7D,WAAO,KAAK,QAA0B,OAAO,cAAc,KAAK,cAAc;AAAA,EAChF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,QAAQ,KAKO;AAC3B,UAAM,OAAgC,EAAE,OAAO,IAAI,MAAM;AACzD,QAAI,IAAI,KAAM,MAAK,OAAO,IAAI;AAC9B,QAAI,IAAI,QAAS,MAAK,UAAU,IAAI;AACpC,QAAI,IAAI,YAAa,MAAK,cAAc,IAAI;AAC5C,UAAM,MAAM,MAAM,KAAK,QAAyB,QAAQ,eAAe,IAAI;AAC3E,QAAI,CAAC,IAAI,oBAAoB;AAC3B,YAAM,IAAI,YAAY,KAAK,+CAA+C;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBACJ,SACiC;AACjC,QAAI,QAAQ,QAAQ;AAElB,UAAI,UAAU;AACd,uBAAiB,SAAS,KAAK,qBAAqB,OAAO,EAAG,YAAW;AACzE,aAAO;AAAA,QACL,IAAI;AAAA,QAAI,QAAQ;AAAA,QAAmB,SAAS;AAAA,QAAG,OAAO,QAAQ;AAAA,QAC9D,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,aAAa,QAAQ,GAAG,eAAe,OAAO,CAAC;AAAA,MACxF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,KAAK,QAAQ,OAAO;AAC9C,UAAM,EAAE,QAAQ,OAAO,IAAQ,mBAAmB;AAClD,UAAM,YAAY,QAAQ,cAAc;AACxC,UAAM,SAAa;AAAA,MACjB,YAAY;AAAA,MACZ;AAAA,MACA,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU;AAAA,QACtC,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ,eAAe;AAAA,QACpC,YAAY;AAAA,MACd,CAAC,CAAC;AAAA,IACJ;AACA,UAAM,OAAO;AAAA,MACX,mBAAmB,YAAY;AAAA,MAC/B,YAAY;AAAA;AAAA,MACZ,KAAK;AAAA,QACH,YAAY,OAAO;AAAA,QACnB,yBAAyB,OAAO;AAAA,QAChC,wBAAwB;AAAA,QACxB,WAAe;AAAA,MACjB;AAAA,IACF;AAEA,QAAI;AAIJ,QAAI;AACF,aAAO,MAAM,KAAK,QAAQ,QAAQ,wBAAwB,IAAI;AAAA,IAChE,SAAS,KAAK;AACZ,UAAI,eAAe,eAAe,IAAI,eAAe,KAAK;AACxD,cAAM,IAAI,YAAY,KAAK,yIAAoI;AAAA,MACjK;AACA,YAAM;AAAA,IACR;AAEA,QAAI,CAAC,KAAK,cAAe,OAAM,IAAI,YAAY,KAAK,2BAA2B;AAC/E,UAAM,QAAY,aAAa,QAAQ,QAAQ,KAAK,cAAc,sBAAsB,KAAK,cAAc,UAAU;AACrH,UAAM,SAAS,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAEzD,WAAO;AAAA,MACL,IAAI,KAAK,MAAM;AAAA,MACf,QAAQ;AAAA,MACR,SAAS,KAAK,WAAW;AAAA,MACzB,OAAO,QAAQ;AAAA,MACf,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,aAAa,SAAS,OAAO,WAAW,GAAG,GAAG,eAAe,OAAO,CAAC;AAAA,MAC5G,OAAO,KAAK,SAAS,OAAO;AAAA,MAC5B,aAAa;AAAA,QACX,QAAQ,YAAY;AAAA,QACpB,SAAS,YAAY,YAAY;AAAA,QACjC,UAAU,CAAC,CAAC,YAAY;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,qBACL,SACuC;AACvC,UAAM,cAAc,MAAM,KAAK,QAAQ,OAAO;AAC9C,UAAM,EAAE,QAAQ,OAAO,IAAQ,mBAAmB;AAClD,UAAM,QAAY,eAAe;AACjC,UAAM,YAAY,QAAQ,cAAc;AACxC,UAAM,SAAa;AAAA,MACjB,YAAY;AAAA,MACZ;AAAA,MACA,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU;AAAA,QACtC,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ,eAAe;AAAA,QACpC,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AACA,UAAM,OAAO;AAAA,MACX,mBAAmB,YAAY;AAAA,MAC/B,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,KAAK;AAAA,QACH,YAAY,OAAO;AAAA,QACnB,yBAAyB,OAAO;AAAA,QAChC,wBAAwB;AAAA,QACxB,WAAe;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AACjE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,MAAM,GAAG,KAAK,OAAO,wBAAwB;AAAA,QACxD,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,OAAO;AACpB,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,QAAI,CAAC,KAAK,IAAI;AACZ,mBAAa,OAAO;AACpB,UAAI,KAAK,WAAW,KAAK;AACvB,cAAM,IAAI,YAAY,KAAK,yIAAoI;AAAA,MACjK;AACA,UAAI,UAAU,KAAK;AACnB,UAAI;AACF,cAAM,IAAK,MAAM,KAAK,KAAK;AAC3B,YAAI,OAAO,EAAE,UAAU,SAAU,WAAU,EAAE;AAAA,iBACpC,EAAE,SAAS,OAAO,EAAE,UAAU,YAAY,aAAa,EAAE,MAAO,WAAU,OAAQ,EAAE,MAA+B,OAAO;AAAA,MACrI,QAAQ;AAAA,MAAe;AACvB,YAAM,IAAI,YAAY,KAAK,QAAQ,OAAO;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,QAAQ,IAAI,cAAc,KAAK;AAClD,QAAI,CAAC,MAAM,SAAS,mBAAmB,KAAK,CAAC,KAAK,MAAM;AACtD,mBAAa,OAAO;AACpB,YAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,CAAC,KAAK,cAAe,OAAM,IAAI,YAAY,KAAK,2BAA2B;AAC/E,YAAM,QAAY,aAAa,QAAQ,QAAQ,KAAK,cAAc,sBAAsB,KAAK,cAAc,UAAU;AACrH,YAAM,UAAW,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC,EAA2B,WAAW;AACjG,UAAI,QAAS,OAAM;AACnB;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,KAAK,UAAU;AACnC,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,gBAAgB;AACtB,UAAM,YAAY,YAA2D;AAC3E,UAAI;AACJ,YAAM,UAAU,IAAI,QAAe,CAAC,GAAG,WAAW;AAChD,YAAI,WAAW,MAAM,OAAO,IAAI,mBAAmB,8BAA8B,CAAC,GAAG,aAAa;AAAA,MACpG,CAAC;AACD,UAAI;AACF,eAAQ,MAAM,QAAQ,KAAK,CAAC,OAAO,KAAK,GAAG,OAAO,CAAC;AAAA,MACrD,UAAE;AACA,YAAI,EAAG,cAAa,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,MAAM;AACV,QAAI,cAAc;AAClB,QAAI,SAA4B;AAChC,QAAI,YAA2B;AAC/B,QAAI,WAAW;AACf,QAAI;AACF,iBAAS;AACP,YAAI,SAAU;AACd,cAAM,EAAE,OAAO,KAAK,IAAI,MAAM,UAAU;AACxC,YAAI,KAAM;AAEV,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,EAAE,QAAQ,SAAS,IAAI;AACpE,YAAI;AACJ,gBAAQ,MAAM,IAAI,QAAQ,MAAM,OAAO,IAAI;AACzC,gBAAM,MAAM,IAAI,MAAM,GAAG,GAAG;AAC5B,gBAAM,IAAI,MAAM,MAAM,CAAC;AACvB,cAAI,IAAI,SAAS,cAAc,EAAG,OAAM,IAAI,YAAY,KAAK,0BAA0B;AACvF,gBAAM,UAAU,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,IAAI;AAC5G,cAAI,CAAC,WAAW,YAAY,SAAU;AAEtC,cAAI;AACJ,cAAI;AACF,oBAAQ,KAAK,MAAM,OAAO;AAAA,UAC5B,QAAQ;AACN,kBAAM,IAAI,YAAY,KAAK,wBAAwB;AAAA,UACrD;AACA,cAAI,OAAO,MAAM,QAAQ,YAAY,CAAC,MAAM,IAAI;AAC9C,kBAAM,IAAI,YAAY,KAAK,+CAA+C;AAAA,UAC5E;AACA,cAAI,MAAM,QAAQ,YAAa,OAAM,IAAI,YAAY,KAAK,iCAAiC,WAAW,SAAS,MAAM,GAAG,GAAG;AAC3H,cAAI,MAAM,QAAQ,GAAG;AACnB,gBAAI,CAAC,MAAM,IAAK,OAAM,IAAI,YAAY,KAAK,sCAAsC;AACjF,wBAAY,MAAM;AAClB,qBAAa,aAAa,QAAQ,SAAS;AAAA,UAC7C;AACA,gBAAM,UAAU,MAAM,UAAU;AAChC,gBAAM,OAAO,IAAI,YAAY,EAAE;AAAA,YACzB,gBAAgB,QAAsB,QAAQ,WAAqB,OAAO,MAAM,KAAK,SAAS,MAAM,EAAE;AAAA,UAC5G;AACA,cAAI,KAAM,OAAM;AAChB;AACA,cAAI,SAAS;AAAE,uBAAW;AAAM;AAAA,UAAO;AAAA,QACzC;AAAA,MACF;AAAA,IACF,UAAE;AACA,mBAAa,OAAO;AACpB,UAAI;AAAE,cAAM,OAAO,OAAO;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IACtD;AACA,QAAI,CAAC,SAAU,OAAM,IAAI,YAAY,KAAK,6CAA6C;AAAA,EACzF;AAAA;AAAA,EAIA,MAAc,sBACZ,QACA,MACA,QACA,MACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,UAAM,aAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,kBAAkB,OAAO;AAAA,MACzB,gBAAgB,OAAO,SAAS;AAAA,MAChC,oBAAoB,OAAO;AAAA,MAC3B,oBAAoB,OAAO;AAAA,MAC3B,gBAAgB,OAAO;AAAA,IACzB;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,QACT,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,iBAC9B,OAAO,OAAO,QAAQ,YAAY,aAAa;AACtD,oBAAU,OAAQ,IAA6B,OAAO;AAAA,MAC1D,QAAQ;AAAA,MAER;AAEA,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,MAC5D;AACA,UAAI,SAAS,WAAW,KAAK;AAC3B,cAAM,IAAI,iBAAiB,SAAS,SAAS;AAAA,MAC/C;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,QAAI,SAAS,WAAW,IAAK,QAAO,CAAC;AACrC,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAc,mBACZ,QACA,MACA,MACA,eACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,UAAM,aAAqC,EAAE,GAAG,KAAK,QAAQ;AAC7D,QAAI,eAAe;AACjB,iBAAW,WAAW,IAAI;AAAA,IAC5B;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,QACT,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,eAAgB,MAAM,SAAS,KAAK;AAC1C,YAAM,IAAI,YAAY,KAAK,oBAAoB,YAAY;AAAA,IAC7D;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,MACzC,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA,EAIA,MAAM,gBACJ,QACA,SACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,gBACJ,eACA,OACA,SACgC;AAChC,UAAM,OAAgC,EAAE,MAAM;AAC9C,QAAI,SAAS,aAAc,MAAK,gBAAgB,QAAQ;AACxD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,mBAAmB,aAAa,CAAC;AAAA,MACrD;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,SAIc;AACjC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACrD,QAAI,SAAS,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAClE,QAAI,SAAS,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACrE,UAAM,KAAK,OAAO,SAAS;AAC3B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,mBAAmB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,aAA6C;AAC9D,WAAO,KAAK,QAAuB,OAAO,oBAAoB,WAAW,EAAE;AAAA,EAC7E;AAAA,EAEA,MAAM,gBACJ,aACA,QAC2C;AAC3C,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,WAAW;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,iBACJ,aACA,QACA,SACgC;AAChC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAClE,QAAI,SAAS,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACrE,UAAM,KAAK,OAAO,SAAS;AAC3B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,WAAW,QAAQ,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;","names":["bs58"]}
package/dist/index.mjs CHANGED
@@ -32,6 +32,78 @@ var SGLConnectionError = class extends SGLError {
32
32
  }
33
33
  };
34
34
 
35
+ // src/e2e.ts
36
+ import { x25519 } from "@noble/curves/ed25519";
37
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha";
38
+ import { sha256 } from "@noble/hashes/sha256";
39
+ import { hkdf } from "@noble/hashes/hkdf";
40
+ import bs58 from "bs58";
41
+ var ALGO_V2 = "x25519-xchacha20poly1305-hkdf-v2";
42
+ var HKDF_SALT = new TextEncoder().encode("sgl-e2e-v2-salt");
43
+ var HKDF_INFO_INPUT = new TextEncoder().encode("sgl-e2e-v2-input");
44
+ var HKDF_INFO_OUTPUT = new TextEncoder().encode("sgl-e2e-v2-output");
45
+ function v2Key(shared, info) {
46
+ return hkdf(sha256, shared, HKDF_SALT, info, 32);
47
+ }
48
+ function aadInput(nodeB58, ephB58, respB58) {
49
+ return new TextEncoder().encode(`sgl-aad/v2/input|node=${nodeB58}|eph=${ephB58}|resp=${respB58}`);
50
+ }
51
+ function aadOutput(respB58, ephB58) {
52
+ return new TextEncoder().encode(`sgl-aad/v2/output|resp=${respB58}|eph=${ephB58}`);
53
+ }
54
+ function aadStream(respB58, ephB58, nonceB58, seq, isFinal) {
55
+ return new TextEncoder().encode(
56
+ `sgl-aad/v2/stream|resp=${respB58}|eph=${ephB58}|nonce=${nonceB58}|seq=${seq}|final=${isFinal ? 1 : 0}`
57
+ );
58
+ }
59
+ function b58enc(u) {
60
+ return bs58.encode(u);
61
+ }
62
+ function b58dec(s) {
63
+ return bs58.decode(s);
64
+ }
65
+ function randomBytes(n) {
66
+ return crypto.getRandomValues(new Uint8Array(n));
67
+ }
68
+ function newResponseKeypair() {
69
+ const secret = x25519.utils.randomPrivateKey();
70
+ return { secret, pubB58: b58enc(x25519.getPublicKey(secret)) };
71
+ }
72
+ function randomNonceB58() {
73
+ return b58enc(randomBytes(16));
74
+ }
75
+ function sealInputV2(nodePubB58, respPubB58, plaintext) {
76
+ const nodePub = b58dec(nodePubB58);
77
+ const ephSecret = x25519.utils.randomPrivateKey();
78
+ const ephPub = x25519.getPublicKey(ephSecret);
79
+ const ephB58 = b58enc(ephPub);
80
+ const shared = x25519.getSharedSecret(ephSecret, nodePub);
81
+ const key = v2Key(shared, HKDF_INFO_INPUT);
82
+ const aad = aadInput(nodePubB58, ephB58, respPubB58);
83
+ const nonce = randomBytes(24);
84
+ const ct = xchacha20poly1305(key, nonce, aad).encrypt(plaintext);
85
+ const out = new Uint8Array(24 + ct.length);
86
+ out.set(nonce, 0);
87
+ out.set(ct, 24);
88
+ return { ciphertext: b58enc(out), ephemeralPub: ephB58 };
89
+ }
90
+ function openOutputV2(respSecret, respPubB58, nodeEphB58, ciphertextB58) {
91
+ const shared = x25519.getSharedSecret(respSecret, b58dec(nodeEphB58));
92
+ const key = v2Key(shared, HKDF_INFO_OUTPUT);
93
+ const aad = aadOutput(respPubB58, nodeEphB58);
94
+ const blob = b58dec(ciphertextB58);
95
+ return xchacha20poly1305(key, blob.slice(0, 24), aad).decrypt(blob.slice(24));
96
+ }
97
+ function streamOutKey(respSecret, nodeStreamEphB58) {
98
+ const shared = x25519.getSharedSecret(respSecret, b58dec(nodeStreamEphB58));
99
+ return v2Key(shared, HKDF_INFO_OUTPUT);
100
+ }
101
+ function openStreamChunk(outKey, respPubB58, streamEphB58, reqNonceB58, seq, isFinal, ctB58) {
102
+ const aad = aadStream(respPubB58, streamEphB58, reqNonceB58, seq, isFinal);
103
+ const blob = b58dec(ctB58);
104
+ return xchacha20poly1305(outKey, blob.slice(0, 24), aad).decrypt(blob.slice(24));
105
+ }
106
+
35
107
  // src/client.ts
36
108
  var DEFAULT_BASE_URL = "https://grid.x402compute.cc";
37
109
  var DEFAULT_TIMEOUT = 6e4;
@@ -42,6 +114,7 @@ var GridClient = class {
42
114
  this.headers = { Accept: "application/json", "Content-Type": "application/json" };
43
115
  if (options.apiKey) {
44
116
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
117
+ this.headers["X-API-Key"] = options.apiKey;
45
118
  }
46
119
  }
47
120
  async request(method, path, body) {
@@ -100,6 +173,21 @@ var GridClient = class {
100
173
  const data = await this.request("GET", "/grid/pricing");
101
174
  return data.pricing ?? [];
102
175
  }
176
+ /**
177
+ * List the nodes serving a model with each node's effective per-token price
178
+ * (operator's custom price if set, else the platform reference), cheapest
179
+ * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,
180
+ * or omit to let the grid route. Optional `cluster` filter (slug or id).
181
+ */
182
+ async providers(model, options) {
183
+ const params = new URLSearchParams({ model });
184
+ if (options?.cluster) params.set("cluster", options.cluster);
185
+ const data = await this.request(
186
+ "GET",
187
+ `/v1/providers?${params.toString()}`
188
+ );
189
+ return data.providers ?? [];
190
+ }
103
191
  // -- Authenticated endpoints ---------------------------------------------
104
192
  async submitJob(model, input, options) {
105
193
  const body = { model, input };
@@ -113,13 +201,228 @@ var GridClient = class {
113
201
  async getAttestation(jobId) {
114
202
  return this.request("GET", `/grid/jobs/${jobId}/attestation`);
115
203
  }
116
- // -- OpenAI-compatible ---------------------------------------------------
204
+ // -- OpenAI-compatible (end-to-end encrypted) ----------------------------
205
+ /** Reserve a node + learn its X25519 key so we can seal the prompt to it.
206
+ * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,
207
+ * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */
208
+ async reserve(req) {
209
+ const body = { model: req.model };
210
+ if (req.node) body.node = req.node;
211
+ if (req.cluster) body.cluster = req.cluster;
212
+ if (req.pay_in_coin) body.pay_in_coin = req.pay_in_coin;
213
+ const res = await this.request("POST", "/v1/reserve", body);
214
+ if (!res.node_x25519_pubkey) {
215
+ throw new SGLAPIError(503, "Reserved node does not support E2E encryption");
216
+ }
217
+ return res;
218
+ }
219
+ /**
220
+ * End-to-end encrypted chat completion. The prompt is sealed in this client to
221
+ * the serving node's key and only decrypts inside its TEE — the orchestrator
222
+ * only relays ciphertext. Requires an `apiKey` (credits); without one the grid
223
+ * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).
224
+ */
117
225
  async chatCompletions(request) {
118
- return this.request(
119
- "POST",
120
- "/v1/chat/completions",
121
- request
226
+ if (request.stream) {
227
+ let content = "";
228
+ for await (const delta of this.chatCompletionStream(request)) content += delta;
229
+ return {
230
+ id: "",
231
+ object: "chat.completion",
232
+ created: 0,
233
+ model: request.model,
234
+ choices: [{ index: 0, message: { role: "assistant", content }, finish_reason: "stop" }]
235
+ };
236
+ }
237
+ const reservation = await this.reserve(request);
238
+ const { secret, pubB58 } = newResponseKeypair();
239
+ const maxTokens = request.max_tokens ?? 512;
240
+ const sealed = sealInputV2(
241
+ reservation.node_x25519_pubkey,
242
+ pubB58,
243
+ new TextEncoder().encode(JSON.stringify({
244
+ messages: request.messages,
245
+ temperature: request.temperature ?? 0.7,
246
+ max_tokens: maxTokens
247
+ }))
248
+ );
249
+ const body = {
250
+ reservation_token: reservation.reservation_token,
251
+ max_tokens: maxTokens,
252
+ // cleartext, only used to quote the x402 price
253
+ enc: {
254
+ ciphertext: sealed.ciphertext,
255
+ client_ephemeral_pubkey: sealed.ephemeralPub,
256
+ client_response_pubkey: pubB58,
257
+ algorithm: ALGO_V2
258
+ }
259
+ };
260
+ let data;
261
+ try {
262
+ data = await this.request("POST", "/v1/chat/completions", body);
263
+ } catch (err) {
264
+ if (err instanceof SGLAPIError && err.statusCode === 402) {
265
+ throw new SGLAPIError(402, "Payment required \u2014 pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.");
266
+ }
267
+ throw err;
268
+ }
269
+ if (!data.sealed_result) throw new SGLAPIError(500, "No sealed result returned");
270
+ const plain = openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);
271
+ const parsed = JSON.parse(new TextDecoder().decode(plain));
272
+ return {
273
+ id: data.id ?? "",
274
+ object: "chat.completion",
275
+ created: data.created ?? 0,
276
+ model: request.model,
277
+ choices: [{ index: 0, message: { role: "assistant", content: parsed.content ?? "" }, finish_reason: "stop" }],
278
+ usage: data.usage ?? parsed.usage,
279
+ attestation: {
280
+ nodeId: reservation.node_id,
281
+ teeType: reservation.tee_type ?? null,
282
+ verified: !!reservation.attestation_verified
283
+ }
284
+ };
285
+ }
286
+ /**
287
+ * Streaming end-to-end encrypted chat completion. Yields decoded text as it
288
+ * arrives; each chunk is decrypted and its ordering + termination verified (a
289
+ * truncated stream throws). Requires `apiKey` (credits). If the server isn't
290
+ * streaming (toggle off), the whole reply is yielded as a single chunk.
291
+ */
292
+ async *chatCompletionStream(request) {
293
+ const reservation = await this.reserve(request);
294
+ const { secret, pubB58 } = newResponseKeypair();
295
+ const nonce = randomNonceB58();
296
+ const maxTokens = request.max_tokens ?? 512;
297
+ const sealed = sealInputV2(
298
+ reservation.node_x25519_pubkey,
299
+ pubB58,
300
+ new TextEncoder().encode(JSON.stringify({
301
+ messages: request.messages,
302
+ temperature: request.temperature ?? 0.7,
303
+ max_tokens: maxTokens,
304
+ stream: true,
305
+ nonce
306
+ }))
122
307
  );
308
+ const body = {
309
+ reservation_token: reservation.reservation_token,
310
+ stream: true,
311
+ max_tokens: maxTokens,
312
+ enc: {
313
+ ciphertext: sealed.ciphertext,
314
+ client_ephemeral_pubkey: sealed.ephemeralPub,
315
+ client_response_pubkey: pubB58,
316
+ algorithm: ALGO_V2
317
+ }
318
+ };
319
+ const controller = new AbortController();
320
+ const overall = setTimeout(() => controller.abort(), this.timeout);
321
+ let resp;
322
+ try {
323
+ resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
324
+ method: "POST",
325
+ headers: this.headers,
326
+ body: JSON.stringify(body),
327
+ signal: controller.signal
328
+ });
329
+ } catch (err) {
330
+ clearTimeout(overall);
331
+ throw new SGLConnectionError(
332
+ `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`
333
+ );
334
+ }
335
+ if (!resp.ok) {
336
+ clearTimeout(overall);
337
+ if (resp.status === 402) {
338
+ throw new SGLAPIError(402, "Payment required \u2014 pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.");
339
+ }
340
+ let message = resp.statusText;
341
+ try {
342
+ const j = await resp.json();
343
+ if (typeof j.error === "string") message = j.error;
344
+ else if (j.error && typeof j.error === "object" && "message" in j.error) message = String(j.error.message);
345
+ } catch {
346
+ }
347
+ throw new SGLAPIError(resp.status, message);
348
+ }
349
+ const ctype = resp.headers.get("content-type") ?? "";
350
+ if (!ctype.includes("text/event-stream") || !resp.body) {
351
+ clearTimeout(overall);
352
+ const data = await resp.json();
353
+ if (!data.sealed_result) throw new SGLAPIError(500, "No sealed result returned");
354
+ const plain = openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);
355
+ const content = JSON.parse(new TextDecoder().decode(plain)).content ?? "";
356
+ if (content) yield content;
357
+ return;
358
+ }
359
+ const reader = resp.body.getReader();
360
+ const decoder = new TextDecoder();
361
+ const INACTIVITY_MS = 6e4;
362
+ const readChunk = async () => {
363
+ let t;
364
+ const timeout = new Promise((_, reject) => {
365
+ t = setTimeout(() => reject(new SGLConnectionError("stream timed out (no tokens)")), INACTIVITY_MS);
366
+ });
367
+ try {
368
+ return await Promise.race([reader.read(), timeout]);
369
+ } finally {
370
+ if (t) clearTimeout(t);
371
+ }
372
+ };
373
+ let buf = "";
374
+ let expectedSeq = 0;
375
+ let outKey = null;
376
+ let streamEph = null;
377
+ let sawFinal = false;
378
+ try {
379
+ for (; ; ) {
380
+ if (sawFinal) break;
381
+ const { value, done } = await readChunk();
382
+ if (done) break;
383
+ buf += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
384
+ let idx;
385
+ while ((idx = buf.indexOf("\n\n")) !== -1) {
386
+ const raw = buf.slice(0, idx);
387
+ buf = buf.slice(idx + 2);
388
+ if (raw.includes("event: error")) throw new SGLAPIError(502, "stream aborted by server");
389
+ const dataStr = raw.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
390
+ if (!dataStr || dataStr === "[DONE]") continue;
391
+ let chunk;
392
+ try {
393
+ chunk = JSON.parse(dataStr);
394
+ } catch {
395
+ throw new SGLAPIError(502, "malformed stream chunk");
396
+ }
397
+ if (typeof chunk.seq !== "number" || !chunk.ct) {
398
+ throw new SGLAPIError(502, "invalid stream chunk (missing seq/ciphertext)");
399
+ }
400
+ if (chunk.seq !== expectedSeq) throw new SGLAPIError(502, `stream out of order (expected ${expectedSeq}, got ${chunk.seq})`);
401
+ if (chunk.seq === 0) {
402
+ if (!chunk.eph) throw new SGLAPIError(502, "stream chunk 0 missing ephemeral key");
403
+ streamEph = chunk.eph;
404
+ outKey = streamOutKey(secret, streamEph);
405
+ }
406
+ const isFinal = chunk.final === true;
407
+ const text = new TextDecoder().decode(
408
+ openStreamChunk(outKey, pubB58, streamEph, nonce, chunk.seq, isFinal, chunk.ct)
409
+ );
410
+ if (text) yield text;
411
+ expectedSeq++;
412
+ if (isFinal) {
413
+ sawFinal = true;
414
+ break;
415
+ }
416
+ }
417
+ }
418
+ } finally {
419
+ clearTimeout(overall);
420
+ try {
421
+ await reader.cancel();
422
+ } catch {
423
+ }
424
+ }
425
+ if (!sawFinal) throw new SGLAPIError(502, "stream ended before final chunk (truncated)");
123
426
  }
124
427
  // -- Processor helpers ----------------------------------------------------
125
428
  async requestWithWalletAuth(method, path, wallet, body) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["export class SGLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SGLError\";\n }\n}\n\nexport class SGLAPIError extends SGLError {\n readonly statusCode: number;\n readonly body?: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(`HTTP ${statusCode}: ${message}`);\n this.name = \"SGLAPIError\";\n this.statusCode = statusCode;\n this.body = body;\n }\n}\n\nexport class SGLAuthError extends SGLAPIError {\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(statusCode, message, body);\n this.name = \"SGLAuthError\";\n }\n}\n\nexport class SGLNotFoundError extends SGLAPIError {\n constructor(message: string, body?: Record<string, unknown>) {\n super(404, message, body);\n this.name = \"SGLNotFoundError\";\n }\n}\n\nexport class SGLConnectionError extends SGLError {\n constructor(message: string) {\n super(message);\n this.name = \"SGLConnectionError\";\n }\n}\n","import { SGLAPIError, SGLAuthError, SGLConnectionError, SGLNotFoundError } from \"./errors.js\";\nimport type {\n AttestationProof,\n CapacityResponse,\n ChatCompletionRequest,\n ChatCompletionResponse,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n ModelInfo,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n WalletAuth,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://grid.x402compute.cc\";\n\nconst DEFAULT_TIMEOUT = 60_000;\n\nexport class GridClient {\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n\n constructor(options: GridClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.headers = { Accept: \"application/json\", \"Content-Type\": \"application/json\" };\n if (options.apiKey) {\n this.headers[\"Authorization\"] = `Bearer ${options.apiKey}`;\n }\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n // -- Public endpoints (no auth) ------------------------------------------\n\n async capacity(): Promise<CapacityResponse> {\n return this.request<CapacityResponse>(\"GET\", \"/grid/capacity\");\n }\n\n async models(): Promise<ModelInfo[]> {\n const data = await this.request<{ models: ModelInfo[] }>(\"GET\", \"/grid/models\");\n return data.models ?? [];\n }\n\n async pricing(): Promise<PricingInfo[]> {\n const data = await this.request<{ pricing: PricingInfo[] }>(\"GET\", \"/grid/pricing\");\n return data.pricing ?? [];\n }\n\n // -- Authenticated endpoints ---------------------------------------------\n\n async submitJob(\n model: string,\n input: Record<string, unknown>,\n options?: { submitterWallet?: string; submitterChain?: string },\n ): Promise<JobResponse> {\n const body: Record<string, unknown> = { model, input };\n if (options?.submitterWallet) body.submitter_wallet = options.submitterWallet;\n if (options?.submitterChain) body.submitter_chain = options.submitterChain;\n return this.request<JobResponse>(\"POST\", \"/grid/jobs\", body);\n }\n\n async getJob(jobId: string): Promise<JobResult> {\n return this.request<JobResult>(\"GET\", `/grid/jobs/${jobId}`);\n }\n\n async getAttestation(jobId: string): Promise<AttestationProof> {\n return this.request<AttestationProof>(\"GET\", `/grid/jobs/${jobId}/attestation`);\n }\n\n // -- OpenAI-compatible ---------------------------------------------------\n\n async chatCompletions(\n request: ChatCompletionRequest,\n ): Promise<ChatCompletionResponse> {\n return this.request<ChatCompletionResponse>(\n \"POST\",\n \"/v1/chat/completions\",\n request,\n );\n }\n\n // -- Processor helpers ----------------------------------------------------\n\n private async requestWithWalletAuth<T>(\n method: string,\n path: string,\n wallet: WalletAuth,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = {\n ...this.headers,\n \"X-Auth-Address\": wallet.address,\n \"X-Auth-Chain\": wallet.chain ?? \"solana\",\n \"X-Auth-Signature\": wallet.signature,\n \"X-Auth-Timestamp\": wallet.timestamp,\n \"X-Auth-Nonce\": wallet.nonce,\n };\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n private async requestWithPayment<T>(\n method: string,\n path: string,\n body?: unknown,\n paymentHeader?: string,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = { ...this.headers };\n if (paymentHeader) {\n reqHeaders[\"X-Payment\"] = paymentHeader;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (response.status === 402) {\n const requirements = (await response.json()) as Record<string, unknown>;\n throw new SGLAPIError(402, \"Payment required\", requirements);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n } catch {\n /* body not JSON */\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n return (await response.json()) as T;\n }\n\n // -- Processors -----------------------------------------------------------\n\n async deployProcessor(\n wallet: WalletAuth,\n options: DeployProcessorOptions,\n ): Promise<ProcessorDeployResult> {\n return this.requestWithWalletAuth<ProcessorDeployResult>(\n \"POST\",\n \"/grid/processors\",\n wallet,\n options,\n );\n }\n\n async invokeProcessor(\n processorName: string,\n input: Record<string, unknown>,\n options?: { paymentHeader?: string; paymentToken?: \"USDC\" | \"SGL\" },\n ): Promise<ProcessorInvokeResult> {\n const body: Record<string, unknown> = { input };\n if (options?.paymentToken) body.payment_token = options.paymentToken;\n return this.requestWithPayment<ProcessorInvokeResult>(\n \"POST\",\n `/grid/processors/${encodeURIComponent(processorName)}/invoke`,\n body,\n options?.paymentHeader,\n );\n }\n\n async listProcessors(options?: {\n owner?: string;\n page?: number;\n limit?: number;\n }): Promise<ProcessorListResponse> {\n const params = new URLSearchParams();\n if (options?.owner) params.set(\"owner\", options.owner);\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<ProcessorListResponse>(\n \"GET\",\n `/grid/processors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async getProcessor(processorId: string): Promise<ProcessorInfo> {\n return this.request<ProcessorInfo>(\"GET\", `/grid/processors/${processorId}`);\n }\n\n async deleteProcessor(\n processorId: string,\n wallet: WalletAuth,\n ): Promise<{ deleted: boolean; id: string }> {\n return this.requestWithWalletAuth<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/grid/processors/${processorId}`,\n wallet,\n );\n }\n\n async getProcessorLogs(\n processorId: string,\n wallet: WalletAuth,\n options?: { page?: number; limit?: number },\n ): Promise<ProcessorLogsResponse> {\n const params = new URLSearchParams();\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.requestWithWalletAuth<ProcessorLogsResponse>(\n \"GET\",\n `/grid/processors/${processorId}/logs${qs ? `?${qs}` : \"\"}`,\n wallet,\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/e2e.ts","../src/client.ts"],"sourcesContent":["export class SGLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SGLError\";\n }\n}\n\nexport class SGLAPIError extends SGLError {\n readonly statusCode: number;\n readonly body?: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(`HTTP ${statusCode}: ${message}`);\n this.name = \"SGLAPIError\";\n this.statusCode = statusCode;\n this.body = body;\n }\n}\n\nexport class SGLAuthError extends SGLAPIError {\n constructor(\n statusCode: number,\n message: string,\n body?: Record<string, unknown>,\n ) {\n super(statusCode, message, body);\n this.name = \"SGLAuthError\";\n }\n}\n\nexport class SGLNotFoundError extends SGLAPIError {\n constructor(message: string, body?: Record<string, unknown>) {\n super(404, message, body);\n this.name = \"SGLNotFoundError\";\n }\n}\n\nexport class SGLConnectionError extends SGLError {\n constructor(message: string) {\n super(message);\n this.name = \"SGLConnectionError\";\n }\n}\n","/**\n * End-to-end encryption for the SGL grid (client side).\n *\n * Must match sgl-node/src/encryption.rs, the orchestrator, and the browser/Python\n * clients byte-for-byte: X25519 ECDH -> HKDF-SHA256 -> XChaCha20-Poly1305 (24-byte\n * nonce), AAD-bound. Sealed blob layout: nonce(24) || ciphertext, base58.\n *\n * The orchestrator only ever relays ciphertext — it never sees the prompt or reply.\n */\n\nimport { x25519 } from \"@noble/curves/ed25519\";\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { hkdf } from \"@noble/hashes/hkdf\";\nimport bs58 from \"bs58\";\n\nexport const ALGO_V2 = \"x25519-xchacha20poly1305-hkdf-v2\";\nexport const ALGO_V2_STREAM = \"x25519-xchacha20poly1305-hkdf-v2-stream\";\n\nconst HKDF_SALT = new TextEncoder().encode(\"sgl-e2e-v2-salt\");\nconst HKDF_INFO_INPUT = new TextEncoder().encode(\"sgl-e2e-v2-input\");\nconst HKDF_INFO_OUTPUT = new TextEncoder().encode(\"sgl-e2e-v2-output\");\n\nfunction v2Key(shared: Uint8Array, info: Uint8Array): Uint8Array {\n return hkdf(sha256, shared, HKDF_SALT, info, 32);\n}\nfunction aadInput(nodeB58: string, ephB58: string, respB58: string): Uint8Array {\n return new TextEncoder().encode(`sgl-aad/v2/input|node=${nodeB58}|eph=${ephB58}|resp=${respB58}`);\n}\nfunction aadOutput(respB58: string, ephB58: string): Uint8Array {\n return new TextEncoder().encode(`sgl-aad/v2/output|resp=${respB58}|eph=${ephB58}`);\n}\nfunction aadStream(respB58: string, ephB58: string, nonceB58: string, seq: number, isFinal: boolean): Uint8Array {\n return new TextEncoder().encode(\n `sgl-aad/v2/stream|resp=${respB58}|eph=${ephB58}|nonce=${nonceB58}|seq=${seq}|final=${isFinal ? 1 : 0}`,\n );\n}\n\nfunction b58enc(u: Uint8Array): string {\n return bs58.encode(u);\n}\nfunction b58dec(s: string): Uint8Array {\n return bs58.decode(s);\n}\nfunction randomBytes(n: number): Uint8Array {\n return crypto.getRandomValues(new Uint8Array(n));\n}\n\nexport interface ResponseKeypair {\n secret: Uint8Array;\n pubB58: string;\n}\n\n/** The caller's response keypair — the node seals its reply to this. */\nexport function newResponseKeypair(): ResponseKeypair {\n const secret = x25519.utils.randomPrivateKey();\n return { secret, pubB58: b58enc(x25519.getPublicKey(secret)) };\n}\n\n/** A per-request nonce bound into every stream chunk's AAD. */\nexport function randomNonceB58(): string {\n return b58enc(randomBytes(16));\n}\n\n/** Seal the prompt to the node's X25519 key. */\nexport function sealInputV2(\n nodePubB58: string,\n respPubB58: string,\n plaintext: Uint8Array,\n): { ciphertext: string; ephemeralPub: string } {\n const nodePub = b58dec(nodePubB58);\n const ephSecret = x25519.utils.randomPrivateKey();\n const ephPub = x25519.getPublicKey(ephSecret);\n const ephB58 = b58enc(ephPub);\n const shared = x25519.getSharedSecret(ephSecret, nodePub);\n const key = v2Key(shared, HKDF_INFO_INPUT);\n const aad = aadInput(nodePubB58, ephB58, respPubB58);\n const nonce = randomBytes(24);\n const ct = xchacha20poly1305(key, nonce, aad).encrypt(plaintext);\n const out = new Uint8Array(24 + ct.length);\n out.set(nonce, 0);\n out.set(ct, 24);\n return { ciphertext: b58enc(out), ephemeralPub: ephB58 };\n}\n\n/** Open the node's (non-stream) reply sealed to our response key. */\nexport function openOutputV2(\n respSecret: Uint8Array,\n respPubB58: string,\n nodeEphB58: string,\n ciphertextB58: string,\n): Uint8Array {\n const shared = x25519.getSharedSecret(respSecret, b58dec(nodeEphB58));\n const key = v2Key(shared, HKDF_INFO_OUTPUT);\n const aad = aadOutput(respPubB58, nodeEphB58);\n const blob = b58dec(ciphertextB58);\n return xchacha20poly1305(key, blob.slice(0, 24), aad).decrypt(blob.slice(24));\n}\n\n/** Derive the stream output key once from the node's stream ephemeral (chunk 0). */\nexport function streamOutKey(respSecret: Uint8Array, nodeStreamEphB58: string): Uint8Array {\n const shared = x25519.getSharedSecret(respSecret, b58dec(nodeStreamEphB58));\n return v2Key(shared, HKDF_INFO_OUTPUT);\n}\n\n/** Open one stream chunk with the precomputed key + nonce/seq/final-bound AAD. */\nexport function openStreamChunk(\n outKey: Uint8Array,\n respPubB58: string,\n streamEphB58: string,\n reqNonceB58: string,\n seq: number,\n isFinal: boolean,\n ctB58: string,\n): Uint8Array {\n const aad = aadStream(respPubB58, streamEphB58, reqNonceB58, seq, isFinal);\n const blob = b58dec(ctB58);\n return xchacha20poly1305(outKey, blob.slice(0, 24), aad).decrypt(blob.slice(24));\n}\n","import { SGLAPIError, SGLAuthError, SGLConnectionError, SGLNotFoundError } from \"./errors.js\";\nimport * as e2e from \"./e2e.js\";\nimport type {\n AttestationProof,\n CapacityResponse,\n ChatCompletionRequest,\n ChatCompletionResponse,\n DeployProcessorOptions,\n GridClientOptions,\n JobResponse,\n JobResult,\n ModelInfo,\n PricingInfo,\n ProcessorDeployResult,\n ProcessorInfo,\n ProcessorInvokeResult,\n ProcessorListResponse,\n ProcessorLogEntry,\n ProcessorLogsResponse,\n ProviderInfo,\n ProvidersResponse,\n ReserveResponse,\n WalletAuth,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://grid.x402compute.cc\";\n\nconst DEFAULT_TIMEOUT = 60_000;\n\nexport class GridClient {\n private readonly baseUrl: string;\n private readonly headers: Record<string, string>;\n private readonly timeout: number;\n\n constructor(options: GridClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.headers = { Accept: \"application/json\", \"Content-Type\": \"application/json\" };\n if (options.apiKey) {\n this.headers[\"Authorization\"] = `Bearer ${options.apiKey}`;\n // Grid credit billing reads X-API-Key; send both so reserve + chat resolve\n // the paying wallet (credits mode) rather than falling back to anonymous x402.\n this.headers[\"X-API-Key\"] = options.apiKey;\n }\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n // -- Public endpoints (no auth) ------------------------------------------\n\n async capacity(): Promise<CapacityResponse> {\n return this.request<CapacityResponse>(\"GET\", \"/grid/capacity\");\n }\n\n async models(): Promise<ModelInfo[]> {\n const data = await this.request<{ models: ModelInfo[] }>(\"GET\", \"/grid/models\");\n return data.models ?? [];\n }\n\n async pricing(): Promise<PricingInfo[]> {\n const data = await this.request<{ pricing: PricingInfo[] }>(\"GET\", \"/grid/pricing\");\n return data.pricing ?? [];\n }\n\n /**\n * List the nodes serving a model with each node's effective per-token price\n * (operator's custom price if set, else the platform reference), cheapest\n * first. Pass a chosen `node_id` as `node` on `chatCompletions` to pin it,\n * or omit to let the grid route. Optional `cluster` filter (slug or id).\n */\n async providers(\n model: string,\n options?: { cluster?: string },\n ): Promise<ProviderInfo[]> {\n const params = new URLSearchParams({ model });\n if (options?.cluster) params.set(\"cluster\", options.cluster);\n const data = await this.request<ProvidersResponse>(\n \"GET\",\n `/v1/providers?${params.toString()}`,\n );\n return data.providers ?? [];\n }\n\n // -- Authenticated endpoints ---------------------------------------------\n\n async submitJob(\n model: string,\n input: Record<string, unknown>,\n options?: { submitterWallet?: string; submitterChain?: string },\n ): Promise<JobResponse> {\n const body: Record<string, unknown> = { model, input };\n if (options?.submitterWallet) body.submitter_wallet = options.submitterWallet;\n if (options?.submitterChain) body.submitter_chain = options.submitterChain;\n return this.request<JobResponse>(\"POST\", \"/grid/jobs\", body);\n }\n\n async getJob(jobId: string): Promise<JobResult> {\n return this.request<JobResult>(\"GET\", `/grid/jobs/${jobId}`);\n }\n\n async getAttestation(jobId: string): Promise<AttestationProof> {\n return this.request<AttestationProof>(\"GET\", `/grid/jobs/${jobId}/attestation`);\n }\n\n // -- OpenAI-compatible (end-to-end encrypted) ----------------------------\n\n /** Reserve a node + learn its X25519 key so we can seal the prompt to it.\n * Forwards an optional pinned `node` (see `providers()`), `cluster` filter,\n * and `pay_in_coin` so the orchestrator reserves + quotes accordingly. */\n private async reserve(req: {\n model: string;\n node?: string;\n cluster?: string;\n pay_in_coin?: boolean;\n }): Promise<ReserveResponse> {\n const body: Record<string, unknown> = { model: req.model };\n if (req.node) body.node = req.node;\n if (req.cluster) body.cluster = req.cluster;\n if (req.pay_in_coin) body.pay_in_coin = req.pay_in_coin;\n const res = await this.request<ReserveResponse>(\"POST\", \"/v1/reserve\", body);\n if (!res.node_x25519_pubkey) {\n throw new SGLAPIError(503, \"Reserved node does not support E2E encryption\");\n }\n return res;\n }\n\n /**\n * End-to-end encrypted chat completion. The prompt is sealed in this client to\n * the serving node's key and only decrypts inside its TEE — the orchestrator\n * only relays ciphertext. Requires an `apiKey` (credits); without one the grid\n * replies 402 (the SDK does not sign x402 payments — use the wallet/browser flow).\n */\n async chatCompletions(\n request: ChatCompletionRequest,\n ): Promise<ChatCompletionResponse> {\n if (request.stream) {\n // Collapse the stream into a single response for the non-streaming API.\n let content = \"\";\n for await (const delta of this.chatCompletionStream(request)) content += delta;\n return {\n id: \"\", object: \"chat.completion\", created: 0, model: request.model,\n choices: [{ index: 0, message: { role: \"assistant\", content }, finish_reason: \"stop\" }],\n };\n }\n\n const reservation = await this.reserve(request);\n const { secret, pubB58 } = e2e.newResponseKeypair();\n const maxTokens = request.max_tokens ?? 512;\n const sealed = e2e.sealInputV2(\n reservation.node_x25519_pubkey,\n pubB58,\n new TextEncoder().encode(JSON.stringify({\n messages: request.messages,\n temperature: request.temperature ?? 0.7,\n max_tokens: maxTokens,\n })),\n );\n const body = {\n reservation_token: reservation.reservation_token,\n max_tokens: maxTokens, // cleartext, only used to quote the x402 price\n enc: {\n ciphertext: sealed.ciphertext,\n client_ephemeral_pubkey: sealed.ephemeralPub,\n client_response_pubkey: pubB58,\n algorithm: e2e.ALGO_V2,\n },\n };\n\n let data: {\n id?: string; created?: number; sealed_result?: { ephemeral_public_key: string; ciphertext: string };\n usage?: ChatCompletionResponse[\"usage\"];\n };\n try {\n data = await this.request(\"POST\", \"/v1/chat/completions\", body);\n } catch (err) {\n if (err instanceof SGLAPIError && err.statusCode === 402) {\n throw new SGLAPIError(402, \"Payment required — pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.\");\n }\n throw err;\n }\n\n if (!data.sealed_result) throw new SGLAPIError(500, \"No sealed result returned\");\n const plain = e2e.openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);\n const parsed = JSON.parse(new TextDecoder().decode(plain)) as { content?: string; usage?: ChatCompletionResponse[\"usage\"] };\n\n return {\n id: data.id ?? \"\",\n object: \"chat.completion\",\n created: data.created ?? 0,\n model: request.model,\n choices: [{ index: 0, message: { role: \"assistant\", content: parsed.content ?? \"\" }, finish_reason: \"stop\" }],\n usage: data.usage ?? parsed.usage,\n attestation: {\n nodeId: reservation.node_id,\n teeType: reservation.tee_type ?? null,\n verified: !!reservation.attestation_verified,\n },\n };\n }\n\n /**\n * Streaming end-to-end encrypted chat completion. Yields decoded text as it\n * arrives; each chunk is decrypted and its ordering + termination verified (a\n * truncated stream throws). Requires `apiKey` (credits). If the server isn't\n * streaming (toggle off), the whole reply is yielded as a single chunk.\n */\n async *chatCompletionStream(\n request: ChatCompletionRequest,\n ): AsyncGenerator<string, void, unknown> {\n const reservation = await this.reserve(request);\n const { secret, pubB58 } = e2e.newResponseKeypair();\n const nonce = e2e.randomNonceB58();\n const maxTokens = request.max_tokens ?? 512;\n const sealed = e2e.sealInputV2(\n reservation.node_x25519_pubkey,\n pubB58,\n new TextEncoder().encode(JSON.stringify({\n messages: request.messages,\n temperature: request.temperature ?? 0.7,\n max_tokens: maxTokens,\n stream: true,\n nonce,\n })),\n );\n const body = {\n reservation_token: reservation.reservation_token,\n stream: true,\n max_tokens: maxTokens,\n enc: {\n ciphertext: sealed.ciphertext,\n client_ephemeral_pubkey: sealed.ephemeralPub,\n client_response_pubkey: pubB58,\n algorithm: e2e.ALGO_V2,\n },\n };\n\n const controller = new AbortController();\n const overall = setTimeout(() => controller.abort(), this.timeout);\n let resp: Response;\n try {\n resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(overall);\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (!resp.ok) {\n clearTimeout(overall);\n if (resp.status === 402) {\n throw new SGLAPIError(402, \"Payment required — pass an apiKey (credits). The TS SDK does not sign x402 payments; use the wallet/browser flow for pay-per-call.\");\n }\n let message = resp.statusText;\n try {\n const j = (await resp.json()) as { error?: unknown };\n if (typeof j.error === \"string\") message = j.error;\n else if (j.error && typeof j.error === \"object\" && \"message\" in j.error) message = String((j.error as { message: unknown }).message);\n } catch { /* ignore */ }\n throw new SGLAPIError(resp.status, message);\n }\n\n const ctype = resp.headers.get(\"content-type\") ?? \"\";\n if (!ctype.includes(\"text/event-stream\") || !resp.body) {\n clearTimeout(overall);\n const data = (await resp.json()) as { sealed_result?: { ephemeral_public_key: string; ciphertext: string } };\n if (!data.sealed_result) throw new SGLAPIError(500, \"No sealed result returned\");\n const plain = e2e.openOutputV2(secret, pubB58, data.sealed_result.ephemeral_public_key, data.sealed_result.ciphertext);\n const content = (JSON.parse(new TextDecoder().decode(plain)) as { content?: string }).content ?? \"\";\n if (content) yield content;\n return;\n }\n\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n const INACTIVITY_MS = 60_000;\n const readChunk = async (): Promise<ReadableStreamReadResult<Uint8Array>> => {\n let t: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n t = setTimeout(() => reject(new SGLConnectionError(\"stream timed out (no tokens)\")), INACTIVITY_MS);\n });\n try {\n return (await Promise.race([reader.read(), timeout])) as ReadableStreamReadResult<Uint8Array>;\n } finally {\n if (t) clearTimeout(t);\n }\n };\n\n let buf = \"\";\n let expectedSeq = 0;\n let outKey: Uint8Array | null = null;\n let streamEph: string | null = null;\n let sawFinal = false;\n try {\n for (;;) {\n if (sawFinal) break;\n const { value, done } = await readChunk();\n if (done) break;\n // Normalize CRLF so \\n\\n event framing works regardless of line endings.\n buf += decoder.decode(value, { stream: true }).replace(/\\r\\n/g, \"\\n\");\n let idx: number;\n while ((idx = buf.indexOf(\"\\n\\n\")) !== -1) {\n const raw = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n if (raw.includes(\"event: error\")) throw new SGLAPIError(502, \"stream aborted by server\");\n const dataStr = raw.split(\"\\n\").filter((l) => l.startsWith(\"data:\")).map((l) => l.slice(5).trim()).join(\"\\n\");\n if (!dataStr || dataStr === \"[DONE]\") continue;\n // Fail closed: a malformed or non-chunk data event is a protocol error.\n let chunk: { seq?: number; final?: boolean; eph?: string; ct?: string };\n try {\n chunk = JSON.parse(dataStr);\n } catch {\n throw new SGLAPIError(502, \"malformed stream chunk\");\n }\n if (typeof chunk.seq !== \"number\" || !chunk.ct) {\n throw new SGLAPIError(502, \"invalid stream chunk (missing seq/ciphertext)\");\n }\n if (chunk.seq !== expectedSeq) throw new SGLAPIError(502, `stream out of order (expected ${expectedSeq}, got ${chunk.seq})`);\n if (chunk.seq === 0) {\n if (!chunk.eph) throw new SGLAPIError(502, \"stream chunk 0 missing ephemeral key\");\n streamEph = chunk.eph;\n outKey = e2e.streamOutKey(secret, streamEph);\n }\n const isFinal = chunk.final === true;\n const text = new TextDecoder().decode(\n e2e.openStreamChunk(outKey as Uint8Array, pubB58, streamEph as string, nonce, chunk.seq, isFinal, chunk.ct),\n );\n if (text) yield text;\n expectedSeq++;\n if (isFinal) { sawFinal = true; break; }\n }\n }\n } finally {\n clearTimeout(overall);\n try { await reader.cancel(); } catch { /* ignore */ }\n }\n if (!sawFinal) throw new SGLAPIError(502, \"stream ended before final chunk (truncated)\");\n }\n\n // -- Processor helpers ----------------------------------------------------\n\n private async requestWithWalletAuth<T>(\n method: string,\n path: string,\n wallet: WalletAuth,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = {\n ...this.headers,\n \"X-Auth-Address\": wallet.address,\n \"X-Auth-Chain\": wallet.chain ?? \"solana\",\n \"X-Auth-Signature\": wallet.signature,\n \"X-Auth-Timestamp\": wallet.timestamp,\n \"X-Auth-Nonce\": wallet.nonce,\n };\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n else if (err && typeof err === \"object\" && \"message\" in err)\n message = String((err as { message: unknown }).message);\n } catch {\n /* body not JSON */\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new SGLAuthError(response.status, message, errorBody);\n }\n if (response.status === 404) {\n throw new SGLNotFoundError(message, errorBody);\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n if (response.status === 204) return {} as T;\n return (await response.json()) as T;\n }\n\n private async requestWithPayment<T>(\n method: string,\n path: string,\n body?: unknown,\n paymentHeader?: string,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n\n const reqHeaders: Record<string, string> = { ...this.headers };\n if (paymentHeader) {\n reqHeaders[\"X-Payment\"] = paymentHeader;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: reqHeaders,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SGLConnectionError(`Request to ${url} timed out`);\n }\n throw new SGLConnectionError(\n `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,\n );\n } finally {\n clearTimeout(timer);\n }\n\n if (response.status === 402) {\n const requirements = (await response.json()) as Record<string, unknown>;\n throw new SGLAPIError(402, \"Payment required\", requirements);\n }\n\n if (!response.ok) {\n let errorBody: Record<string, unknown> | undefined;\n let message = response.statusText;\n try {\n errorBody = (await response.json()) as Record<string, unknown>;\n const err = errorBody?.error;\n if (typeof err === \"string\") message = err;\n } catch {\n /* body not JSON */\n }\n throw new SGLAPIError(response.status, message, errorBody);\n }\n\n return (await response.json()) as T;\n }\n\n // -- Processors -----------------------------------------------------------\n\n async deployProcessor(\n wallet: WalletAuth,\n options: DeployProcessorOptions,\n ): Promise<ProcessorDeployResult> {\n return this.requestWithWalletAuth<ProcessorDeployResult>(\n \"POST\",\n \"/grid/processors\",\n wallet,\n options,\n );\n }\n\n async invokeProcessor(\n processorName: string,\n input: Record<string, unknown>,\n options?: { paymentHeader?: string; paymentToken?: \"USDC\" | \"SGL\" },\n ): Promise<ProcessorInvokeResult> {\n const body: Record<string, unknown> = { input };\n if (options?.paymentToken) body.payment_token = options.paymentToken;\n return this.requestWithPayment<ProcessorInvokeResult>(\n \"POST\",\n `/grid/processors/${encodeURIComponent(processorName)}/invoke`,\n body,\n options?.paymentHeader,\n );\n }\n\n async listProcessors(options?: {\n owner?: string;\n page?: number;\n limit?: number;\n }): Promise<ProcessorListResponse> {\n const params = new URLSearchParams();\n if (options?.owner) params.set(\"owner\", options.owner);\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.request<ProcessorListResponse>(\n \"GET\",\n `/grid/processors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n async getProcessor(processorId: string): Promise<ProcessorInfo> {\n return this.request<ProcessorInfo>(\"GET\", `/grid/processors/${processorId}`);\n }\n\n async deleteProcessor(\n processorId: string,\n wallet: WalletAuth,\n ): Promise<{ deleted: boolean; id: string }> {\n return this.requestWithWalletAuth<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/grid/processors/${processorId}`,\n wallet,\n );\n }\n\n async getProcessorLogs(\n processorId: string,\n wallet: WalletAuth,\n options?: { page?: number; limit?: number },\n ): Promise<ProcessorLogsResponse> {\n const params = new URLSearchParams();\n if (options?.page != null) params.set(\"page\", String(options.page));\n if (options?.limit != null) params.set(\"limit\", String(options.limit));\n const qs = params.toString();\n return this.requestWithWalletAuth<ProcessorLogsResponse>(\n \"GET\",\n `/grid/processors/${processorId}/logs${qs ? `?${qs}` : \"\"}`,\n wallet,\n );\n }\n}\n"],"mappings":";AAAO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,cAAN,cAA0B,SAAS;AAAA,EAIxC,YACE,YACA,SACA,MACA;AACA,UAAM,QAAQ,UAAU,KAAK,OAAO,EAAE;AACtC,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,YAAY;AAAA,EAC5C,YACE,YACA,SACA,MACA;AACA,UAAM,YAAY,SAAS,IAAI;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,mBAAN,cAA+B,YAAY;AAAA,EAChD,YAAY,SAAiB,MAAgC;AAC3D,UAAM,KAAK,SAAS,IAAI;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,SAAS;AAAA,EAC/C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACpCA,SAAS,cAAc;AACvB,SAAS,yBAAyB;AAClC,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,OAAO,UAAU;AAEV,IAAM,UAAU;AAGvB,IAAM,YAAY,IAAI,YAAY,EAAE,OAAO,iBAAiB;AAC5D,IAAM,kBAAkB,IAAI,YAAY,EAAE,OAAO,kBAAkB;AACnE,IAAM,mBAAmB,IAAI,YAAY,EAAE,OAAO,mBAAmB;AAErE,SAAS,MAAM,QAAoB,MAA8B;AAC/D,SAAO,KAAK,QAAQ,QAAQ,WAAW,MAAM,EAAE;AACjD;AACA,SAAS,SAAS,SAAiB,QAAgB,SAA6B;AAC9E,SAAO,IAAI,YAAY,EAAE,OAAO,yBAAyB,OAAO,QAAQ,MAAM,SAAS,OAAO,EAAE;AAClG;AACA,SAAS,UAAU,SAAiB,QAA4B;AAC9D,SAAO,IAAI,YAAY,EAAE,OAAO,0BAA0B,OAAO,QAAQ,MAAM,EAAE;AACnF;AACA,SAAS,UAAU,SAAiB,QAAgB,UAAkB,KAAa,SAA8B;AAC/G,SAAO,IAAI,YAAY,EAAE;AAAA,IACvB,0BAA0B,OAAO,QAAQ,MAAM,UAAU,QAAQ,QAAQ,GAAG,UAAU,UAAU,IAAI,CAAC;AAAA,EACvG;AACF;AAEA,SAAS,OAAO,GAAuB;AACrC,SAAO,KAAK,OAAO,CAAC;AACtB;AACA,SAAS,OAAO,GAAuB;AACrC,SAAO,KAAK,OAAO,CAAC;AACtB;AACA,SAAS,YAAY,GAAuB;AAC1C,SAAO,OAAO,gBAAgB,IAAI,WAAW,CAAC,CAAC;AACjD;AAQO,SAAS,qBAAsC;AACpD,QAAM,SAAS,OAAO,MAAM,iBAAiB;AAC7C,SAAO,EAAE,QAAQ,QAAQ,OAAO,OAAO,aAAa,MAAM,CAAC,EAAE;AAC/D;AAGO,SAAS,iBAAyB;AACvC,SAAO,OAAO,YAAY,EAAE,CAAC;AAC/B;AAGO,SAAS,YACd,YACA,YACA,WAC8C;AAC9C,QAAM,UAAU,OAAO,UAAU;AACjC,QAAM,YAAY,OAAO,MAAM,iBAAiB;AAChD,QAAM,SAAS,OAAO,aAAa,SAAS;AAC5C,QAAM,SAAS,OAAO,MAAM;AAC5B,QAAM,SAAS,OAAO,gBAAgB,WAAW,OAAO;AACxD,QAAM,MAAM,MAAM,QAAQ,eAAe;AACzC,QAAM,MAAM,SAAS,YAAY,QAAQ,UAAU;AACnD,QAAM,QAAQ,YAAY,EAAE;AAC5B,QAAM,KAAK,kBAAkB,KAAK,OAAO,GAAG,EAAE,QAAQ,SAAS;AAC/D,QAAM,MAAM,IAAI,WAAW,KAAK,GAAG,MAAM;AACzC,MAAI,IAAI,OAAO,CAAC;AAChB,MAAI,IAAI,IAAI,EAAE;AACd,SAAO,EAAE,YAAY,OAAO,GAAG,GAAG,cAAc,OAAO;AACzD;AAGO,SAAS,aACd,YACA,YACA,YACA,eACY;AACZ,QAAM,SAAS,OAAO,gBAAgB,YAAY,OAAO,UAAU,CAAC;AACpE,QAAM,MAAM,MAAM,QAAQ,gBAAgB;AAC1C,QAAM,MAAM,UAAU,YAAY,UAAU;AAC5C,QAAM,OAAO,OAAO,aAAa;AACjC,SAAO,kBAAkB,KAAK,KAAK,MAAM,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC;AAC9E;AAGO,SAAS,aAAa,YAAwB,kBAAsC;AACzF,QAAM,SAAS,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,CAAC;AAC1E,SAAO,MAAM,QAAQ,gBAAgB;AACvC;AAGO,SAAS,gBACd,QACA,YACA,cACA,aACA,KACA,SACA,OACY;AACZ,QAAM,MAAM,UAAU,YAAY,cAAc,aAAa,KAAK,OAAO;AACzE,QAAM,OAAO,OAAO,KAAK;AACzB,SAAO,kBAAkB,QAAQ,KAAK,MAAM,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC;AACjF;;;AC7FO,IAAM,mBAAmB;AAEhC,IAAM,kBAAkB;AAEjB,IAAM,aAAN,MAAiB;AAAA,EAKtB,YAAY,UAA6B,CAAC,GAAG;AAC3C,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACvE,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,UAAU,EAAE,QAAQ,oBAAoB,gBAAgB,mBAAmB;AAChF,QAAI,QAAQ,QAAQ;AAClB,WAAK,QAAQ,eAAe,IAAI,UAAU,QAAQ,MAAM;AAGxD,WAAK,QAAQ,WAAW,IAAI,QAAQ;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS,KAAK;AAAA,QACd,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,iBAC9B,OAAO,OAAO,QAAQ,YAAY,aAAa;AACtD,oBAAU,OAAQ,IAA6B,OAAO;AAAA,MAC1D,QAAQ;AAAA,MAER;AAEA,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,MAC5D;AACA,UAAI,SAAS,WAAW,KAAK;AAC3B,cAAM,IAAI,iBAAiB,SAAS,SAAS;AAAA,MAC/C;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,QAAI,SAAS,WAAW,IAAK,QAAO,CAAC;AACrC,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA,EAIA,MAAM,WAAsC;AAC1C,WAAO,KAAK,QAA0B,OAAO,gBAAgB;AAAA,EAC/D;AAAA,EAEA,MAAM,SAA+B;AACnC,UAAM,OAAO,MAAM,KAAK,QAAiC,OAAO,cAAc;AAC9E,WAAO,KAAK,UAAU,CAAC;AAAA,EACzB;AAAA,EAEA,MAAM,UAAkC;AACtC,UAAM,OAAO,MAAM,KAAK,QAAoC,OAAO,eAAe;AAClF,WAAO,KAAK,WAAW,CAAC;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UACJ,OACA,SACyB;AACzB,UAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,CAAC;AAC5C,QAAI,SAAS,QAAS,QAAO,IAAI,WAAW,QAAQ,OAAO;AAC3D,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA,iBAAiB,OAAO,SAAS,CAAC;AAAA,IACpC;AACA,WAAO,KAAK,aAAa,CAAC;AAAA,EAC5B;AAAA;AAAA,EAIA,MAAM,UACJ,OACA,OACA,SACsB;AACtB,UAAM,OAAgC,EAAE,OAAO,MAAM;AACrD,QAAI,SAAS,gBAAiB,MAAK,mBAAmB,QAAQ;AAC9D,QAAI,SAAS,eAAgB,MAAK,kBAAkB,QAAQ;AAC5D,WAAO,KAAK,QAAqB,QAAQ,cAAc,IAAI;AAAA,EAC7D;AAAA,EAEA,MAAM,OAAO,OAAmC;AAC9C,WAAO,KAAK,QAAmB,OAAO,cAAc,KAAK,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,eAAe,OAA0C;AAC7D,WAAO,KAAK,QAA0B,OAAO,cAAc,KAAK,cAAc;AAAA,EAChF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,QAAQ,KAKO;AAC3B,UAAM,OAAgC,EAAE,OAAO,IAAI,MAAM;AACzD,QAAI,IAAI,KAAM,MAAK,OAAO,IAAI;AAC9B,QAAI,IAAI,QAAS,MAAK,UAAU,IAAI;AACpC,QAAI,IAAI,YAAa,MAAK,cAAc,IAAI;AAC5C,UAAM,MAAM,MAAM,KAAK,QAAyB,QAAQ,eAAe,IAAI;AAC3E,QAAI,CAAC,IAAI,oBAAoB;AAC3B,YAAM,IAAI,YAAY,KAAK,+CAA+C;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBACJ,SACiC;AACjC,QAAI,QAAQ,QAAQ;AAElB,UAAI,UAAU;AACd,uBAAiB,SAAS,KAAK,qBAAqB,OAAO,EAAG,YAAW;AACzE,aAAO;AAAA,QACL,IAAI;AAAA,QAAI,QAAQ;AAAA,QAAmB,SAAS;AAAA,QAAG,OAAO,QAAQ;AAAA,QAC9D,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,aAAa,QAAQ,GAAG,eAAe,OAAO,CAAC;AAAA,MACxF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,KAAK,QAAQ,OAAO;AAC9C,UAAM,EAAE,QAAQ,OAAO,IAAQ,mBAAmB;AAClD,UAAM,YAAY,QAAQ,cAAc;AACxC,UAAM,SAAa;AAAA,MACjB,YAAY;AAAA,MACZ;AAAA,MACA,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU;AAAA,QACtC,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ,eAAe;AAAA,QACpC,YAAY;AAAA,MACd,CAAC,CAAC;AAAA,IACJ;AACA,UAAM,OAAO;AAAA,MACX,mBAAmB,YAAY;AAAA,MAC/B,YAAY;AAAA;AAAA,MACZ,KAAK;AAAA,QACH,YAAY,OAAO;AAAA,QACnB,yBAAyB,OAAO;AAAA,QAChC,wBAAwB;AAAA,QACxB,WAAe;AAAA,MACjB;AAAA,IACF;AAEA,QAAI;AAIJ,QAAI;AACF,aAAO,MAAM,KAAK,QAAQ,QAAQ,wBAAwB,IAAI;AAAA,IAChE,SAAS,KAAK;AACZ,UAAI,eAAe,eAAe,IAAI,eAAe,KAAK;AACxD,cAAM,IAAI,YAAY,KAAK,yIAAoI;AAAA,MACjK;AACA,YAAM;AAAA,IACR;AAEA,QAAI,CAAC,KAAK,cAAe,OAAM,IAAI,YAAY,KAAK,2BAA2B;AAC/E,UAAM,QAAY,aAAa,QAAQ,QAAQ,KAAK,cAAc,sBAAsB,KAAK,cAAc,UAAU;AACrH,UAAM,SAAS,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAEzD,WAAO;AAAA,MACL,IAAI,KAAK,MAAM;AAAA,MACf,QAAQ;AAAA,MACR,SAAS,KAAK,WAAW;AAAA,MACzB,OAAO,QAAQ;AAAA,MACf,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,aAAa,SAAS,OAAO,WAAW,GAAG,GAAG,eAAe,OAAO,CAAC;AAAA,MAC5G,OAAO,KAAK,SAAS,OAAO;AAAA,MAC5B,aAAa;AAAA,QACX,QAAQ,YAAY;AAAA,QACpB,SAAS,YAAY,YAAY;AAAA,QACjC,UAAU,CAAC,CAAC,YAAY;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,qBACL,SACuC;AACvC,UAAM,cAAc,MAAM,KAAK,QAAQ,OAAO;AAC9C,UAAM,EAAE,QAAQ,OAAO,IAAQ,mBAAmB;AAClD,UAAM,QAAY,eAAe;AACjC,UAAM,YAAY,QAAQ,cAAc;AACxC,UAAM,SAAa;AAAA,MACjB,YAAY;AAAA,MACZ;AAAA,MACA,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU;AAAA,QACtC,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ,eAAe;AAAA,QACpC,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AACA,UAAM,OAAO;AAAA,MACX,mBAAmB,YAAY;AAAA,MAC/B,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,KAAK;AAAA,QACH,YAAY,OAAO;AAAA,QACnB,yBAAyB,OAAO;AAAA,QAChC,wBAAwB;AAAA,QACxB,WAAe;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AACjE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,MAAM,GAAG,KAAK,OAAO,wBAAwB;AAAA,QACxD,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,mBAAa,OAAO;AACpB,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,QAAI,CAAC,KAAK,IAAI;AACZ,mBAAa,OAAO;AACpB,UAAI,KAAK,WAAW,KAAK;AACvB,cAAM,IAAI,YAAY,KAAK,yIAAoI;AAAA,MACjK;AACA,UAAI,UAAU,KAAK;AACnB,UAAI;AACF,cAAM,IAAK,MAAM,KAAK,KAAK;AAC3B,YAAI,OAAO,EAAE,UAAU,SAAU,WAAU,EAAE;AAAA,iBACpC,EAAE,SAAS,OAAO,EAAE,UAAU,YAAY,aAAa,EAAE,MAAO,WAAU,OAAQ,EAAE,MAA+B,OAAO;AAAA,MACrI,QAAQ;AAAA,MAAe;AACvB,YAAM,IAAI,YAAY,KAAK,QAAQ,OAAO;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,QAAQ,IAAI,cAAc,KAAK;AAClD,QAAI,CAAC,MAAM,SAAS,mBAAmB,KAAK,CAAC,KAAK,MAAM;AACtD,mBAAa,OAAO;AACpB,YAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAI,CAAC,KAAK,cAAe,OAAM,IAAI,YAAY,KAAK,2BAA2B;AAC/E,YAAM,QAAY,aAAa,QAAQ,QAAQ,KAAK,cAAc,sBAAsB,KAAK,cAAc,UAAU;AACrH,YAAM,UAAW,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC,EAA2B,WAAW;AACjG,UAAI,QAAS,OAAM;AACnB;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,KAAK,UAAU;AACnC,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,gBAAgB;AACtB,UAAM,YAAY,YAA2D;AAC3E,UAAI;AACJ,YAAM,UAAU,IAAI,QAAe,CAAC,GAAG,WAAW;AAChD,YAAI,WAAW,MAAM,OAAO,IAAI,mBAAmB,8BAA8B,CAAC,GAAG,aAAa;AAAA,MACpG,CAAC;AACD,UAAI;AACF,eAAQ,MAAM,QAAQ,KAAK,CAAC,OAAO,KAAK,GAAG,OAAO,CAAC;AAAA,MACrD,UAAE;AACA,YAAI,EAAG,cAAa,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,MAAM;AACV,QAAI,cAAc;AAClB,QAAI,SAA4B;AAChC,QAAI,YAA2B;AAC/B,QAAI,WAAW;AACf,QAAI;AACF,iBAAS;AACP,YAAI,SAAU;AACd,cAAM,EAAE,OAAO,KAAK,IAAI,MAAM,UAAU;AACxC,YAAI,KAAM;AAEV,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,EAAE,QAAQ,SAAS,IAAI;AACpE,YAAI;AACJ,gBAAQ,MAAM,IAAI,QAAQ,MAAM,OAAO,IAAI;AACzC,gBAAM,MAAM,IAAI,MAAM,GAAG,GAAG;AAC5B,gBAAM,IAAI,MAAM,MAAM,CAAC;AACvB,cAAI,IAAI,SAAS,cAAc,EAAG,OAAM,IAAI,YAAY,KAAK,0BAA0B;AACvF,gBAAM,UAAU,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,IAAI;AAC5G,cAAI,CAAC,WAAW,YAAY,SAAU;AAEtC,cAAI;AACJ,cAAI;AACF,oBAAQ,KAAK,MAAM,OAAO;AAAA,UAC5B,QAAQ;AACN,kBAAM,IAAI,YAAY,KAAK,wBAAwB;AAAA,UACrD;AACA,cAAI,OAAO,MAAM,QAAQ,YAAY,CAAC,MAAM,IAAI;AAC9C,kBAAM,IAAI,YAAY,KAAK,+CAA+C;AAAA,UAC5E;AACA,cAAI,MAAM,QAAQ,YAAa,OAAM,IAAI,YAAY,KAAK,iCAAiC,WAAW,SAAS,MAAM,GAAG,GAAG;AAC3H,cAAI,MAAM,QAAQ,GAAG;AACnB,gBAAI,CAAC,MAAM,IAAK,OAAM,IAAI,YAAY,KAAK,sCAAsC;AACjF,wBAAY,MAAM;AAClB,qBAAa,aAAa,QAAQ,SAAS;AAAA,UAC7C;AACA,gBAAM,UAAU,MAAM,UAAU;AAChC,gBAAM,OAAO,IAAI,YAAY,EAAE;AAAA,YACzB,gBAAgB,QAAsB,QAAQ,WAAqB,OAAO,MAAM,KAAK,SAAS,MAAM,EAAE;AAAA,UAC5G;AACA,cAAI,KAAM,OAAM;AAChB;AACA,cAAI,SAAS;AAAE,uBAAW;AAAM;AAAA,UAAO;AAAA,QACzC;AAAA,MACF;AAAA,IACF,UAAE;AACA,mBAAa,OAAO;AACpB,UAAI;AAAE,cAAM,OAAO,OAAO;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IACtD;AACA,QAAI,CAAC,SAAU,OAAM,IAAI,YAAY,KAAK,6CAA6C;AAAA,EACzF;AAAA;AAAA,EAIA,MAAc,sBACZ,QACA,MACA,QACA,MACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,UAAM,aAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,kBAAkB,OAAO;AAAA,MACzB,gBAAgB,OAAO,SAAS;AAAA,MAChC,oBAAoB,OAAO;AAAA,MAC3B,oBAAoB,OAAO;AAAA,MAC3B,gBAAgB,OAAO;AAAA,IACzB;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,QACT,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,iBAC9B,OAAO,OAAO,QAAQ,YAAY,aAAa;AACtD,oBAAU,OAAQ,IAA6B,OAAO;AAAA,MAC1D,QAAQ;AAAA,MAER;AAEA,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI,aAAa,SAAS,QAAQ,SAAS,SAAS;AAAA,MAC5D;AACA,UAAI,SAAS,WAAW,KAAK;AAC3B,cAAM,IAAI,iBAAiB,SAAS,SAAS;AAAA,MAC/C;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,QAAI,SAAS,WAAW,IAAK,QAAO,CAAC;AACrC,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAc,mBACZ,QACA,MACA,MACA,eACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAE/D,UAAM,aAAqC,EAAE,GAAG,KAAK,QAAQ;AAC7D,QAAI,eAAe;AACjB,iBAAW,WAAW,IAAI;AAAA,IAC5B;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA,SAAS;AAAA,QACT,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,mBAAmB,cAAc,GAAG,YAAY;AAAA,MAC5D;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,eAAgB,MAAM,SAAS,KAAK;AAC1C,YAAM,IAAI,YAAY,KAAK,oBAAoB,YAAY;AAAA,IAC7D;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI;AACJ,UAAI,UAAU,SAAS;AACvB,UAAI;AACF,oBAAa,MAAM,SAAS,KAAK;AACjC,cAAM,MAAM,WAAW;AACvB,YAAI,OAAO,QAAQ,SAAU,WAAU;AAAA,MACzC,QAAQ;AAAA,MAER;AACA,YAAM,IAAI,YAAY,SAAS,QAAQ,SAAS,SAAS;AAAA,IAC3D;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA,EAIA,MAAM,gBACJ,QACA,SACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,gBACJ,eACA,OACA,SACgC;AAChC,UAAM,OAAgC,EAAE,MAAM;AAC9C,QAAI,SAAS,aAAc,MAAK,gBAAgB,QAAQ;AACxD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,mBAAmB,aAAa,CAAC;AAAA,MACrD;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,SAIc;AACjC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,MAAO,QAAO,IAAI,SAAS,QAAQ,KAAK;AACrD,QAAI,SAAS,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAClE,QAAI,SAAS,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACrE,UAAM,KAAK,OAAO,SAAS;AAC3B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,mBAAmB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,aAA6C;AAC9D,WAAO,KAAK,QAAuB,OAAO,oBAAoB,WAAW,EAAE;AAAA,EAC7E;AAAA,EAEA,MAAM,gBACJ,aACA,QAC2C;AAC3C,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,WAAW;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,iBACJ,aACA,QACA,SACgC;AAChC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,QAAQ,KAAM,QAAO,IAAI,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAClE,QAAI,SAAS,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACrE,UAAM,KAAK,OAAO,SAAS;AAC3B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,oBAAoB,WAAW,QAAQ,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@singularity-layer/grid",
3
- "version": "0.2.0",
4
- "description": "TypeScript SDK for the SGL Network confidential compute grid",
3
+ "version": "0.4.0",
4
+ "description": "TypeScript SDK for the SGL Network confidential compute grid (end-to-end encrypted + streaming)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -45,6 +45,12 @@
45
45
  "engines": {
46
46
  "node": ">=18"
47
47
  },
48
+ "dependencies": {
49
+ "@noble/ciphers": "^1.0.0",
50
+ "@noble/curves": "^1.6.0",
51
+ "@noble/hashes": "^1.5.0",
52
+ "bs58": "^6.0.0"
53
+ },
48
54
  "devDependencies": {
49
55
  "tsup": "^8.0.0",
50
56
  "typescript": "^5.4.0"