@tangle-network/sandbox 0.60.16-develop.20261006055839.0a30657 → 0.60.16

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/dist/index.d.ts CHANGED
@@ -870,8 +870,8 @@ declare const manageSandboxesInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
870
870
  off: "off";
871
871
  }>>;
872
872
  logs: z.ZodOptional<z.ZodEnum<{
873
- on: "on";
874
873
  off: "off";
874
+ on: "on";
875
875
  }>>;
876
876
  }, z.core.$strict>>;
877
877
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -1199,8 +1199,8 @@ declare const manageSandboxesInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1199
1199
  off: "off";
1200
1200
  }>>;
1201
1201
  logs: z.ZodOptional<z.ZodEnum<{
1202
- on: "on";
1203
1202
  off: "off";
1203
+ on: "on";
1204
1204
  }>>;
1205
1205
  }, z.core.$strict>>;
1206
1206
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -1538,8 +1538,8 @@ declare const manageSandboxesInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1538
1538
  off: "off";
1539
1539
  }>>;
1540
1540
  logs: z.ZodOptional<z.ZodEnum<{
1541
- on: "on";
1542
1541
  off: "off";
1542
+ on: "on";
1543
1543
  }>>;
1544
1544
  }, z.core.$strict>>;
1545
1545
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -1862,8 +1862,8 @@ declare const manageSandboxesInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1862
1862
  off: "off";
1863
1863
  }>>;
1864
1864
  logs: z.ZodOptional<z.ZodEnum<{
1865
- on: "on";
1866
1865
  off: "off";
1866
+ on: "on";
1867
1867
  }>>;
1868
1868
  }, z.core.$strict>>;
1869
1869
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -2191,8 +2191,8 @@ declare const manageSandboxesInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
2191
2191
  off: "off";
2192
2192
  }>>;
2193
2193
  logs: z.ZodOptional<z.ZodEnum<{
2194
- on: "on";
2195
2194
  off: "off";
2195
+ on: "on";
2196
2196
  }>>;
2197
2197
  }, z.core.$strict>>;
2198
2198
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -2625,8 +2625,8 @@ declare const MANAGE_SANDBOXES_PARAMETERS: z.core.ZodStandardJSONSchemaPayload<z
2625
2625
  off: "off";
2626
2626
  }>>;
2627
2627
  logs: z.ZodOptional<z.ZodEnum<{
2628
- on: "on";
2629
2628
  off: "off";
2629
+ on: "on";
2630
2630
  }>>;
2631
2631
  }, z.core.$strict>>;
2632
2632
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -2954,8 +2954,8 @@ declare const MANAGE_SANDBOXES_PARAMETERS: z.core.ZodStandardJSONSchemaPayload<z
2954
2954
  off: "off";
2955
2955
  }>>;
2956
2956
  logs: z.ZodOptional<z.ZodEnum<{
2957
- on: "on";
2958
2957
  off: "off";
2958
+ on: "on";
2959
2959
  }>>;
2960
2960
  }, z.core.$strict>>;
2961
2961
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -3293,8 +3293,8 @@ declare const MANAGE_SANDBOXES_PARAMETERS: z.core.ZodStandardJSONSchemaPayload<z
3293
3293
  off: "off";
3294
3294
  }>>;
3295
3295
  logs: z.ZodOptional<z.ZodEnum<{
3296
- on: "on";
3297
3296
  off: "off";
3297
+ on: "on";
3298
3298
  }>>;
3299
3299
  }, z.core.$strict>>;
3300
3300
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -3617,8 +3617,8 @@ declare const MANAGE_SANDBOXES_PARAMETERS: z.core.ZodStandardJSONSchemaPayload<z
3617
3617
  off: "off";
3618
3618
  }>>;
3619
3619
  logs: z.ZodOptional<z.ZodEnum<{
3620
- on: "on";
3621
3620
  off: "off";
3621
+ on: "on";
3622
3622
  }>>;
3623
3623
  }, z.core.$strict>>;
3624
3624
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -3946,8 +3946,8 @@ declare const MANAGE_SANDBOXES_PARAMETERS: z.core.ZodStandardJSONSchemaPayload<z
3946
3946
  off: "off";
3947
3947
  }>>;
3948
3948
  logs: z.ZodOptional<z.ZodEnum<{
3949
- on: "on";
3950
3949
  off: "off";
3950
+ on: "on";
3951
3951
  }>>;
3952
3952
  }, z.core.$strict>>;
3953
3953
  idempotencyKey: z.ZodOptional<z.ZodString>;
@@ -181,66 +181,6 @@ interface SessionSummaryList {
181
181
  items: SessionSummary[];
182
182
  nextCursor: string | null;
183
183
  }
184
- /**
185
- * One agent session file the platform captured from a cloud sandbox of a run
186
- * (`GET /v1/run-sessions/{runId}`). Files outlive the sandbox; the stored
187
- * bytes are redacted, and `sourceSha256` names the bytes as captured.
188
- */
189
- interface RunSessionFile {
190
- runId: string;
191
- sandboxId: string;
192
- /** The sidecar session the file was captured from. */
193
- sessionId: string;
194
- /** `native/<executionId>/<attempt>/<storePath>`, `events/<executionId>.ndjson` or `capture.json`. */
195
- path: string;
196
- harness: string | null;
197
- format: string | null;
198
- contentType: string;
199
- /** `withheld`: captured but not stored (`unscannable_binary` or `too_large`). */
200
- state: "stored" | "withheld";
201
- withheldReason: string | null;
202
- sourceSha256: string;
203
- sourceBytes: number;
204
- storedSha256: string | null;
205
- storedBytes: number | null;
206
- redactionVersion: string | null;
207
- redactionCount: number | null;
208
- updatedAt: string;
209
- }
210
- /** The final capture of one sandbox, sealed before the sandbox was deleted. */
211
- interface RunSessionSeal {
212
- runId: string;
213
- sandboxId: string;
214
- /** Whether the sandbox was running when its final capture was taken. */
215
- runtime: "running" | "not_running";
216
- /** True when the final capture named no gap. */
217
- complete: boolean;
218
- missing: string[];
219
- storedFiles: number;
220
- withheldFiles: number;
221
- storedBytes: number;
222
- workspaceSnapshot: string | null;
223
- sealedAt: string;
224
- }
225
- /**
226
- * One page of a run's captured files (`GET /v1/run-sessions/{runId}`), in
227
- * sandbox, session and path order. Every sandbox's seal comes with the first
228
- * page; later pages carry an empty `seals`.
229
- */
230
- interface RunSessionList {
231
- runId: string;
232
- files: RunSessionFile[];
233
- seals: RunSessionSeal[];
234
- /** Pass as `cursor` for the next page; null on the last page. */
235
- nextCursor: string | null;
236
- }
237
- /** Query params for {@link IntelligenceClient.listRunSessionFiles}. */
238
- interface ListRunSessionFilesParams {
239
- /** Files per page, clamped server-side to `[1, 5000]` (default 1000). */
240
- limit?: number;
241
- /** Opaque cursor from the prior page's `nextCursor`. */
242
- cursor?: string;
243
- }
244
184
  /** Query params for {@link IntelligenceClient.listTraces}. */
245
185
  interface ListTracesParams {
246
186
  /** Page size, clamped server-side to `[1, 200]` (default 50). */
@@ -320,20 +260,6 @@ interface IntelligenceClient {
320
260
  getRun(runId: string): Promise<Run>;
321
261
  /** List the spans pivoted to this run (by `run_id`), bounded window. */
322
262
  getRunTraces(runId: string): Promise<RunSpanWindow>;
323
- /**
324
- * List one page of the agent session files the platform captured from this
325
- * run's cloud sandboxes; the first page also carries each sandbox's final
326
- * seal. A run is the sandbox's create `metadata.run`, or the sandbox id when
327
- * the sandbox named no run.
328
- */
329
- listRunSessionFiles(runId: string, params?: ListRunSessionFilesParams): Promise<RunSessionList>;
330
- /**
331
- * Async iterator over every captured file of a run, following `nextCursor`
332
- * page by page. Seals are on the first page of {@link listRunSessionFiles}.
333
- */
334
- iterateRunSessionFiles(runId: string, params?: ListRunSessionFilesParams): AsyncGenerator<RunSessionFile, void, unknown>;
335
- /** Download one captured file's stored (redacted) bytes. */
336
- downloadRunSessionFile(runId: string, file: Pick<RunSessionFile, "sandboxId" | "sessionId" | "path">): Promise<Uint8Array>;
337
263
  /** List tenant-wide session summaries (one row per session), newest first. */
338
264
  listSessions(params?: ListSessionsParams): Promise<SessionSummaryList>;
339
265
  /** List one session's traces as the trace-summary shape, keyset-paginated. */
@@ -363,4 +289,4 @@ interface IntelligenceClient {
363
289
  */
364
290
  declare function createIntelligenceClient(config: IntelligenceClientConfig): IntelligenceClient;
365
291
  //#endregion
366
- export { DEFAULT_INTELLIGENCE_BASE_URL, GetSessionTracesParams, GetTraceSpansParams, IntelligenceClient, IntelligenceClientConfig, ListRunSessionFilesParams, ListRunsParams, ListSessionsParams, ListTracesParams, NanoString, Run, RunList, RunSessionFile, RunSessionList, RunSessionSeal, RunSpanWindow, SerializedSpan, SessionSummary, SessionSummaryList, SpanPage, TraceStatus, TraceSummary, TraceSummaryList, createIntelligenceClient };
292
+ export { DEFAULT_INTELLIGENCE_BASE_URL, GetSessionTracesParams, GetTraceSpansParams, IntelligenceClient, IntelligenceClientConfig, ListRunsParams, ListSessionsParams, ListTracesParams, NanoString, Run, RunList, RunSpanWindow, SerializedSpan, SessionSummary, SessionSummaryList, SpanPage, TraceStatus, TraceSummary, TraceSummaryList, createIntelligenceClient };
@@ -67,51 +67,37 @@ function createIntelligenceClient(config) {
67
67
  * an `AbortController` timeout that surfaces as `TimeoutError`, and a
68
68
  * transport failure wrapped as `NetworkError`. Never reuses the sandbox
69
69
  * `baseUrl` — this is the intelligence host.
70
- *
71
- * The timeout covers `read` as well, so a server that sends its headers
72
- * and then stalls the body still times out. A caller that hands the body
73
- * stream on (the NDJSON export) returns the response from `read` and owns
74
- * the stream's pace from there.
75
70
  */
76
- async function request(path, read) {
71
+ async function request(path) {
77
72
  const url = `${baseUrl}${path}`;
78
73
  const controller = new AbortController();
79
74
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
80
75
  try {
81
- let response;
82
- try {
83
- response = await globalThis.fetch(url, {
84
- headers: {
85
- Authorization: `Bearer ${apiKey}`,
86
- Accept: "application/json"
87
- },
88
- signal: controller.signal
89
- });
90
- } catch (err) {
91
- if (controller.signal.aborted || err instanceof Error && err.name === "AbortError") throw new TimeoutError(timeoutMs);
92
- throw new NetworkError(`Failed to connect to Intelligence API: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0, {
93
- endpoint: path,
94
- origin: "sandbox-api"
95
- });
96
- }
97
- return await read(response);
76
+ return await globalThis.fetch(url, {
77
+ headers: {
78
+ Authorization: `Bearer ${apiKey}`,
79
+ Accept: "application/json"
80
+ },
81
+ signal: controller.signal
82
+ });
98
83
  } catch (err) {
99
- if (controller.signal.aborted && !(err instanceof TimeoutError)) throw new TimeoutError(timeoutMs);
100
- throw err;
84
+ if (err instanceof Error && err.name === "AbortError") throw new TimeoutError(timeoutMs);
85
+ throw new NetworkError(`Failed to connect to Intelligence API: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0, {
86
+ endpoint: path,
87
+ origin: "sandbox-api"
88
+ });
101
89
  } finally {
102
90
  clearTimeout(timeoutId);
103
91
  }
104
92
  }
105
93
  /** GET a JSON route, raising the SDK's typed error on a non-2xx response. */
106
94
  async function getJson(path) {
107
- const body = await request(path, async (response) => {
108
- const text = await response.text();
109
- if (!response.ok) throw parseErrorResponse(response.status, text, {
110
- method: "GET",
111
- path
112
- }, response.headers);
113
- return text;
114
- });
95
+ const response = await request(path);
96
+ const body = await response.text();
97
+ if (!response.ok) throw parseErrorResponse(response.status, body, {
98
+ method: "GET",
99
+ path
100
+ }, response.headers);
115
101
  return JSON.parse(body);
116
102
  }
117
103
  async function listTraces(params = {}) {
@@ -135,7 +121,7 @@ function createIntelligenceClient(config) {
135
121
  }
136
122
  async function exportTraceSpansNdjson(traceId) {
137
123
  const path = `/v1/traces/${encodeURIComponent(traceId)}/spans.ndjson`;
138
- const response = await request(path, async (served) => served);
124
+ const response = await request(path);
139
125
  if (!response.ok) {
140
126
  const body = await response.text();
141
127
  throw parseErrorResponse(response.status, body, {
@@ -168,40 +154,6 @@ function createIntelligenceClient(config) {
168
154
  async function getRunTraces(runId) {
169
155
  return getJson(`/v1/runs/${encodeURIComponent(runId)}/traces`);
170
156
  }
171
- async function listRunSessionFiles(runId, params = {}) {
172
- const search = new URLSearchParams();
173
- appendParam(search, "limit", params.limit);
174
- appendParam(search, "cursor", params.cursor);
175
- const suffix = search.size > 0 ? `?${search}` : "";
176
- return getJson(`/v1/run-sessions/${encodeURIComponent(runId)}${suffix}`);
177
- }
178
- async function* iterateRunSessionFiles(runId, params = {}) {
179
- let cursor = params.cursor;
180
- for (;;) {
181
- const page = await listRunSessionFiles(runId, {
182
- ...params,
183
- cursor
184
- });
185
- for (const file of page.files) yield file;
186
- if (!page.nextCursor) return;
187
- cursor = page.nextCursor;
188
- }
189
- }
190
- async function downloadRunSessionFile(runId, file) {
191
- const search = new URLSearchParams({
192
- sandboxId: file.sandboxId,
193
- sessionId: file.sessionId,
194
- path: file.path
195
- });
196
- const path = `/v1/run-sessions/${encodeURIComponent(runId)}/content?${search}`;
197
- return request(path, async (response) => {
198
- if (!response.ok) throw parseErrorResponse(response.status, await response.text(), {
199
- method: "GET",
200
- path
201
- }, response.headers);
202
- return new Uint8Array(await response.arrayBuffer());
203
- });
204
- }
205
157
  async function listSessions(params = {}) {
206
158
  const search = new URLSearchParams();
207
159
  appendParam(search, "limit", params.limit);
@@ -262,9 +214,6 @@ function createIntelligenceClient(config) {
262
214
  listRuns,
263
215
  getRun,
264
216
  getRunTraces,
265
- listRunSessionFiles,
266
- iterateRunSessionFiles,
267
- downloadRunSessionFile,
268
217
  listSessions,
269
218
  getSessionTraces,
270
219
  iterateTraceSpans,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox",
3
- "version": "0.60.16-develop.20261006055839.0a30657",
3
+ "version": "0.60.16",
4
4
  "description": "Client SDK for the Tangle Sandbox platform - build AI agent applications with dev containers",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -151,9 +151,9 @@
151
151
  "ws": "^8.20.0",
152
152
  "yjs": "13.6.30",
153
153
  "@products/sandbox-test-utils": "0.0.0",
154
- "@repo/shared": "0.0.0",
155
154
  "@tangle-network/agent-profile-materialize": "0.20.9",
156
155
  "@tangle-network/cli-agent-registry": "0.5.0",
156
+ "@repo/shared": "0.0.0",
157
157
  "@tangle-network/runtime-contracts": "0.14.0",
158
158
  "@tangle-network/sdk": "3.3.41",
159
159
  "@tangle-network/sse-core": "0.0.0"
@@ -1,503 +0,0 @@
1
- //#region src/gpu-lease/intent.d.ts
2
- declare const INTENT_VERSION = 1;
3
- /** keccak256 of the canonical intent string. */
4
- declare function gpuLeaseIntentHash(input: {
5
- intentVersion: number;
6
- gpuClass: string;
7
- durationSeconds: bigint | number;
8
- confidentiality: number;
9
- region: string;
10
- }): `0x${string}`;
11
- /** ABI shape of GpuLeaseRequest — mirrors the blueprint's sol! type. */
12
- declare const GPU_LEASE_REQUEST_ABI: never;
13
- interface GpuLeaseRequestParams {
14
- intentVersion: number;
15
- intentHash: `0x${string}`;
16
- pricePerSecondWei: bigint;
17
- durationSeconds: bigint;
18
- confidentiality: number;
19
- gpuClass: string;
20
- region: string;
21
- lessee: `0x${string}`;
22
- leaseId: `0x${string}`;
23
- }
24
- /**
25
- * ABI-encoded GpuLeaseRequest — the LEASE job calldata payload.
26
- * Positional (ABI order) — order is pinned by the blueprint's generated
27
- * encodeType in metadata/blueprint.json.
28
- */
29
- declare function encodeGpuLeaseRequest(params: GpuLeaseRequestParams): `0x${string}`;
30
- //#endregion
31
- //#region src/gpu-lease/types.d.ts
32
- /**
33
- * GPU lease client types — mirrors the gpu-lease-blueprint operator API
34
- * (gpu-lease-blueprint/gpu-lease-blueprint-lib/src/api.rs) and the on-chain
35
- * ABI (contracts/src/GpuLeaseBlueprint.sol sol! mirror).
36
- *
37
- * Every on-chain and off-chain surface is schemaVersioned; unknown versions
38
- * fail closed (SPEC §3 of the blueprint).
39
- */
40
- /** The only schema this client understands. */
41
- declare const GPU_LEASE_SCHEMA_VERSION: 1;
42
- interface ClassCapability {
43
- id: string;
44
- tee: boolean;
45
- unit: string;
46
- /** Indicative policy price (wei/second) — the binding price is the quote. */
47
- pricePerSecond: string;
48
- idleCount: number;
49
- }
50
- interface Capabilities {
51
- schemaVersion: number;
52
- compute: {
53
- kind: string;
54
- classes: ClassCapability[];
55
- };
56
- }
57
- interface OperatorQuote {
58
- schemaVersion: number;
59
- gpuClass: string;
60
- /** The operator's EVM address — who the escrow names (I5 binding).
61
- * When the quote is signed this MUST equal the recovered signer (verified
62
- * in quotes.ts); config-supplied values are interim-trust only. */
63
- operatorAddress: `0x${string}`;
64
- /** EIP-191 personal_sign over the canonical quote payload (0x RSV). */
65
- signature?: `0x${string}`;
66
- /** wei/second, from the operator's signed policy at request time. */
67
- pricePerSecond: string;
68
- durationSeconds: number;
69
- confidentiality: number;
70
- /** wei — pricePerSecond × durationSeconds exactly. */
71
- escrowTotal: string;
72
- /** Utilization the price was struck at (bps of 10_000) — the surge "why". */
73
- utilizationBps: number;
74
- /** keccak256(canonical intent) — binds the RFQ and the vault lease (I5). */
75
- intentHash: `0x${string}`;
76
- /** Unix seconds after which the quote is stale. */
77
- validUntil: number;
78
- }
79
- interface LeaseStatus {
80
- schemaVersion: number;
81
- leaseId: `0x${string}`;
82
- deviceId: string;
83
- lessee: string;
84
- /** Unix seconds — the chain-of-truth session end (parking meter). */
85
- expiresAt: number;
86
- endpoint: string;
87
- }
88
- interface SessionChallenge {
89
- nonce: `0x${string}`;
90
- /** The exact string the lessee must EIP-191 personal_sign. */
91
- message: string;
92
- expiresAt: number;
93
- }
94
- interface SessionCredential {
95
- token: string;
96
- leaseId: `0x${string}`;
97
- scopes: string[];
98
- expiresAt: number;
99
- }
100
- /** A validated, ready-to-escrow quote. */
101
- interface ValidatedQuote {
102
- quote: OperatorQuote;
103
- pricePerSecondWei: bigint;
104
- escrowTotalWei: bigint;
105
- }
106
- /** A resolved lease handed to the caller by `gpuLease()`. */
107
- interface GpuLease {
108
- leaseId: `0x${string}`;
109
- /** Public endpoint descriptor (versioned union; v1 = local-attach). */
110
- endpoint: string;
111
- schemaVersion: number;
112
- quote: ValidatedQuote;
113
- }
114
- interface RefundReceipt {
115
- leaseId: `0x${string}`;
116
- /** Exact wei refunded: pricePerSecond × remainingSeconds at settle time. */
117
- refundWei: bigint;
118
- operatorTakeWei: bigint;
119
- }
120
- //#endregion
121
- //#region src/gpu-lease/operators.d.ts
122
- interface QuoteParams {
123
- gpuClass: string;
124
- durationSeconds: number;
125
- confidentiality?: number;
126
- region?: string;
127
- }
128
- interface OperatorEndpoint {
129
- /** Operator base URL, e.g. https://gpu.example.com */
130
- baseUrl: string;
131
- /** The operator's EVM address (escrow + job binding). */
132
- address: `0x${string}`;
133
- fetch?: typeof fetch;
134
- }
135
- /** JSON request body — named at the I/O boundary (anti-slop: no `unknown`). */
136
- type JsonRequestBody = Record<string, unknown>;
137
- declare class GpuLeaseOperatorClient {
138
- private readonly endpoint;
139
- constructor(endpoint: OperatorEndpoint);
140
- capabilities(): Promise<Capabilities>;
141
- quote(params: QuoteParams): Promise<OperatorQuote>;
142
- leaseStatus(leaseId: `0x${string}`): Promise<LeaseStatus>;
143
- sessionChallenge(leaseId: `0x${string}`, lessee: string): Promise<SessionChallenge>;
144
- sessionVerify(nonce: `0x${string}`, signature: `0x${string}`): Promise<SessionCredential>;
145
- }
146
- //#endregion
147
- //#region src/gpu-lease/output.d.ts
148
- interface GpuLeaseOutputDecoded {
149
- leaseId: `0x${string}`;
150
- endpoint: string;
151
- schemaVersion: number;
152
- }
153
- /** Decode + fail closed on unknown schemaVersion (blueprint SPEC §3). */
154
- declare function decodeGpuLeaseOutput(encoded: `0x${string}`): GpuLeaseOutputDecoded;
155
- //#endregion
156
- //#region src/gpu-lease/quotes.d.ts
157
- /** The exact string the operator signs — mirrors the blueprint's
158
- * `canonical_quote_payload` byte-for-byte. Every priced field is bound. */
159
- declare function canonicalQuotePayload(quote: {
160
- gpuClass: string;
161
- pricePerSecond: string;
162
- durationSeconds: number;
163
- confidentiality: number;
164
- escrowTotal: string;
165
- intentHash: string;
166
- validUntil: number;
167
- }): string;
168
- /** Offline EIP-191 recovery over the canonical payload. */
169
- declare function verifyOperatorQuote(quote: OperatorQuote): Promise<`0x${string}`>;
170
- type QuoteRejection = {
171
- kind: "stale";
172
- validUntil: number;
173
- now: number;
174
- } | {
175
- kind: "class-mismatch";
176
- requested: string;
177
- quoted: string;
178
- } | {
179
- kind: "escrow-inconsistent";
180
- expected: bigint;
181
- actual: bigint;
182
- } | {
183
- kind: "confidentiality-mismatch";
184
- requested: number;
185
- quoted: number;
186
- } | {
187
- kind: "signature-mismatch";
188
- claimed: string;
189
- recovered: string;
190
- } | {
191
- kind: "unsigned";
192
- operator: string;
193
- };
194
- interface QuoteFilterInput {
195
- requestedClass: string;
196
- requestedDurationSeconds: number;
197
- requestedConfidentiality: number;
198
- now?: () => number;
199
- }
200
- /**
201
- * Validate one quote. Fail-closed on every field the RFQ binds:
202
- * class, duration-derived escrow, confidentiality, and expiry.
203
- */
204
- declare function validateQuote(quote: OperatorQuote, input: QuoteFilterInput): ValidatedQuote | QuoteRejection;
205
- /**
206
- * Collect validated quotes from N operators and rank cheapest-first.
207
- * Operators that fail validation (or whose fetch rejects) are reported,
208
- * never silently dropped — the buyer sees why an operator lost.
209
- */
210
- declare function selectQuotes(quotes: {
211
- operator: string;
212
- quote: OperatorQuote;
213
- }[], input: QuoteFilterInput): {
214
- ranked: {
215
- operator: string;
216
- validated: ValidatedQuote;
217
- }[];
218
- rejected: {
219
- operator: string;
220
- reason: QuoteRejection;
221
- }[];
222
- };
223
- /**
224
- * Enforce the signed RFQ envelope when present:
225
- * - signature present => it MUST recover to the claimed operator address;
226
- * invalid signature => reject (config-trust is dead on this path).
227
- * - no signature => the endpoint-config address is used (interim fallback);
228
- * strict mode rejects unsigned quotes outright.
229
- */
230
- declare function validateQuoteIdentity(quote: OperatorQuote, opts?: {
231
- strict?: boolean;
232
- }): Promise<ValidatedQuote | QuoteRejection>;
233
- //#endregion
234
- //#region src/gpu-lease/vault.d.ts
235
- declare const GPU_LEASE_VAULT_ABI: readonly [{
236
- readonly name: "create";
237
- readonly type: "function";
238
- readonly stateMutability: "payable";
239
- readonly inputs: readonly [{
240
- readonly type: "address";
241
- readonly name: "operator";
242
- }, {
243
- readonly type: "uint128";
244
- readonly name: "pricePerSecond";
245
- }, {
246
- readonly type: "uint64";
247
- readonly name: "durationSeconds";
248
- }, {
249
- readonly type: "bytes32";
250
- readonly name: "intentHash";
251
- }, {
252
- readonly type: "uint8";
253
- readonly name: "confidentiality";
254
- }, {
255
- readonly type: "bytes";
256
- readonly name: "endpointInfo";
257
- }];
258
- readonly outputs: readonly [{
259
- readonly type: "bytes32";
260
- readonly name: "leaseId";
261
- }];
262
- }, {
263
- readonly name: "extend";
264
- readonly type: "function";
265
- readonly stateMutability: "payable";
266
- readonly inputs: readonly [{
267
- readonly type: "bytes32";
268
- readonly name: "leaseId";
269
- }, {
270
- readonly type: "uint64";
271
- readonly name: "addSeconds";
272
- }];
273
- readonly outputs: readonly [];
274
- }, {
275
- readonly name: "release";
276
- readonly type: "function";
277
- readonly stateMutability: "nonpayable";
278
- readonly inputs: readonly [{
279
- readonly type: "bytes32";
280
- readonly name: "leaseId";
281
- }];
282
- readonly outputs: readonly [];
283
- }, {
284
- readonly name: "reap";
285
- readonly type: "function";
286
- readonly stateMutability: "nonpayable";
287
- readonly inputs: readonly [{
288
- readonly type: "bytes32";
289
- readonly name: "leaseId";
290
- }];
291
- readonly outputs: readonly [];
292
- }, {
293
- readonly name: "withdrawEarnings";
294
- readonly type: "function";
295
- readonly stateMutability: "nonpayable";
296
- readonly inputs: readonly [];
297
- readonly outputs: readonly [];
298
- }, {
299
- readonly name: "totalEscrowed";
300
- readonly type: "function";
301
- readonly stateMutability: "view";
302
- readonly inputs: readonly [];
303
- readonly outputs: readonly [{
304
- readonly type: "uint256";
305
- }];
306
- }, {
307
- readonly name: "operatorEarnings";
308
- readonly type: "function";
309
- readonly stateMutability: "view";
310
- readonly inputs: readonly [];
311
- readonly outputs: readonly [{
312
- readonly type: "uint256";
313
- }];
314
- }, {
315
- readonly name: "leases";
316
- readonly type: "function";
317
- readonly stateMutability: "view";
318
- readonly inputs: readonly [{
319
- readonly type: "bytes32";
320
- }];
321
- readonly outputs: readonly [{
322
- readonly type: "address";
323
- readonly name: "operator";
324
- }, {
325
- readonly type: "address";
326
- readonly name: "lessee";
327
- }, {
328
- readonly type: "uint128";
329
- readonly name: "escrow";
330
- }, {
331
- readonly type: "uint128";
332
- readonly name: "pricePerSecond";
333
- }, {
334
- readonly type: "uint64";
335
- readonly name: "expiry";
336
- }, {
337
- readonly type: "bytes32";
338
- readonly name: "intentHash";
339
- }, {
340
- readonly type: "uint8";
341
- readonly name: "confidentiality";
342
- }, {
343
- readonly type: "uint8";
344
- readonly name: "state";
345
- }];
346
- }, {
347
- readonly name: "LeaseCreated";
348
- readonly type: "event";
349
- readonly inputs: readonly [{
350
- readonly type: "bytes32";
351
- readonly name: "leaseId";
352
- readonly indexed: true;
353
- }, {
354
- readonly type: "address";
355
- readonly name: "operator";
356
- readonly indexed: true;
357
- }, {
358
- readonly type: "address";
359
- readonly name: "lessee";
360
- readonly indexed: true;
361
- }, {
362
- readonly type: "uint128";
363
- readonly name: "escrow";
364
- }, {
365
- readonly type: "uint128";
366
- readonly name: "pricePerSecond";
367
- }, {
368
- readonly type: "uint64";
369
- readonly name: "expiry";
370
- }, {
371
- readonly type: "bytes32";
372
- readonly name: "intentHash";
373
- }, {
374
- readonly type: "uint8";
375
- readonly name: "confidentiality";
376
- }, {
377
- readonly type: "bytes";
378
- readonly name: "endpointInfo";
379
- }, {
380
- readonly type: "uint16";
381
- readonly name: "schemaVersion";
382
- }];
383
- }, {
384
- readonly name: "LeaseReleased";
385
- readonly type: "event";
386
- readonly inputs: readonly [{
387
- readonly type: "bytes32";
388
- readonly name: "leaseId";
389
- readonly indexed: true;
390
- }, {
391
- readonly type: "uint128";
392
- readonly name: "refund";
393
- }, {
394
- readonly type: "uint128";
395
- readonly name: "operatorTake";
396
- }];
397
- }, {
398
- readonly name: "LeaseReaped";
399
- readonly type: "event";
400
- readonly inputs: readonly [{
401
- readonly type: "bytes32";
402
- readonly name: "leaseId";
403
- readonly indexed: true;
404
- }, {
405
- readonly type: "uint128";
406
- readonly name: "operatorTake";
407
- }, {
408
- readonly type: "address";
409
- readonly name: "caller";
410
- }];
411
- }];
412
- /** Vault lease states (mirrors the contract). */
413
- declare const LEASE_STATE: {
414
- readonly LIVE: 0;
415
- readonly RELEASED: 1;
416
- readonly REAPED: 2;
417
- };
418
- interface VaultLeaseView {
419
- operator: `0x${string}`;
420
- lessee: `0x${string}`;
421
- escrowWei: bigint;
422
- pricePerSecondWei: bigint;
423
- /** Unix seconds — the chain clock is the parking meter's truth. */
424
- expiry: bigint;
425
- intentHash: `0x${string}`;
426
- confidentiality: number;
427
- state: number;
428
- }
429
- /**
430
- * Exact refund projection (I3): pricePerSecond × max(expiry − now, 0).
431
- * Exact by construction because escrow === price × paidSeconds in the vault.
432
- */
433
- declare function projectRefundWei(lease: VaultLeaseView, nowSeconds: bigint): {
434
- refundWei: bigint;
435
- operatorTakeWei: bigint;
436
- };
437
- /** I2 from the client side: unpayable time can never be requested. */
438
- declare function maxAffordableSeconds(escrowWei: bigint, pricePerSecondWei: bigint): bigint;
439
- //#endregion
440
- //#region src/gpu-lease/resolver.d.ts
441
- /** Job ids — MUST match the blueprint router (sequential, no gaps). */
442
- declare const GPU_LEASE_JOBS: {
443
- readonly LEASE: 0;
444
- readonly RELEASE: 1;
445
- readonly EXTEND: 2;
446
- readonly REAP: 3;
447
- };
448
- interface GpuLeaseChain {
449
- /** The buyer's address (the RFQ binds the requester — I5). */
450
- readonly account: `0x${string}`;
451
- /** Create the escrowed lease on the vault; returns the vault leaseId. */
452
- createVaultLease(input: {
453
- operator: `0x${string}`;
454
- pricePerSecondWei: bigint;
455
- durationSeconds: bigint;
456
- intentHash: `0x${string}`;
457
- confidentiality: number;
458
- endpointInfo: string;
459
- escrowWei: bigint;
460
- }): Promise<`0x${string}`>;
461
- /** Submit a job through tnt-core; resolves with the result payload. */
462
- submitJob(jobId: number, encodedInput: `0x${string}`): Promise<`0x${string}`>;
463
- /** Read a vault lease (chain clock truth for projections). */
464
- readLease(leaseId: `0x${string}`): Promise<VaultLeaseView>;
465
- /** The chain's current timestamp (unix seconds) — meter truth, not Date.now(). */
466
- chainNow?(): Promise<bigint>;
467
- /** Voluntary release on the vault; returns {refund, take} wei. */
468
- releaseLease(leaseId: `0x${string}`): Promise<{
469
- refundWei: bigint;
470
- operatorTakeWei: bigint;
471
- }>;
472
- }
473
- interface GpuLeaseOptions {
474
- gpuClass: string;
475
- durationSeconds: number;
476
- confidentiality?: number;
477
- region?: string;
478
- /** Operators to request quotes from (multi-quote = the decentralized scheduler). */
479
- operators: OperatorEndpoint[];
480
- chain: GpuLeaseChain;
481
- now?: () => number;
482
- }
483
- interface GpuLeaseOutcome {
484
- lease: GpuLease;
485
- /** Operators ranked cheapest-first. */
486
- rankedOperators: string[];
487
- }
488
- /** Resolve a GPU lease end-to-end. Throws with the full picture on failure. */
489
- declare function gpuLease(options: GpuLeaseOptions): Promise<GpuLeaseOutcome>;
490
- /** Teardown: voluntary release with an exact-refund receipt (I3/I4). */
491
- declare function releaseGpuLease(leaseId: `0x${string}`, chain: GpuLeaseChain): Promise<RefundReceipt>;
492
- /**
493
- * Refund projection for UI meters. Uses the chain clock when the injected
494
- * chain provides `chainNow()`; otherwise requires an explicit timestamp.
495
- * Never Date.now() — the drift lesson from the blueprint's own E2E.
496
- */
497
- declare function projectGpuLeaseRefund(leaseId: `0x${string}`, chain: GpuLeaseChain, explicitNowSeconds?: bigint): Promise<{
498
- refundWei: bigint;
499
- operatorTakeWei: bigint;
500
- nowSeconds: bigint;
501
- }>;
502
- //#endregion
503
- export { Capabilities, ClassCapability, GPU_LEASE_JOBS, GPU_LEASE_REQUEST_ABI, GPU_LEASE_SCHEMA_VERSION, GPU_LEASE_VAULT_ABI, GpuLease, GpuLeaseChain, GpuLeaseOperatorClient, GpuLeaseOptions, GpuLeaseOutcome, GpuLeaseOutputDecoded, GpuLeaseRequestParams, INTENT_VERSION, JsonRequestBody, LEASE_STATE, LeaseStatus, OperatorEndpoint, OperatorQuote, QuoteFilterInput, QuoteParams, QuoteRejection, RefundReceipt, SessionChallenge, SessionCredential, ValidatedQuote, VaultLeaseView, canonicalQuotePayload, decodeGpuLeaseOutput, encodeGpuLeaseRequest, gpuLease, gpuLeaseIntentHash, maxAffordableSeconds, projectGpuLeaseRefund, projectRefundWei, releaseGpuLease, selectQuotes, validateQuote, validateQuoteIdentity, verifyOperatorQuote };
@@ -1,410 +0,0 @@
1
- import { decodeAbiParameters, encodeAbiParameters, keccak256, parseAbi, parseAbiParameters, recoverMessageAddress, stringToHex } from "viem";
2
- //#region src/gpu-lease/intent.ts
3
- /**
4
- * Canonical GPU-lease intent + job ABI encoding — MUST match:
5
- * gpu-lease-blueprint/gpu-lease-blueprint-lib/src/jobs/mod.rs (intent_hash)
6
- * gpu-lease-blueprint/contracts/src/GpuLeaseBlueprint.sol (GpuLeaseRequest)
7
- * gpu-lease-blueprint/metadata/blueprint.json (encodeType, generated).
8
- */
9
- const INTENT_VERSION = 1;
10
- /** keccak256 of the canonical intent string. */
11
- function gpuLeaseIntentHash(input) {
12
- return keccak256(stringToHex(`gpu-lease-intent|v${input.intentVersion}|${input.gpuClass}|${input.durationSeconds}|${input.confidentiality}|${input.region}`));
13
- }
14
- /** ABI shape of GpuLeaseRequest — mirrors the blueprint's sol! type. */
15
- const GPU_LEASE_REQUEST_ABI = parseAbiParameters("(uint8 intentVersion, bytes32 intentHash, uint128 pricePerSecond, uint64 durationSeconds, uint8 confidentiality, string gpuClass, string region, address lessee, bytes32 leaseId)");
16
- /**
17
- * ABI-encoded GpuLeaseRequest — the LEASE job calldata payload.
18
- * Positional (ABI order) — order is pinned by the blueprint's generated
19
- * encodeType in metadata/blueprint.json.
20
- */
21
- function encodeGpuLeaseRequest(params) {
22
- return encodeAbiParameters(GPU_LEASE_REQUEST_ABI, [[
23
- params.intentVersion,
24
- params.intentHash,
25
- params.pricePerSecondWei,
26
- params.durationSeconds,
27
- params.confidentiality,
28
- params.gpuClass,
29
- params.region,
30
- params.lessee,
31
- params.leaseId
32
- ]]);
33
- }
34
- //#endregion
35
- //#region src/gpu-lease/types.ts
36
- /**
37
- * GPU lease client types — mirrors the gpu-lease-blueprint operator API
38
- * (gpu-lease-blueprint/gpu-lease-blueprint-lib/src/api.rs) and the on-chain
39
- * ABI (contracts/src/GpuLeaseBlueprint.sol sol! mirror).
40
- *
41
- * Every on-chain and off-chain surface is schemaVersioned; unknown versions
42
- * fail closed (SPEC §3 of the blueprint).
43
- */
44
- /** The only schema this client understands. */
45
- const GPU_LEASE_SCHEMA_VERSION = 1;
46
- //#endregion
47
- //#region src/gpu-lease/operators.ts
48
- function assertKnownSchema(actual, where) {
49
- if (actual !== 1) throw new Error(`${where}: unknown schemaVersion ${actual} (client understands ${GPU_LEASE_VERSION_TEXT}) — failing closed`);
50
- }
51
- const GPU_LEASE_VERSION_TEXT = String(1);
52
- async function getJson(endpoint, path) {
53
- const res = await (endpoint.fetch ?? fetch)(new URL(path, endpoint.baseUrl).toString(), { headers: { accept: "application/json" } });
54
- if (!res.ok) throw new Error(`GET ${path}: operator returned ${res.status}`);
55
- const body = await res.json();
56
- assertKnownSchema(body.schemaVersion, `GET ${path}`);
57
- return body;
58
- }
59
- async function postJson(endpoint, path, payload, requireSchema = false) {
60
- const res = await (endpoint.fetch ?? fetch)(new URL(path, endpoint.baseUrl).toString(), {
61
- method: "POST",
62
- headers: {
63
- "content-type": "application/json",
64
- accept: "application/json"
65
- },
66
- body: JSON.stringify(payload)
67
- });
68
- if (!res.ok) {
69
- const detail = await res.text().catch(() => "");
70
- throw new Error(`POST ${path}: operator returned ${res.status} ${detail.slice(0, 200)}`);
71
- }
72
- const body = await res.json();
73
- if (requireSchema) assertKnownSchema(body.schemaVersion, `POST ${path}`);
74
- return body;
75
- }
76
- var GpuLeaseOperatorClient = class {
77
- constructor(endpoint) {
78
- this.endpoint = endpoint;
79
- }
80
- capabilities() {
81
- return getJson(this.endpoint, "/api/capabilities");
82
- }
83
- async quote(params) {
84
- const quote = await postJson(this.endpoint, "/api/quote", {
85
- gpuClass: params.gpuClass,
86
- durationSeconds: params.durationSeconds,
87
- confidentiality: params.confidentiality ?? 0,
88
- ...params.region !== void 0 ? { region: params.region } : {}
89
- }, true);
90
- return {
91
- ...quote,
92
- operatorAddress: quote.operatorAddress ?? this.endpoint.address
93
- };
94
- }
95
- leaseStatus(leaseId) {
96
- return getJson(this.endpoint, `/api/leases/${leaseId.replace(/^0x/, "")}`);
97
- }
98
- sessionChallenge(leaseId, lessee) {
99
- return postJson(this.endpoint, "/api/session/challenge", {
100
- leaseId,
101
- lessee
102
- });
103
- }
104
- sessionVerify(nonce, signature) {
105
- return postJson(this.endpoint, "/api/session/verify", {
106
- nonce,
107
- signature
108
- });
109
- }
110
- };
111
- //#endregion
112
- //#region src/gpu-lease/output.ts
113
- /**
114
- * GpuLeaseOutput decoding — the LEASE job result payload. The operator API
115
- * does NOT carry this (it is a job RESULT); it arrives from the chain via
116
- * the injected job client.
117
- */
118
- const GPU_LEASE_OUTPUT_ABI = parseAbiParameters("(bytes32 leaseId, string endpoint, uint16 schemaVersion)");
119
- /** Decode + fail closed on unknown schemaVersion (blueprint SPEC §3). */
120
- function decodeGpuLeaseOutput(encoded) {
121
- const [decoded] = decodeAbiParameters(GPU_LEASE_OUTPUT_ABI, encoded);
122
- if (decoded.schemaVersion !== 1) throw new Error(`GpuLeaseOutput: unknown schemaVersion ${decoded.schemaVersion} — failing closed`);
123
- return decoded;
124
- }
125
- //#endregion
126
- //#region src/gpu-lease/quotes.ts
127
- /**
128
- * Multi-operator RFQ: collect quotes, validate each against fail-closed
129
- * rules, rank by price. Pure — no I/O. Mirrors the validated-quote flow
130
- * proven in blueprint #1568 and the operator policy's ceiling semantics.
131
- */
132
- /** The exact string the operator signs — mirrors the blueprint's
133
- * `canonical_quote_payload` byte-for-byte. Every priced field is bound. */
134
- function canonicalQuotePayload(quote) {
135
- return `gpu-lease-quote|v1|${quote.gpuClass}|${quote.pricePerSecond}|${quote.durationSeconds}|${quote.confidentiality}|${quote.escrowTotal}|${quote.intentHash}|${quote.validUntil}`;
136
- }
137
- /** Offline EIP-191 recovery over the canonical payload. */
138
- async function verifyOperatorQuote(quote) {
139
- return (await recoverMessageAddress({
140
- message: canonicalQuotePayload(quote),
141
- signature: quote.signature
142
- })).toLowerCase();
143
- }
144
- /**
145
- * Validate one quote. Fail-closed on every field the RFQ binds:
146
- * class, duration-derived escrow, confidentiality, and expiry.
147
- */
148
- function validateQuote(quote, input) {
149
- const now = (input.now ?? Date.now)() / 1e3;
150
- if (quote.gpuClass !== input.requestedClass) return {
151
- kind: "class-mismatch",
152
- requested: input.requestedClass,
153
- quoted: quote.gpuClass
154
- };
155
- if (quote.confidentiality !== input.requestedConfidentiality) return {
156
- kind: "confidentiality-mismatch",
157
- requested: input.requestedConfidentiality,
158
- quoted: quote.confidentiality
159
- };
160
- const price = BigInt(quote.pricePerSecond);
161
- const expectedEscrow = price * BigInt(quote.durationSeconds);
162
- const actualEscrow = BigInt(quote.escrowTotal);
163
- if (expectedEscrow !== actualEscrow) return {
164
- kind: "escrow-inconsistent",
165
- expected: expectedEscrow,
166
- actual: actualEscrow
167
- };
168
- if (quote.validUntil <= now) return {
169
- kind: "stale",
170
- validUntil: quote.validUntil,
171
- now
172
- };
173
- return {
174
- quote,
175
- pricePerSecondWei: price,
176
- escrowTotalWei: actualEscrow
177
- };
178
- }
179
- /**
180
- * Collect validated quotes from N operators and rank cheapest-first.
181
- * Operators that fail validation (or whose fetch rejects) are reported,
182
- * never silently dropped — the buyer sees why an operator lost.
183
- */
184
- function selectQuotes(quotes, input) {
185
- const ranked = [];
186
- const rejected = [];
187
- for (const { operator, quote } of quotes) {
188
- const result = validateQuote(quote, input);
189
- if ("kind" in result) rejected.push({
190
- operator,
191
- reason: result
192
- });
193
- else ranked.push({
194
- operator,
195
- validated: result
196
- });
197
- }
198
- ranked.sort((a, b) => {
199
- if (a.validated.pricePerSecondWei !== b.validated.pricePerSecondWei) return a.validated.pricePerSecondWei < b.validated.pricePerSecondWei ? -1 : 1;
200
- return a.validated.escrowTotalWei < b.validated.escrowTotalWei ? -1 : 1;
201
- });
202
- return {
203
- ranked,
204
- rejected
205
- };
206
- }
207
- /**
208
- * Enforce the signed RFQ envelope when present:
209
- * - signature present => it MUST recover to the claimed operator address;
210
- * invalid signature => reject (config-trust is dead on this path).
211
- * - no signature => the endpoint-config address is used (interim fallback);
212
- * strict mode rejects unsigned quotes outright.
213
- */
214
- async function validateQuoteIdentity(quote, opts = {}) {
215
- const base = {
216
- quote,
217
- pricePerSecondWei: BigInt(quote.pricePerSecond),
218
- escrowTotalWei: BigInt(quote.escrowTotal)
219
- };
220
- if (quote.signature) {
221
- const recovered = await verifyOperatorQuote(quote).catch(() => null);
222
- const claimed = quote.operatorAddress.toLowerCase();
223
- if (recovered === null || recovered !== claimed) return {
224
- kind: "signature-mismatch",
225
- claimed,
226
- recovered: recovered ?? "unrecoverable"
227
- };
228
- return base;
229
- }
230
- if (opts.strict) return {
231
- kind: "unsigned",
232
- operator: quote.operatorAddress
233
- };
234
- return base;
235
- }
236
- //#endregion
237
- //#region src/gpu-lease/vault.ts
238
- /**
239
- * Vault money semantics — the client mirror of GpuLeaseVault.sol
240
- * (gpu-lease-blueprint/contracts/src/GpuLeaseVault.sol), invariants I1–I5.
241
- * Exact integers everywhere; wei only, never floats.
242
- */
243
- const GPU_LEASE_VAULT_ABI = parseAbi([
244
- "function create(address operator, uint128 pricePerSecond, uint64 durationSeconds, bytes32 intentHash, uint8 confidentiality, bytes endpointInfo) external payable returns (bytes32 leaseId)",
245
- "function extend(bytes32 leaseId, uint64 addSeconds) external payable",
246
- "function release(bytes32 leaseId) external",
247
- "function reap(bytes32 leaseId) external",
248
- "function withdrawEarnings() external",
249
- "function totalEscrowed() external view returns (uint256)",
250
- "function operatorEarnings() external view returns (uint256)",
251
- "function leases(bytes32) external view returns (address operator, address lessee, uint128 escrow, uint128 pricePerSecond, uint64 expiry, bytes32 intentHash, uint8 confidentiality, uint8 state)",
252
- "event LeaseCreated(bytes32 indexed leaseId, address indexed operator, address indexed lessee, uint128 escrow, uint128 pricePerSecond, uint64 expiry, bytes32 intentHash, uint8 confidentiality, bytes endpointInfo, uint16 schemaVersion)",
253
- "event LeaseReleased(bytes32 indexed leaseId, uint128 refund, uint128 operatorTake)",
254
- "event LeaseReaped(bytes32 indexed leaseId, uint128 operatorTake, address caller)"
255
- ]);
256
- /** Vault lease states (mirrors the contract). */
257
- const LEASE_STATE = {
258
- LIVE: 0,
259
- RELEASED: 1,
260
- REAPED: 2
261
- };
262
- /**
263
- * Exact refund projection (I3): pricePerSecond × max(expiry − now, 0).
264
- * Exact by construction because escrow === price × paidSeconds in the vault.
265
- */
266
- function projectRefundWei(lease, nowSeconds) {
267
- const remaining = lease.expiry > nowSeconds ? lease.expiry - nowSeconds : 0n;
268
- const refundWei = lease.pricePerSecondWei * remaining;
269
- return {
270
- refundWei,
271
- operatorTakeWei: lease.escrowWei - refundWei
272
- };
273
- }
274
- /** I2 from the client side: unpayable time can never be requested. */
275
- function maxAffordableSeconds(escrowWei, pricePerSecondWei) {
276
- if (pricePerSecondWei <= 0n) throw new Error("pricePerSecond must be positive");
277
- return escrowWei / pricePerSecondWei;
278
- }
279
- //#endregion
280
- //#region src/gpu-lease/resolver.ts
281
- /**
282
- * `gpuLease()` — the SPEC §5 resolver: collect + validate quotes → escrow on
283
- * the vault → LEASE job through tnt-core → teardown with an exact refund
284
- * receipt. Chain and job submission are INJECTED (transport-agnostic: works
285
- * with viem wallets, the TangleDriver, or test doubles).
286
- */
287
- /** Job ids — MUST match the blueprint router (sequential, no gaps). */
288
- const GPU_LEASE_JOBS = {
289
- LEASE: 0,
290
- RELEASE: 1,
291
- EXTEND: 2,
292
- REAP: 3
293
- };
294
- /** Resolve a GPU lease end-to-end. Throws with the full picture on failure. */
295
- async function gpuLease(options) {
296
- const { gpuClass, durationSeconds, region = "any" } = options;
297
- const confidentiality = options.confidentiality ?? 0;
298
- const collected = (await Promise.allSettled(options.operators.map(async (endpoint) => {
299
- const quote = await new GpuLeaseOperatorClient(endpoint).quote({
300
- gpuClass,
301
- durationSeconds,
302
- confidentiality,
303
- region
304
- });
305
- return {
306
- operator: endpoint.baseUrl,
307
- quote
308
- };
309
- }))).flatMap((s) => s.status === "fulfilled" ? [s.value] : []);
310
- if (collected.length === 0) throw new Error("gpuLease: no operator returned a quote");
311
- const { ranked, rejected } = selectQuotes(collected, {
312
- requestedClass: gpuClass,
313
- requestedDurationSeconds: durationSeconds,
314
- requestedConfidentiality: confidentiality,
315
- now: options.now
316
- });
317
- if (ranked.length === 0) {
318
- const reasons = rejected.map((r) => `${r.operator}: ${r.reason.kind}`).join(", ");
319
- throw new Error(`gpuLease: every quote failed validation (${reasons})`);
320
- }
321
- let best;
322
- for (const candidate of ranked) {
323
- const identity = await validateQuoteIdentity(candidate.validated.quote);
324
- if ("kind" in identity) {
325
- rejected.push({
326
- operator: candidate.operator,
327
- reason: identity
328
- });
329
- continue;
330
- }
331
- best = {
332
- ...candidate,
333
- validated: identity
334
- };
335
- break;
336
- }
337
- if (!best) {
338
- const reasons = rejected.map((r) => `${r.operator}: ${r.reason.kind}`).join(", ");
339
- throw new Error(`gpuLease: no quote passed identity verification (${reasons})`);
340
- }
341
- const operatorAddress = best.validated.quote.operatorAddress;
342
- const intentHash = gpuLeaseIntentHash({
343
- intentVersion: 1,
344
- gpuClass,
345
- durationSeconds,
346
- confidentiality,
347
- region
348
- });
349
- const leaseId = await options.chain.createVaultLease({
350
- operator: operatorAddress,
351
- pricePerSecondWei: best.validated.pricePerSecondWei,
352
- durationSeconds: BigInt(durationSeconds),
353
- intentHash,
354
- confidentiality,
355
- endpointInfo: "{}",
356
- escrowWei: best.validated.escrowTotalWei
357
- });
358
- const encoded = encodeGpuLeaseRequest({
359
- intentVersion: 1,
360
- intentHash,
361
- pricePerSecondWei: best.validated.pricePerSecondWei,
362
- durationSeconds: BigInt(durationSeconds),
363
- confidentiality,
364
- gpuClass,
365
- region,
366
- lessee: options.chain.account,
367
- leaseId
368
- });
369
- const output = decodeGpuLeaseOutput(await options.chain.submitJob(GPU_LEASE_JOBS.LEASE, encoded));
370
- if (output.leaseId !== leaseId) throw new Error(`gpuLease: job returned leaseId ${output.leaseId}, expected the vault leaseId ${leaseId} (one identity across money and routing)`);
371
- return {
372
- lease: {
373
- leaseId,
374
- endpoint: output.endpoint,
375
- schemaVersion: output.schemaVersion,
376
- quote: best.validated
377
- },
378
- rankedOperators: ranked.map((r) => r.operator)
379
- };
380
- }
381
- /** Teardown: voluntary release with an exact-refund receipt (I3/I4). */
382
- async function releaseGpuLease(leaseId, chain) {
383
- const { refundWei, operatorTakeWei } = await chain.releaseLease(leaseId);
384
- return {
385
- leaseId,
386
- refundWei,
387
- operatorTakeWei
388
- };
389
- }
390
- /**
391
- * Refund projection for UI meters. Uses the chain clock when the injected
392
- * chain provides `chainNow()`; otherwise requires an explicit timestamp.
393
- * Never Date.now() — the drift lesson from the blueprint's own E2E.
394
- */
395
- async function projectGpuLeaseRefund(leaseId, chain, explicitNowSeconds) {
396
- const lease = await chain.readLease(leaseId);
397
- let nowSeconds = explicitNowSeconds;
398
- if (nowSeconds === void 0) {
399
- if (!chain.chainNow) throw new Error("projectGpuLeaseRefund: chain has no chainNow() — pass explicitNowSeconds");
400
- nowSeconds = await chain.chainNow();
401
- }
402
- const { refundWei, operatorTakeWei } = projectRefundWei(lease, nowSeconds);
403
- return {
404
- refundWei,
405
- operatorTakeWei,
406
- nowSeconds
407
- };
408
- }
409
- //#endregion
410
- export { GPU_LEASE_JOBS, GPU_LEASE_REQUEST_ABI, GPU_LEASE_SCHEMA_VERSION, GPU_LEASE_VAULT_ABI, GpuLeaseOperatorClient, INTENT_VERSION, LEASE_STATE, canonicalQuotePayload, decodeGpuLeaseOutput, encodeGpuLeaseRequest, gpuLease, gpuLeaseIntentHash, maxAffordableSeconds, projectGpuLeaseRefund, projectRefundWei, releaseGpuLease, selectQuotes, validateQuote, validateQuoteIdentity, verifyOperatorQuote };