@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 +1 -1
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +318 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +308 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
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
|
-
|
|
151
|
-
"
|
|
152
|
-
|
|
153
|
-
|
|
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":"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","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
|
-
|
|
119
|
-
"
|
|
120
|
-
|
|
121
|
-
|
|
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) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -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":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singularity-layer/grid",
|
|
3
|
-
"version": "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"
|