@tangle-network/tcloud 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/attestation.ts
21
+ var attestation_exports = {};
22
+ __export(attestation_exports, {
23
+ assertAttestation: () => import_tcloud_attestation.assertAttestation,
24
+ createSevSnpHardwareVerifier: () => import_tcloud_attestation.createSevSnpHardwareVerifier,
25
+ createTdxHardwareVerifier: () => import_tcloud_attestation.createTdxHardwareVerifier,
26
+ normalizeTeeType: () => import_tcloud_attestation.normalizeTeeType,
27
+ parseAttestation: () => import_tcloud_attestation.parseAttestation,
28
+ parseSevSnpReport: () => import_tcloud_attestation.parseSevSnpReport,
29
+ toHex: () => import_tcloud_attestation.toHex,
30
+ verifyAttestation: () => import_tcloud_attestation.verifyAttestation,
31
+ verifyAttestationAsync: () => import_tcloud_attestation.verifyAttestationAsync
32
+ });
33
+ module.exports = __toCommonJS(attestation_exports);
34
+ var import_tcloud_attestation = require("@tangle-network/tcloud-attestation");
35
+ // Annotate the CommonJS export names for ESM import in node:
36
+ 0 && (module.exports = {
37
+ assertAttestation,
38
+ createSevSnpHardwareVerifier,
39
+ createTdxHardwareVerifier,
40
+ normalizeTeeType,
41
+ parseAttestation,
42
+ parseSevSnpReport,
43
+ toHex,
44
+ verifyAttestation,
45
+ verifyAttestationAsync
46
+ });
@@ -0,0 +1 @@
1
+ export { AsyncAttestationPolicy, AsyncHardwareVerifier, AttestationPolicy, AttestationVerificationResult, HardwareVerifier, HardwareVerifierResult, ParsedAttestation, SevSnpReport, SevSnpVerifierOptions, TeeType, assertAttestation, createSevSnpHardwareVerifier, createTdxHardwareVerifier, normalizeTeeType, parseAttestation, parseSevSnpReport, toHex, verifyAttestation, verifyAttestationAsync } from '@tangle-network/tcloud-attestation';
@@ -0,0 +1 @@
1
+ export { AsyncAttestationPolicy, AsyncHardwareVerifier, AttestationPolicy, AttestationVerificationResult, HardwareVerifier, HardwareVerifierResult, ParsedAttestation, SevSnpReport, SevSnpVerifierOptions, TeeType, assertAttestation, createSevSnpHardwareVerifier, createTdxHardwareVerifier, normalizeTeeType, parseAttestation, parseSevSnpReport, toHex, verifyAttestation, verifyAttestationAsync } from '@tangle-network/tcloud-attestation';
@@ -0,0 +1,23 @@
1
+ // src/attestation.ts
2
+ import {
3
+ assertAttestation,
4
+ createSevSnpHardwareVerifier,
5
+ createTdxHardwareVerifier,
6
+ normalizeTeeType,
7
+ parseSevSnpReport,
8
+ parseAttestation,
9
+ toHex,
10
+ verifyAttestation,
11
+ verifyAttestationAsync
12
+ } from "@tangle-network/tcloud-attestation";
13
+ export {
14
+ assertAttestation,
15
+ createSevSnpHardwareVerifier,
16
+ createTdxHardwareVerifier,
17
+ normalizeTeeType,
18
+ parseAttestation,
19
+ parseSevSnpReport,
20
+ toHex,
21
+ verifyAttestation,
22
+ verifyAttestationAsync
23
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TCloudClient
3
- } from "./chunk-B22AH4JH.js";
3
+ } from "./chunk-M5K3EFNP.js";
4
4
 
5
5
  // src/shielded.ts
6
6
  import { privateKeyToAccount } from "viem/accounts";
@@ -0,0 +1,324 @@
1
+ // src/sandbox.ts
2
+ import { Sandbox } from "@tangle-network/sandbox";
3
+ import { createHash, randomBytes } from "crypto";
4
+ import {
5
+ normalizeTeeType,
6
+ verifyAttestationAsync
7
+ } from "@tangle-network/tcloud-attestation";
8
+ var DEFAULT_SANDBOX_URL = "https://sandbox.tangle.tools";
9
+ var TCloudSandbox = class {
10
+ client;
11
+ apiKey;
12
+ baseUrl;
13
+ timeoutMs;
14
+ constructor(config) {
15
+ this.apiKey = config.apiKey;
16
+ this.baseUrl = (config.baseUrl ?? DEFAULT_SANDBOX_URL).replace(/\/+$/, "");
17
+ this.timeoutMs = config.timeoutMs;
18
+ this.client = new Sandbox({
19
+ apiKey: this.apiKey,
20
+ baseUrl: this.baseUrl,
21
+ timeoutMs: config.timeoutMs
22
+ });
23
+ }
24
+ async create(options) {
25
+ const effectiveVerify = shouldVerifyAttestation(options);
26
+ const attestationPolicy = buildAttestationPolicy(options);
27
+ const createOptions = buildSandboxCreateOptions({
28
+ ...options,
29
+ verify: effectiveVerify
30
+ });
31
+ const sandbox = await this.client.create(createOptions);
32
+ this.attachTeeApiFallbacks(sandbox);
33
+ let attestation = attestationFromMetadata(sandbox.metadata);
34
+ if ((effectiveVerify || createOptions.confidential?.attestationNonce) && !attestation) {
35
+ const getTeeAttestation = sandbox.getTeeAttestation;
36
+ if (typeof getTeeAttestation !== "function") {
37
+ throw new Error("Installed @tangle-network/sandbox does not expose TEE attestation fetching");
38
+ }
39
+ attestation = (await getTeeAttestation.call(
40
+ sandbox,
41
+ createOptions.confidential?.attestationNonce ? { attestationNonce: createOptions.confidential.attestationNonce } : void 0
42
+ )).attestation;
43
+ }
44
+ if (effectiveVerify && !attestation) {
45
+ throw new Error("TEE attestation verification requested but no evidence was returned");
46
+ }
47
+ const verification = effectiveVerify ? await verifyAttestationAsync(attestation, {
48
+ ...attestationPolicy,
49
+ expectedNonce: createOptions.confidential?.attestationNonce
50
+ }) : void 0;
51
+ if (verification && !verification.valid) {
52
+ throw new Error(`TEE attestation verification failed: ${verification.errors.join("; ")}`);
53
+ }
54
+ return {
55
+ sandbox,
56
+ attestation,
57
+ verification,
58
+ attestationNonce: createOptions.confidential?.attestationNonce,
59
+ attestationStatus: {
60
+ requested: Boolean(options.tee),
61
+ evidenceReturned: Boolean(attestation),
62
+ verified: Boolean(verification?.valid),
63
+ nonceBound: Boolean(createOptions.confidential?.attestationNonce && verification?.valid),
64
+ errors: verification?.errors ?? []
65
+ }
66
+ };
67
+ }
68
+ attachTeeApiFallbacks(sandbox) {
69
+ if (typeof sandbox !== "object" || sandbox === null) return;
70
+ const target = sandbox;
71
+ if (typeof target.id !== "string" || target.id.length === 0) return;
72
+ if (typeof target.getTeeAttestation !== "function") {
73
+ target.getTeeAttestation = (options) => this.fetchTeeAttestation(target.id, options?.attestationNonce);
74
+ }
75
+ if (typeof target.getTeePublicKey !== "function") {
76
+ target.getTeePublicKey = () => this.fetchTeePublicKey(target.id);
77
+ }
78
+ }
79
+ async fetchTeeAttestation(sandboxId, attestationNonce) {
80
+ const response = await this.fetchSandboxApi(
81
+ `/v1/sandboxes/${encodeURIComponent(sandboxId)}/tee/attestation`,
82
+ {
83
+ method: attestationNonce ? "POST" : "GET",
84
+ body: attestationNonce ? JSON.stringify({ attestation_nonce: attestationNonce }) : void 0
85
+ }
86
+ );
87
+ const data = await response.json();
88
+ if (attestationNonce && !data.attestationNonce) {
89
+ data.attestationNonce = attestationNonce;
90
+ }
91
+ return data;
92
+ }
93
+ async fetchTeePublicKey(sandboxId) {
94
+ const response = await this.fetchSandboxApi(
95
+ `/v1/sandboxes/${encodeURIComponent(sandboxId)}/tee/public-key`,
96
+ { method: "GET" }
97
+ );
98
+ return response.json();
99
+ }
100
+ async fetchSandboxApi(path, options) {
101
+ const headers = new Headers(options.headers);
102
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
103
+ if (options.body && !headers.has("Content-Type")) {
104
+ headers.set("Content-Type", "application/json");
105
+ }
106
+ const response = await fetch(`${this.baseUrl}${path}`, {
107
+ ...options,
108
+ headers,
109
+ signal: options.signal ?? (this.timeoutMs ? AbortSignal.timeout(this.timeoutMs) : void 0)
110
+ });
111
+ if (!response.ok) {
112
+ const body = await response.text();
113
+ throw new Error(`Sandbox API ${path} failed with HTTP ${response.status}: ${body}`);
114
+ }
115
+ return response;
116
+ }
117
+ };
118
+ function createTeeAttestationChallenge(context) {
119
+ const randomHex = generateAttestationNonce(32);
120
+ if (context == null) {
121
+ return { nonce: randomHex, randomHex };
122
+ }
123
+ const contextHashHex = createHash("sha256").update(typeof context === "string" ? Buffer.from(context) : Buffer.from(context)).digest("hex");
124
+ return {
125
+ nonce: `${randomHex}${contextHashHex}`,
126
+ randomHex,
127
+ contextHashHex
128
+ };
129
+ }
130
+ function startTeeAttestationHeartbeat(sandbox, options = {}) {
131
+ const getTeeAttestation = sandbox?.getTeeAttestation;
132
+ if (typeof getTeeAttestation !== "function") {
133
+ throw new Error("Sandbox does not expose TEE attestation fetching");
134
+ }
135
+ const intervalMs = options.intervalMs ?? 6e4;
136
+ const sessionId = options.sessionId ?? generateAttestationNonce(16);
137
+ const policy = options.tee ? buildAttestationPolicy({ tee: options.tee, attestationPolicy: options.attestationPolicy }) : options.attestationPolicy ?? {};
138
+ let stopped = false;
139
+ let failures = 0;
140
+ let sequence = 0;
141
+ let latest;
142
+ let timer;
143
+ let resolveDone;
144
+ const done = new Promise((resolve) => {
145
+ resolveDone = resolve;
146
+ });
147
+ const stop = () => {
148
+ if (stopped) return;
149
+ stopped = true;
150
+ if (timer) clearTimeout(timer);
151
+ resolveDone?.();
152
+ };
153
+ const ping = async (context) => {
154
+ if (stopped || options.signal?.aborted) {
155
+ throw new Error("TEE attestation heartbeat is stopped");
156
+ }
157
+ const nextSequence = sequence + 1;
158
+ const boundContext = context ?? `${sessionId}:${nextSequence}`;
159
+ const challenge = createTeeAttestationChallenge(boundContext);
160
+ const response = await getTeeAttestation.call(sandbox, {
161
+ attestationNonce: challenge.nonce
162
+ });
163
+ const attestation = response?.attestation;
164
+ if (!attestation) {
165
+ throw new Error("TEE attestation heartbeat returned no evidence");
166
+ }
167
+ const verification = await verifyAttestationAsync(attestation, {
168
+ ...policy,
169
+ expectedNonce: challenge.nonce
170
+ });
171
+ if (!verification.valid) {
172
+ throw new Error(`TEE attestation heartbeat verification failed: ${verification.errors.join("; ")}`);
173
+ }
174
+ const sample = {
175
+ sequence: nextSequence,
176
+ nonce: challenge.nonce,
177
+ context: boundContext,
178
+ attestation,
179
+ verification,
180
+ checkedAt: /* @__PURE__ */ new Date()
181
+ };
182
+ sequence = nextSequence;
183
+ latest = sample;
184
+ options.onSuccess?.(sample);
185
+ return sample;
186
+ };
187
+ const schedule = () => {
188
+ if (stopped || options.signal?.aborted) {
189
+ stop();
190
+ return;
191
+ }
192
+ timer = setTimeout(() => {
193
+ void loop();
194
+ }, intervalMs);
195
+ };
196
+ const loop = async () => {
197
+ try {
198
+ await ping();
199
+ schedule();
200
+ } catch (error) {
201
+ failures += 1;
202
+ options.onFailure?.(error);
203
+ if (options.continueOnFailure) {
204
+ schedule();
205
+ } else {
206
+ stop();
207
+ }
208
+ }
209
+ };
210
+ options.signal?.addEventListener("abort", stop, { once: true });
211
+ if (options.immediate === false) {
212
+ schedule();
213
+ } else {
214
+ void loop();
215
+ }
216
+ return {
217
+ stop,
218
+ ping,
219
+ get stopped() {
220
+ return stopped;
221
+ },
222
+ get failures() {
223
+ return failures;
224
+ },
225
+ get latest() {
226
+ return latest;
227
+ },
228
+ done
229
+ };
230
+ }
231
+ function buildSandboxCreateOptions(options) {
232
+ if (!options.tee && (options.sealed || options.attestationNonce || options.verify)) {
233
+ throw new Error("TEE options require a tee value");
234
+ }
235
+ const shouldGenerateNonce = options.attestationNonce === "auto" || shouldVerifyAttestation(options) && !options.attestationNonce;
236
+ const attestationNonce = shouldGenerateNonce ? generateAttestationNonce() : options.attestationNonce;
237
+ if (attestationNonce) {
238
+ validateAttestationNonce(attestationNonce);
239
+ }
240
+ return {
241
+ name: options.name,
242
+ environment: options.environment ?? options.image,
243
+ sshEnabled: options.ssh || void 0,
244
+ git: options.gitUrl ? {
245
+ url: options.gitUrl,
246
+ ref: options.gitRef
247
+ } : void 0,
248
+ resources: options.cpu || options.memoryMb || options.diskGb ? {
249
+ cpuCores: options.cpu,
250
+ memoryMB: options.memoryMb,
251
+ diskGB: options.diskGb
252
+ } : void 0,
253
+ backend: options.backend ? { type: options.backend } : void 0,
254
+ confidential: options.tee ? {
255
+ tee: options.tee,
256
+ sealed: options.sealed || void 0,
257
+ attestationNonce,
258
+ attestationRefresh: Boolean(attestationNonce)
259
+ } : void 0
260
+ };
261
+ }
262
+ function shouldVerifyAttestation(options) {
263
+ return Boolean(options.tee || options.verify);
264
+ }
265
+ function buildAttestationPolicy(options) {
266
+ if (!options.tee || options.tee === "any") return options.attestationPolicy ?? {};
267
+ const requestedTypes = acceptedAttestationTypesForTee(options.tee);
268
+ const acceptedTeeTypes = options.attestationPolicy?.acceptedTeeTypes;
269
+ if (acceptedTeeTypes?.length && !acceptedTeeTypes.some((type) => requestedTypes.includes(type))) {
270
+ throw new Error(
271
+ `TEE attestation policy does not accept requested TEE type ${options.tee}`
272
+ );
273
+ }
274
+ return {
275
+ ...options.attestationPolicy,
276
+ acceptedTeeTypes: acceptedTeeTypes?.length ? acceptedTeeTypes.filter((type) => requestedTypes.includes(type)) : requestedTypes
277
+ };
278
+ }
279
+ function acceptedAttestationTypesForTee(tee) {
280
+ switch (tee) {
281
+ case "phala-dstack":
282
+ return ["tdx", "phala-dstack"];
283
+ case "gcp":
284
+ return ["tdx", "sev-snp", "gcp"];
285
+ case "azure":
286
+ return ["sev-snp", "azure"];
287
+ default:
288
+ return [normalizeTeeType(tee)];
289
+ }
290
+ }
291
+ function validateAttestationNonce(value) {
292
+ const normalized = value.trim().toLowerCase().replace(/^0x/, "");
293
+ if (!/^[0-9a-f]+$/.test(normalized)) {
294
+ throw new Error("attestation nonce must be hex");
295
+ }
296
+ if (normalized.length % 2 !== 0) {
297
+ throw new Error("attestation nonce must have even hex length");
298
+ }
299
+ const bytes = normalized.length / 2;
300
+ if (bytes < 32 || bytes > 64) {
301
+ throw new Error(`attestation nonce must be 32-64 bytes, got ${bytes}`);
302
+ }
303
+ }
304
+ function generateAttestationNonce(bytes = 32) {
305
+ return Array.from(randomBytes(bytes)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
306
+ }
307
+ function attestationFromMetadata(metadata) {
308
+ const raw = metadata?.teeAttestationJson;
309
+ if (typeof raw !== "string" || raw.trim() === "") return void 0;
310
+ try {
311
+ return JSON.parse(raw);
312
+ } catch {
313
+ return void 0;
314
+ }
315
+ }
316
+
317
+ export {
318
+ TCloudSandbox,
319
+ createTeeAttestationChallenge,
320
+ startTeeAttestationHeartbeat,
321
+ buildSandboxCreateOptions,
322
+ shouldVerifyAttestation,
323
+ generateAttestationNonce
324
+ };
@@ -10,6 +10,8 @@ var PrivateRouter = class {
10
10
  usage = /* @__PURE__ */ new Map();
11
11
  currentIndex = 0;
12
12
  totalRequests = 0;
13
+ /** Slug of the most-recently-selected operator. Populated on each selectOperator() hit. */
14
+ _lastSelectedSlug = null;
13
15
  constructor(config = {}) {
14
16
  this.config = {
15
17
  strategy: config.strategy || "round-robin",
@@ -155,6 +157,11 @@ var PrivateRouter = class {
155
157
  requestCount: (existing?.requestCount || 0) + 1,
156
158
  lastUsedAt: Date.now()
157
159
  });
160
+ this._lastSelectedSlug = op.slug;
161
+ }
162
+ /** Slug of the most-recently-selected operator (null before the first call). */
163
+ get lastSelectedSlug() {
164
+ return this._lastSelectedSlug;
158
165
  }
159
166
  getLastUsedOperator() {
160
167
  let latest = null;
@@ -212,6 +219,7 @@ var PrivateRouter = class {
212
219
  };
213
220
 
214
221
  // src/client.ts
222
+ var ROTATING_MARKER = "__tcloudRotating";
215
223
  var DEFAULT_BASE_URL = "https://router.tangle.tools/v1";
216
224
  var SDK_VERSION = "0.4.0";
217
225
  async function proxiedFetch(privacy, url, init, streaming) {
@@ -309,6 +317,54 @@ var TCloudClient = class _TCloudClient {
309
317
  const baseURL = opts.url.replace(/\/+$/, "") + "/v1";
310
318
  return new _TCloudClient({ ...opts.config ?? {}, apiKey: opts.bearer, baseURL });
311
319
  }
320
+ /**
321
+ * Build a client that rotates which operator serves each call. Mirrors
322
+ * {@link TCloudClient.shielded} in shape: returns a standard `TCloudClient`
323
+ * that behaves identically for the OpenAI-compatible surface but
324
+ * dispatches each chat/completions/embeddings request through a
325
+ * {@link PrivateRouter} — different operator per call per the chosen
326
+ * strategy.
327
+ *
328
+ * ```ts
329
+ * const tcloud = TCloudClient.rotating({
330
+ * apiKey: process.env.TCLOUD_API_KEY,
331
+ * routing: { strategy: 'min-exposure' },
332
+ * })
333
+ * await tcloud.ask('hello')
334
+ * tcloud.getRotationStats() // { callsByOperator: { ... }, currentOperator: '…' }
335
+ * ```
336
+ *
337
+ * Rotation is meaningful only for stateless calls. Sandbox-harness
338
+ * sessions bind to a single operator for the lifetime of the session;
339
+ * `rotating()` clients refuse to dispatch them — see {@link bridge}.
340
+ */
341
+ static rotating(config = {}) {
342
+ const routing = config.routing ?? {};
343
+ const strategy = routing.strategy ?? "min-exposure";
344
+ const { routing: _omit, ...base } = config;
345
+ const client = new _TCloudClient(base);
346
+ const router = new PrivateRouter({
347
+ strategy,
348
+ minOperators: routing.minOperators ?? 1,
349
+ maxRequestsPerOperator: routing.maxRequestsPerOperator,
350
+ excludeOperators: routing.excludeOperators,
351
+ preferRegions: routing.preferRegions
352
+ });
353
+ if (routing.pool && routing.pool.length > 0) {
354
+ router.setOperators(routing.pool);
355
+ client._cachedOperators = routing.pool;
356
+ client._operatorsCachedAt = Date.now();
357
+ }
358
+ ;
359
+ client.privateRouter = router;
360
+ Object.defineProperty(client, ROTATING_MARKER, {
361
+ value: true,
362
+ enumerable: false,
363
+ configurable: false,
364
+ writable: false
365
+ });
366
+ return client;
367
+ }
312
368
  constructor(config = {}) {
313
369
  this.baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/$/, "");
314
370
  this.platformURL = (config.platformURL || DEFAULT_PLATFORM_URL).replace(/\/$/, "");
@@ -624,10 +680,38 @@ var TCloudClient = class _TCloudClient {
624
680
  *
625
681
  * Sessions persist across process restarts — use the same `resume` id
626
682
  * to land on the same CLI conversation (context intact, no replay tax).
683
+ *
684
+ * Guard: clients built via {@link TCloudClient.rotating} cannot dispatch
685
+ * sandbox-harness sessions (rotation rotates per call; a sandbox session
686
+ * binds to one operator). Attempting `bridge({ harness: 'sandbox' })` on
687
+ * a rotating client throws.
627
688
  */
628
689
  bridge(cfg) {
690
+ if (cfg.harness === "sandbox" && this[ROTATING_MARKER] === true) {
691
+ throw new Error(
692
+ "TCloudClient.rotating() cannot dispatch sandbox-harness sessions.\nSandbox sessions bind to a single operator; rotation is meaningful only for\nstateless calls. Use TCloudClient.shielded() + AgentProfile.confidential.tee\nfor privacy-preserving sandbox execution instead."
693
+ );
694
+ }
629
695
  return new BridgeSession(this, cfg);
630
696
  }
697
+ /**
698
+ * Rotation stats — populated only on clients created via
699
+ * {@link TCloudClient.rotating}. Non-rotating clients return an empty
700
+ * counter and `currentOperator: null`.
701
+ */
702
+ getRotationStats() {
703
+ if (!this.privateRouter) {
704
+ return { callsByOperator: {}, currentOperator: null };
705
+ }
706
+ const callsByOperator = {};
707
+ for (const row of this.privateRouter.getStats().operatorBreakdown) {
708
+ callsByOperator[row.slug] = row.requests;
709
+ }
710
+ return {
711
+ callsByOperator,
712
+ currentOperator: this.privateRouter.lastSelectedSlug
713
+ };
714
+ }
631
715
  /** Convenience: send a single message and get the text response */
632
716
  async ask(message, modelOrOptions) {
633
717
  const options = typeof modelOrOptions === "string" ? { model: modelOrOptions } : modelOrOptions;
@@ -1129,6 +1213,8 @@ var BridgeSession = class _BridgeSession {
1129
1213
  this.client = client;
1130
1214
  this.cfg = cfg;
1131
1215
  }
1216
+ client;
1217
+ cfg;
1132
1218
  /** Full chat completion (non-streaming). */
1133
1219
  async chat(options) {
1134
1220
  return this.client.chat({ ...options, bridge: this.cfg });
@@ -1198,6 +1284,7 @@ var TCloudError = class extends Error {
1198
1284
  this.status = status;
1199
1285
  this.name = "TCloudError";
1200
1286
  }
1287
+ status;
1201
1288
  };
1202
1289
 
1203
1290
  export {
@@ -1,12 +1,23 @@
1
1
  import {
2
2
  createShieldedClient,
3
3
  generateWallet
4
- } from "./chunk-A7AEPV2G.js";
4
+ } from "./chunk-4ZUVGKVH.js";
5
5
  import {
6
6
  TCloudClient
7
- } from "./chunk-B22AH4JH.js";
7
+ } from "./chunk-M5K3EFNP.js";
8
8
 
9
9
  // src/index.ts
10
+ import {
11
+ assertAttestation,
12
+ createSevSnpHardwareVerifier,
13
+ createTdxHardwareVerifier,
14
+ normalizeTeeType,
15
+ parseAttestation,
16
+ parseSevSnpReport,
17
+ toHex,
18
+ verifyAttestation,
19
+ verifyAttestationAsync
20
+ } from "@tangle-network/tcloud-attestation";
10
21
  var TCloud = class _TCloud extends TCloudClient {
11
22
  constructor(config) {
12
23
  super(config);
@@ -35,6 +46,21 @@ var TCloud = class _TCloud extends TCloudClient {
35
46
  static shielded(config) {
36
47
  return createShieldedClient(config);
37
48
  }
49
+ /**
50
+ * Create a client that rotates operators per call.
51
+ * See {@link TCloudClient.rotating} for semantics.
52
+ *
53
+ * ```ts
54
+ * const client = TCloud.rotating({
55
+ * apiKey: process.env.TCLOUD_API_KEY,
56
+ * routing: { strategy: 'min-exposure' },
57
+ * })
58
+ * const stats = client.getRotationStats()
59
+ * ```
60
+ */
61
+ static rotating(config) {
62
+ return TCloudClient.rotating(config);
63
+ }
38
64
  /**
39
65
  * Generate a new ephemeral wallet (without creating a client).
40
66
  *
@@ -47,5 +73,14 @@ var TCloud = class _TCloud extends TCloudClient {
47
73
  };
48
74
 
49
75
  export {
50
- TCloud
76
+ TCloud,
77
+ assertAttestation,
78
+ createSevSnpHardwareVerifier,
79
+ createTdxHardwareVerifier,
80
+ normalizeTeeType,
81
+ parseAttestation,
82
+ parseSevSnpReport,
83
+ toHex,
84
+ verifyAttestation,
85
+ verifyAttestationAsync
51
86
  };