@enterstellar-ai/cloud 0.1.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/LICENSE +196 -0
- package/NOTICE +17 -0
- package/README.md +297 -0
- package/dist/index.cjs +1433 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1359 -0
- package/dist/index.d.ts +1359 -0
- package/dist/index.js +1428 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1428 @@
|
|
|
1
|
+
import { EnterstellarError } from '@enterstellar-ai/types';
|
|
2
|
+
import { createParser } from 'eventsource-parser';
|
|
3
|
+
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
var CloudError = class _CloudError extends EnterstellarError {
|
|
6
|
+
/**
|
|
7
|
+
* The Cloud-specific error code.
|
|
8
|
+
*
|
|
9
|
+
* For SDK-originated errors, this mirrors `EnterstellarError.code` (e.g., `'ENS-5001'`).
|
|
10
|
+
* For server-originated errors, this is the `ENS-C{NNNN}` code from the
|
|
11
|
+
* response body (e.g., `'ENS-C4290'`).
|
|
12
|
+
*/
|
|
13
|
+
cloudCode;
|
|
14
|
+
/**
|
|
15
|
+
* URL for the billing upgrade page.
|
|
16
|
+
* Present only on `ENS-C4290` (IPU quota exceeded) errors.
|
|
17
|
+
*
|
|
18
|
+
* @see Design Choice SD3 — app decides how to surface the upgrade prompt.
|
|
19
|
+
*/
|
|
20
|
+
upgradeUrl;
|
|
21
|
+
/**
|
|
22
|
+
* Milliseconds until the quota resets or rate limit expires.
|
|
23
|
+
* Present only on `ENS-C4290` errors.
|
|
24
|
+
*
|
|
25
|
+
* Use this to schedule a retry or display a countdown to the user.
|
|
26
|
+
*/
|
|
27
|
+
retryAfterMs;
|
|
28
|
+
/**
|
|
29
|
+
* The `X-Request-Id` header value from the server response.
|
|
30
|
+
* A bare ULID (AG16) for support ticket correlation.
|
|
31
|
+
* `undefined` if the error occurred before a server response was received
|
|
32
|
+
* (e.g., network failure, config validation).
|
|
33
|
+
*/
|
|
34
|
+
requestId;
|
|
35
|
+
/**
|
|
36
|
+
* @internal Use factory functions instead of constructing directly.
|
|
37
|
+
*
|
|
38
|
+
* @param code - The `EnterstellarErrorCode` for the base `EnterstellarError` class.
|
|
39
|
+
* @param cloudCode - The Cloud-specific error code (`ENS-5xxx` or `ENS-C{NNNN}`).
|
|
40
|
+
* @param message - Human-readable error description.
|
|
41
|
+
* @param recoverable - Whether the caller can meaningfully retry.
|
|
42
|
+
* @param options - Optional Cloud-specific metadata.
|
|
43
|
+
*/
|
|
44
|
+
constructor(code, cloudCode, message, recoverable, options) {
|
|
45
|
+
super(code, "cloud", message, recoverable, options?.cause);
|
|
46
|
+
this.name = "CloudError";
|
|
47
|
+
this.cloudCode = cloudCode;
|
|
48
|
+
this.upgradeUrl = options?.upgradeUrl;
|
|
49
|
+
this.retryAfterMs = options?.retryAfterMs;
|
|
50
|
+
this.requestId = options?.requestId;
|
|
51
|
+
if ("captureStackTrace" in Error) {
|
|
52
|
+
Error.captureStackTrace(
|
|
53
|
+
this,
|
|
54
|
+
_CloudError
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Serializes the error to a plain object for logging, telemetry, or DevTools.
|
|
60
|
+
*
|
|
61
|
+
* Extends `EnterstellarError.toJSON()` with Cloud-specific fields.
|
|
62
|
+
*
|
|
63
|
+
* @returns A plain object representation including all Cloud metadata.
|
|
64
|
+
*/
|
|
65
|
+
toJSON() {
|
|
66
|
+
return {
|
|
67
|
+
...super.toJSON(),
|
|
68
|
+
name: this.name,
|
|
69
|
+
cloudCode: this.cloudCode,
|
|
70
|
+
module: "cloud",
|
|
71
|
+
upgradeUrl: this.upgradeUrl,
|
|
72
|
+
retryAfterMs: this.retryAfterMs,
|
|
73
|
+
requestId: this.requestId
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function createConfigError(field) {
|
|
78
|
+
return new CloudError(
|
|
79
|
+
"ENS-5001",
|
|
80
|
+
"ENS-5001",
|
|
81
|
+
`@enterstellar-ai/cloud: Invalid config \u2014 "${field}" is required and must be a non-empty string.`,
|
|
82
|
+
false
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
function createDisposedError() {
|
|
86
|
+
return new CloudError(
|
|
87
|
+
"ENS-5002",
|
|
88
|
+
"ENS-5002",
|
|
89
|
+
"@enterstellar-ai/cloud: Client has been disposed. Create a new client with createEnterstellarCloudClient().",
|
|
90
|
+
false
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
function createAnonymousModeError(method) {
|
|
94
|
+
return new CloudError(
|
|
95
|
+
"ENS-5004",
|
|
96
|
+
"ENS-5004",
|
|
97
|
+
`@enterstellar-ai/cloud: ${method}() is not available in anonymous mode. Anonymous clients (pk_anon_*) can only call submitSignal(). Use a project API key (ak_*) for full access.`,
|
|
98
|
+
false
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
function createRetriesExhaustedError(attempts, lastStatusCode, requestId) {
|
|
102
|
+
const statusSuffix = lastStatusCode !== void 0 ? ` Last status: ${String(lastStatusCode)}.` : " Last error: network failure.";
|
|
103
|
+
return new CloudError(
|
|
104
|
+
"ENS-5005",
|
|
105
|
+
"ENS-5005",
|
|
106
|
+
`@enterstellar-ai/cloud: All ${String(attempts)} retry attempts failed.${statusSuffix}`,
|
|
107
|
+
true,
|
|
108
|
+
{ requestId }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function createQuotaExceededError(body, requestId) {
|
|
112
|
+
return new CloudError(
|
|
113
|
+
"ENS-5003",
|
|
114
|
+
// Base EnterstellarErrorCode — server errors map to ENS-5003
|
|
115
|
+
body.code,
|
|
116
|
+
// Cloud code — 'ENS-C4290' (or 'ENS-C4291' if server sends it)
|
|
117
|
+
`@enterstellar-ai/cloud: ${body.message}`,
|
|
118
|
+
true,
|
|
119
|
+
// Recoverable — caller can upgrade tier or wait for reset
|
|
120
|
+
{
|
|
121
|
+
upgradeUrl: body.upgradeUrl,
|
|
122
|
+
retryAfterMs: body.retryAfterMs,
|
|
123
|
+
requestId
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/version.ts
|
|
129
|
+
var CLOUD_SDK_VERSION = "0.1.0";
|
|
130
|
+
|
|
131
|
+
// src/transport/idempotency.ts
|
|
132
|
+
var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
133
|
+
function encodeTimestamp(timeMs) {
|
|
134
|
+
let remaining = timeMs;
|
|
135
|
+
const chars = new Array(10);
|
|
136
|
+
for (let i = 9; i >= 0; i--) {
|
|
137
|
+
chars[i] = CROCKFORD_BASE32[remaining & 31];
|
|
138
|
+
remaining = Math.floor(remaining / 32);
|
|
139
|
+
}
|
|
140
|
+
return chars.join("");
|
|
141
|
+
}
|
|
142
|
+
function encodeRandomness() {
|
|
143
|
+
const bytes = new Uint8Array(10);
|
|
144
|
+
crypto.getRandomValues(bytes);
|
|
145
|
+
const chars = new Array(16);
|
|
146
|
+
let bitBuffer = 0;
|
|
147
|
+
let bitsInBuffer = 0;
|
|
148
|
+
let charIndex = 0;
|
|
149
|
+
for (let byteIndex = 0; byteIndex < 10; byteIndex++) {
|
|
150
|
+
bitBuffer = bitBuffer << 8 | bytes[byteIndex];
|
|
151
|
+
bitsInBuffer += 8;
|
|
152
|
+
while (bitsInBuffer >= 5) {
|
|
153
|
+
bitsInBuffer -= 5;
|
|
154
|
+
const fiveBitValue = bitBuffer >>> bitsInBuffer & 31;
|
|
155
|
+
chars[charIndex] = CROCKFORD_BASE32[fiveBitValue];
|
|
156
|
+
charIndex++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return chars.join("");
|
|
160
|
+
}
|
|
161
|
+
function generateIdempotencyKey() {
|
|
162
|
+
return encodeTimestamp(Date.now()) + encodeRandomness();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/transport/cloud-http.ts
|
|
166
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
167
|
+
var MAX_ATTEMPTS = 3;
|
|
168
|
+
var RETRY_BACKOFF_MS = [1e3, 2e3, 4e3];
|
|
169
|
+
var OPERATION_TIMEOUTS = Object.freeze({
|
|
170
|
+
/** Forge P99 = 10s (§8.9), 3× safety margin. */
|
|
171
|
+
forge: 3e4,
|
|
172
|
+
/** CR5: max 60s runtime + network/queue overhead. */
|
|
173
|
+
certify: 9e4,
|
|
174
|
+
/** OLAP queries can be slow depending on data volume. */
|
|
175
|
+
analytics: 3e4,
|
|
176
|
+
/** Business analytics — same profile as trace analytics. */
|
|
177
|
+
businessAnalytics: 3e4,
|
|
178
|
+
/** Default for all other operations. */
|
|
179
|
+
default: 1e4
|
|
180
|
+
});
|
|
181
|
+
function parseNumericHeader(headers, name) {
|
|
182
|
+
const raw = headers.get(name);
|
|
183
|
+
if (raw === null || raw.trim().length === 0) {
|
|
184
|
+
return void 0;
|
|
185
|
+
}
|
|
186
|
+
const value = Number(raw);
|
|
187
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
async function safeParseJson(response) {
|
|
193
|
+
try {
|
|
194
|
+
const data = await response.json();
|
|
195
|
+
return data;
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function parseErrorBody(response) {
|
|
201
|
+
try {
|
|
202
|
+
const raw = await response.json();
|
|
203
|
+
if (typeof raw === "object" && raw !== null && "error" in raw) {
|
|
204
|
+
const envelope = raw;
|
|
205
|
+
const errorObj = envelope.error;
|
|
206
|
+
if (typeof errorObj === "object" && errorObj !== null && "code" in errorObj && "message" in errorObj && typeof errorObj.code === "string" && typeof errorObj.message === "string") {
|
|
207
|
+
const typed = errorObj;
|
|
208
|
+
return {
|
|
209
|
+
code: typed.code,
|
|
210
|
+
message: typed.message,
|
|
211
|
+
retryAfterMs: typeof typed.retryAfterMs === "number" ? typed.retryAfterMs : void 0,
|
|
212
|
+
upgradeUrl: typeof typed.upgradeUrl === "string" ? typed.upgradeUrl : void 0
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function sleep(ms) {
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
setTimeout(resolve, ms);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function resolveTimeout(globalTimeout, operationTimeout) {
|
|
227
|
+
return globalTimeout ?? operationTimeout ?? DEFAULT_TIMEOUT_MS;
|
|
228
|
+
}
|
|
229
|
+
function createCloudHttpTransport(config) {
|
|
230
|
+
const { endpoint, apiKey, timeoutMs: globalTimeoutMs } = config;
|
|
231
|
+
return {
|
|
232
|
+
async request(reqConfig) {
|
|
233
|
+
const effectiveTimeout = resolveTimeout(
|
|
234
|
+
globalTimeoutMs,
|
|
235
|
+
reqConfig.operationTimeout
|
|
236
|
+
);
|
|
237
|
+
const url = `${endpoint}${reqConfig.path}`;
|
|
238
|
+
const idempotencyKey = reqConfig.ipuCost > 0 ? generateIdempotencyKey() : void 0;
|
|
239
|
+
const headers = {
|
|
240
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
241
|
+
"User-Agent": `enterstellar-cloud-sdk/${CLOUD_SDK_VERSION}`,
|
|
242
|
+
"Accept": "application/json"
|
|
243
|
+
};
|
|
244
|
+
if (reqConfig.body !== void 0) {
|
|
245
|
+
headers["Content-Type"] = "application/json";
|
|
246
|
+
}
|
|
247
|
+
if (idempotencyKey !== void 0) {
|
|
248
|
+
headers["X-Idempotency-Key"] = idempotencyKey;
|
|
249
|
+
}
|
|
250
|
+
let lastStatusCode;
|
|
251
|
+
let lastRequestId;
|
|
252
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
253
|
+
const controller = new AbortController();
|
|
254
|
+
const timeoutId = setTimeout(() => {
|
|
255
|
+
controller.abort();
|
|
256
|
+
}, effectiveTimeout);
|
|
257
|
+
try {
|
|
258
|
+
const response = await fetch(url, {
|
|
259
|
+
method: reqConfig.method,
|
|
260
|
+
headers,
|
|
261
|
+
...reqConfig.body !== void 0 ? { body: JSON.stringify(reqConfig.body) } : {},
|
|
262
|
+
signal: controller.signal
|
|
263
|
+
});
|
|
264
|
+
clearTimeout(timeoutId);
|
|
265
|
+
const ipuUsed = parseNumericHeader(response.headers, "X-IPU-Used");
|
|
266
|
+
const ipuRemaining = parseNumericHeader(response.headers, "X-IPU-Remaining");
|
|
267
|
+
const ipuCost = parseNumericHeader(response.headers, "X-IPU-Cost");
|
|
268
|
+
const requestId = response.headers.get("X-Request-Id") ?? void 0;
|
|
269
|
+
lastStatusCode = response.status;
|
|
270
|
+
lastRequestId = requestId;
|
|
271
|
+
if (response.ok) {
|
|
272
|
+
const data = await safeParseJson(response);
|
|
273
|
+
return {
|
|
274
|
+
ok: true,
|
|
275
|
+
statusCode: response.status,
|
|
276
|
+
data,
|
|
277
|
+
ipuUsed,
|
|
278
|
+
ipuRemaining,
|
|
279
|
+
ipuCost,
|
|
280
|
+
requestId,
|
|
281
|
+
error: null
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (response.status === 429) {
|
|
285
|
+
const errorBody2 = await parseErrorBody(response);
|
|
286
|
+
const body = errorBody2 ?? {
|
|
287
|
+
code: "ENS-C4290",
|
|
288
|
+
message: "IPU quota exceeded"
|
|
289
|
+
};
|
|
290
|
+
throw createQuotaExceededError(body, requestId);
|
|
291
|
+
}
|
|
292
|
+
if (response.status >= 500) {
|
|
293
|
+
await response.text().catch(() => void 0);
|
|
294
|
+
if (attempt < MAX_ATTEMPTS - 1) {
|
|
295
|
+
const backoffMs = RETRY_BACKOFF_MS[attempt];
|
|
296
|
+
await sleep(backoffMs);
|
|
297
|
+
}
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const errorBody = await parseErrorBody(response);
|
|
301
|
+
throw new CloudError(
|
|
302
|
+
"ENS-5003",
|
|
303
|
+
errorBody?.code ?? `HTTP-${String(response.status)}`,
|
|
304
|
+
`@enterstellar-ai/cloud: Request failed \u2014 ${errorBody?.message ?? `HTTP ${String(response.status)}`}.`,
|
|
305
|
+
false,
|
|
306
|
+
{ requestId }
|
|
307
|
+
);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
clearTimeout(timeoutId);
|
|
310
|
+
if (error instanceof CloudError) {
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
lastStatusCode = void 0;
|
|
314
|
+
lastRequestId = void 0;
|
|
315
|
+
if (attempt < MAX_ATTEMPTS - 1) {
|
|
316
|
+
const backoffMs = RETRY_BACKOFF_MS[attempt];
|
|
317
|
+
await sleep(backoffMs);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
throw createRetriesExhaustedError(MAX_ATTEMPTS, lastStatusCode, lastRequestId);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function parseNumericHeader2(headers, name) {
|
|
326
|
+
const raw = headers.get(name);
|
|
327
|
+
if (raw === null || raw.trim().length === 0) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
const value = Number(raw);
|
|
331
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
332
|
+
return value;
|
|
333
|
+
}
|
|
334
|
+
return void 0;
|
|
335
|
+
}
|
|
336
|
+
function parseIPUHeaders(headers, isAnonymous) {
|
|
337
|
+
if (isAnonymous) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
const used = parseNumericHeader2(headers, "X-IPU-Used");
|
|
341
|
+
const remaining = parseNumericHeader2(headers, "X-IPU-Remaining");
|
|
342
|
+
const cost = parseNumericHeader2(headers, "X-IPU-Cost");
|
|
343
|
+
if (used !== void 0 && remaining !== void 0 && cost !== void 0) {
|
|
344
|
+
return { used, remaining, cost };
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
async function parseErrorBody2(response) {
|
|
349
|
+
try {
|
|
350
|
+
const raw = await response.json();
|
|
351
|
+
if (typeof raw === "object" && raw !== null && "error" in raw) {
|
|
352
|
+
const envelope = raw;
|
|
353
|
+
const errorObj = envelope.error;
|
|
354
|
+
if (typeof errorObj === "object" && errorObj !== null && "code" in errorObj && "message" in errorObj && typeof errorObj.code === "string" && typeof errorObj.message === "string") {
|
|
355
|
+
const typed = errorObj;
|
|
356
|
+
return {
|
|
357
|
+
code: typed.code,
|
|
358
|
+
message: typed.message,
|
|
359
|
+
retryAfterMs: typeof typed.retryAfterMs === "number" ? typed.retryAfterMs : void 0,
|
|
360
|
+
upgradeUrl: typeof typed.upgradeUrl === "string" ? typed.upgradeUrl : void 0
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function safeParseJsonString(data) {
|
|
370
|
+
try {
|
|
371
|
+
return JSON.parse(data);
|
|
372
|
+
} catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function mapEventToFragment(event, ipu) {
|
|
377
|
+
const eventType = event.event ?? "message";
|
|
378
|
+
switch (eventType) {
|
|
379
|
+
case "meta": {
|
|
380
|
+
const data = safeParseJsonString(event.data);
|
|
381
|
+
if (data === null) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const fragment = {
|
|
385
|
+
type: "meta",
|
|
386
|
+
data: { provider: data.provider, model: data.model },
|
|
387
|
+
ipu
|
|
388
|
+
};
|
|
389
|
+
return fragment;
|
|
390
|
+
}
|
|
391
|
+
case "node": {
|
|
392
|
+
const data = safeParseJsonString(event.data);
|
|
393
|
+
if (data === null) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
const fragment = {
|
|
397
|
+
type: "node",
|
|
398
|
+
data
|
|
399
|
+
};
|
|
400
|
+
return fragment;
|
|
401
|
+
}
|
|
402
|
+
case "property": {
|
|
403
|
+
const data = safeParseJsonString(event.data);
|
|
404
|
+
if (data === null) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const fragment = {
|
|
408
|
+
type: "property",
|
|
409
|
+
data: { path: data.path, value: data.value }
|
|
410
|
+
};
|
|
411
|
+
return fragment;
|
|
412
|
+
}
|
|
413
|
+
case "complete": {
|
|
414
|
+
const data = safeParseJsonString(event.data);
|
|
415
|
+
if (data === null) {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
const fragment = {
|
|
419
|
+
type: "complete",
|
|
420
|
+
data,
|
|
421
|
+
ipu
|
|
422
|
+
};
|
|
423
|
+
return fragment;
|
|
424
|
+
}
|
|
425
|
+
case "error": {
|
|
426
|
+
const data = safeParseJsonString(event.data);
|
|
427
|
+
if (data === null) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
const fragment = {
|
|
431
|
+
type: "error",
|
|
432
|
+
data: { code: data.code, message: data.message }
|
|
433
|
+
};
|
|
434
|
+
return fragment;
|
|
435
|
+
}
|
|
436
|
+
default:
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function createCloudSSETransport(config) {
|
|
441
|
+
const { endpoint, apiKey, timeoutMs: globalTimeoutMs } = config;
|
|
442
|
+
return {
|
|
443
|
+
async *stream(sseConfig) {
|
|
444
|
+
const effectiveTimeout = globalTimeoutMs ?? OPERATION_TIMEOUTS.forge;
|
|
445
|
+
const url = `${endpoint}/v1/forge`;
|
|
446
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
447
|
+
const headers = {
|
|
448
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
449
|
+
"User-Agent": `enterstellar-cloud-sdk/${CLOUD_SDK_VERSION}`,
|
|
450
|
+
"Accept": "text/event-stream",
|
|
451
|
+
"Content-Type": "application/json",
|
|
452
|
+
"X-Idempotency-Key": idempotencyKey
|
|
453
|
+
};
|
|
454
|
+
const controller = new AbortController();
|
|
455
|
+
const timeoutId = setTimeout(() => {
|
|
456
|
+
controller.abort();
|
|
457
|
+
}, effectiveTimeout);
|
|
458
|
+
let response;
|
|
459
|
+
try {
|
|
460
|
+
response = await fetch(url, {
|
|
461
|
+
method: "POST",
|
|
462
|
+
headers,
|
|
463
|
+
body: JSON.stringify(sseConfig.body),
|
|
464
|
+
signal: controller.signal
|
|
465
|
+
});
|
|
466
|
+
} catch {
|
|
467
|
+
clearTimeout(timeoutId);
|
|
468
|
+
throw createRetriesExhaustedError(
|
|
469
|
+
1,
|
|
470
|
+
void 0,
|
|
471
|
+
void 0
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
if (!response.ok) {
|
|
475
|
+
clearTimeout(timeoutId);
|
|
476
|
+
if (response.status === 429) {
|
|
477
|
+
const errorBody2 = await parseErrorBody2(response);
|
|
478
|
+
const requestId2 = response.headers.get("X-Request-Id") ?? void 0;
|
|
479
|
+
const body2 = errorBody2 ?? {
|
|
480
|
+
code: "ENS-C4290",
|
|
481
|
+
message: "IPU quota exceeded"
|
|
482
|
+
};
|
|
483
|
+
throw createQuotaExceededError(body2, requestId2);
|
|
484
|
+
}
|
|
485
|
+
const errorBody = await parseErrorBody2(response);
|
|
486
|
+
const requestId = response.headers.get("X-Request-Id") ?? void 0;
|
|
487
|
+
throw new CloudError(
|
|
488
|
+
"ENS-5003",
|
|
489
|
+
errorBody?.code ?? `HTTP-${String(response.status)}`,
|
|
490
|
+
`@enterstellar-ai/cloud: Forge stream failed \u2014 ${errorBody?.message ?? `HTTP ${String(response.status)}`}.`,
|
|
491
|
+
false,
|
|
492
|
+
{ requestId }
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
const ipu = parseIPUHeaders(response.headers, sseConfig.isAnonymous);
|
|
496
|
+
const body = response.body;
|
|
497
|
+
if (body === null) {
|
|
498
|
+
clearTimeout(timeoutId);
|
|
499
|
+
throw createRetriesExhaustedError(1, response.status);
|
|
500
|
+
}
|
|
501
|
+
const fragmentBuffer = [];
|
|
502
|
+
const streamState = { done: false };
|
|
503
|
+
const parser = createParser({
|
|
504
|
+
onEvent(event) {
|
|
505
|
+
const fragment = mapEventToFragment(event, ipu);
|
|
506
|
+
if (fragment !== null) {
|
|
507
|
+
fragmentBuffer.push(fragment);
|
|
508
|
+
if (fragment.type === "complete" || fragment.type === "error") {
|
|
509
|
+
streamState.done = true;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
function drainBuffer() {
|
|
515
|
+
return fragmentBuffer.splice(0);
|
|
516
|
+
}
|
|
517
|
+
const reader = body.getReader();
|
|
518
|
+
const decoder = new TextDecoder();
|
|
519
|
+
try {
|
|
520
|
+
let readerDone = false;
|
|
521
|
+
while (!readerDone) {
|
|
522
|
+
const readResult = await reader.read();
|
|
523
|
+
readerDone = readResult.done;
|
|
524
|
+
if (readResult.value !== void 0) {
|
|
525
|
+
const text = decoder.decode(readResult.value, { stream: true });
|
|
526
|
+
parser.feed(text);
|
|
527
|
+
}
|
|
528
|
+
for (const fragment of drainBuffer()) {
|
|
529
|
+
yield fragment;
|
|
530
|
+
}
|
|
531
|
+
if (streamState.done) {
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
for (const fragment of drainBuffer()) {
|
|
536
|
+
yield fragment;
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
if (error instanceof CloudError) {
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
throw createRetriesExhaustedError(1, void 0, void 0);
|
|
543
|
+
} finally {
|
|
544
|
+
clearTimeout(timeoutId);
|
|
545
|
+
try {
|
|
546
|
+
reader.releaseLock();
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/metering/ipu-tracker.ts
|
|
555
|
+
var DRIFT_THRESHOLD = 0.1;
|
|
556
|
+
function createIPUTracker() {
|
|
557
|
+
let localUsed = 0;
|
|
558
|
+
let serverLimit = null;
|
|
559
|
+
let lastCorrected = false;
|
|
560
|
+
let lastIPUCost;
|
|
561
|
+
return {
|
|
562
|
+
record(cost) {
|
|
563
|
+
localUsed += cost;
|
|
564
|
+
},
|
|
565
|
+
reconcile(serverUsed, serverRemaining, ipuCost) {
|
|
566
|
+
lastIPUCost = ipuCost;
|
|
567
|
+
serverLimit = serverUsed + serverRemaining;
|
|
568
|
+
if (serverUsed === 0) {
|
|
569
|
+
if (localUsed > 0) {
|
|
570
|
+
localUsed = serverUsed;
|
|
571
|
+
lastCorrected = true;
|
|
572
|
+
} else {
|
|
573
|
+
lastCorrected = false;
|
|
574
|
+
}
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const drift = Math.abs(localUsed - serverUsed) / serverUsed;
|
|
578
|
+
if (drift > DRIFT_THRESHOLD) {
|
|
579
|
+
localUsed = serverUsed;
|
|
580
|
+
lastCorrected = true;
|
|
581
|
+
} else {
|
|
582
|
+
lastCorrected = false;
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
getEstimate() {
|
|
586
|
+
const remaining = serverLimit !== null ? Math.max(0, serverLimit - localUsed) : null;
|
|
587
|
+
return {
|
|
588
|
+
used: localUsed,
|
|
589
|
+
remaining,
|
|
590
|
+
limit: serverLimit,
|
|
591
|
+
lastReconciliationCorrected: lastCorrected
|
|
592
|
+
};
|
|
593
|
+
},
|
|
594
|
+
isOverQuota() {
|
|
595
|
+
if (serverLimit === null) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
return localUsed >= serverLimit;
|
|
599
|
+
},
|
|
600
|
+
getLastIPUCost() {
|
|
601
|
+
return lastIPUCost;
|
|
602
|
+
},
|
|
603
|
+
reset() {
|
|
604
|
+
localUsed = 0;
|
|
605
|
+
serverLimit = null;
|
|
606
|
+
lastCorrected = false;
|
|
607
|
+
lastIPUCost = void 0;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/metering/ipu-costs.ts
|
|
613
|
+
var IPU_COSTS = Object.freeze({
|
|
614
|
+
/**
|
|
615
|
+
* CloudForge generation — LLM-based component contract generation.
|
|
616
|
+
* Premium feature, highest per-operation cost.
|
|
617
|
+
*
|
|
618
|
+
* @see Design Choice CL2 — "CloudForge generation = 10 IPU"
|
|
619
|
+
* @see Bible §9.1 — `POST /v1/forge`
|
|
620
|
+
*/
|
|
621
|
+
FORGE: 10,
|
|
622
|
+
/**
|
|
623
|
+
* Cloud semantic search — vector similarity lookup via Vectorize.
|
|
624
|
+
* Lightweight operation, lowest per-operation cost.
|
|
625
|
+
*
|
|
626
|
+
* @see Design Choice CL2 — "cloud semantic search = 1 IPU"
|
|
627
|
+
* @see Bible §9.1 — `POST /v1/semantic-search`
|
|
628
|
+
*/
|
|
629
|
+
SEMANTIC_SEARCH: 1,
|
|
630
|
+
/**
|
|
631
|
+
* Intent routing — frequency-based (Phase 2) or ML-based (Phase 3)
|
|
632
|
+
* component prediction for a single intent hash.
|
|
633
|
+
*
|
|
634
|
+
* @see Design Choice IR2 — router prediction response shape.
|
|
635
|
+
* @see Bible §9.1 — `POST /v1/route`
|
|
636
|
+
*/
|
|
637
|
+
ROUTE: 1,
|
|
638
|
+
/**
|
|
639
|
+
* Batch intent routing — per-intent cost within a batch request.
|
|
640
|
+
* A batch of N intents costs `N × 1 IPU`.
|
|
641
|
+
*
|
|
642
|
+
* @see Design Choice IR5 — batch routing for pre-rendering.
|
|
643
|
+
* @see Bible §9.1 — `POST /v1/route/batch`
|
|
644
|
+
*/
|
|
645
|
+
ROUTE_BATCH_PER_INTENT: 1,
|
|
646
|
+
/**
|
|
647
|
+
* ForgeSignal submission — telemetry data ingestion.
|
|
648
|
+
* Free — signal data is Enterstellar's #1 strategic asset (§9.1:
|
|
649
|
+
* "never charge for data collection").
|
|
650
|
+
*
|
|
651
|
+
* @see Design Choice SD4 — transparent `pk_anon` auth for signals.
|
|
652
|
+
* @see Bible §9.1 — `POST /v1/signals`
|
|
653
|
+
*/
|
|
654
|
+
SIGNAL_SUBMIT: 0,
|
|
655
|
+
/**
|
|
656
|
+
* AgentTrace submission — trace data ingestion for aggregation.
|
|
657
|
+
* Free — trace data is the feedstock for analytics features.
|
|
658
|
+
*
|
|
659
|
+
* **CORRECTED:** Was 5 IPU in the OSS Bible (§4.13). Changed to 0
|
|
660
|
+
* in the Cloud Bible §9.1: "never charge for data collection."
|
|
661
|
+
*
|
|
662
|
+
* @see Bible §9.1 — `POST /v1/traces` (0 IPU)
|
|
663
|
+
*/
|
|
664
|
+
TRACE_SUBMIT: 0,
|
|
665
|
+
/**
|
|
666
|
+
* Trace analytics query — server-side OLAP aggregation via ClickHouse.
|
|
667
|
+
* Moderate cost reflecting the compute intensity.
|
|
668
|
+
*
|
|
669
|
+
* @see Design Choice TA5 — fixed analytics query types.
|
|
670
|
+
* @see Bible §9.1 — `POST /v1/traces/analytics`
|
|
671
|
+
*/
|
|
672
|
+
TRACE_ANALYTICS: 5,
|
|
673
|
+
/**
|
|
674
|
+
* Business analytics query — product intelligence via ClickHouse.
|
|
675
|
+
* Same cost profile as trace analytics.
|
|
676
|
+
*
|
|
677
|
+
* @see Design Choice TA10 — Enterstellar Analytics (business intelligence).
|
|
678
|
+
* @see Bible §9.1 — `POST /v1/analytics/*`
|
|
679
|
+
*/
|
|
680
|
+
BUSINESS_ANALYTICS: 5,
|
|
681
|
+
/**
|
|
682
|
+
* Contract certification — "Enterstellar Certified" audit initiation.
|
|
683
|
+
* Highest cost — involves Fly.io microVM test execution (CR5).
|
|
684
|
+
*
|
|
685
|
+
* @see Design Choice GI5 — certification lifecycle.
|
|
686
|
+
* @see Design Choice CR6 — certification costs 20 IPU.
|
|
687
|
+
* @see Bible §9.1 — `POST /v1/contracts/:id/certify`
|
|
688
|
+
*/
|
|
689
|
+
CERTIFY: 20,
|
|
690
|
+
/**
|
|
691
|
+
* Usage query — returns current IPU consumption and tier.
|
|
692
|
+
* Free — necessary for clients to monitor their own usage.
|
|
693
|
+
*
|
|
694
|
+
* @see Bible §9.1 — `GET /v1/usage`
|
|
695
|
+
*/
|
|
696
|
+
USAGE_QUERY: 0,
|
|
697
|
+
/**
|
|
698
|
+
* IPU ledger query — per-operation charge audit trail.
|
|
699
|
+
* Free — billing transparency.
|
|
700
|
+
*
|
|
701
|
+
* @see Design Choice AM13 — IPU ledger exposure.
|
|
702
|
+
* @see Bible §9.1 — `GET /v1/usage/ledger`
|
|
703
|
+
*/
|
|
704
|
+
LEDGER_QUERY: 0,
|
|
705
|
+
/**
|
|
706
|
+
* Trace listing query — paginated trace retrieval.
|
|
707
|
+
* Free — reading your own data is never charged.
|
|
708
|
+
*
|
|
709
|
+
* @see Bible §9.1 — `GET /v1/traces`
|
|
710
|
+
*/
|
|
711
|
+
GET_TRACES: 0,
|
|
712
|
+
/**
|
|
713
|
+
* GDPR data deletion — initiate project data purge.
|
|
714
|
+
* Free — compliance operations are never charged.
|
|
715
|
+
*
|
|
716
|
+
* @see Design Choice AG9 — two-phase delete.
|
|
717
|
+
* @see Design Choice D110 — GDPR soft-delete.
|
|
718
|
+
* @see Bible §9.1 — `DELETE /v1/project/:id/data`
|
|
719
|
+
*/
|
|
720
|
+
DELETE_PROJECT_DATA: 0
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
// src/inference/cloud-forge-proxy.ts
|
|
724
|
+
function buildIPU(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
725
|
+
if (isAnonymous) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
729
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
730
|
+
}
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
function createCloudForgeProxy(transport, sseTransport, tracker, isAnonymous, sessionType) {
|
|
734
|
+
return {
|
|
735
|
+
async forge(options) {
|
|
736
|
+
if (tracker.isOverQuota()) {
|
|
737
|
+
throw createQuotaExceededError({
|
|
738
|
+
code: "ENS-C4290",
|
|
739
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
const response = await transport.request({
|
|
743
|
+
method: "POST",
|
|
744
|
+
path: "/v1/forge",
|
|
745
|
+
body: {
|
|
746
|
+
intent: options.intent,
|
|
747
|
+
constraints: options.constraints,
|
|
748
|
+
sessionType
|
|
749
|
+
},
|
|
750
|
+
ipuCost: IPU_COSTS.FORGE,
|
|
751
|
+
operationTimeout: OPERATION_TIMEOUTS.forge
|
|
752
|
+
});
|
|
753
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
754
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
755
|
+
}
|
|
756
|
+
tracker.record(IPU_COSTS.FORGE);
|
|
757
|
+
const ipu = buildIPU(
|
|
758
|
+
response.ipuUsed,
|
|
759
|
+
response.ipuRemaining,
|
|
760
|
+
response.ipuCost,
|
|
761
|
+
isAnonymous
|
|
762
|
+
);
|
|
763
|
+
const data = response.data;
|
|
764
|
+
if (data === null) {
|
|
765
|
+
throw createQuotaExceededError({
|
|
766
|
+
code: "ENS-C5000",
|
|
767
|
+
message: "Forge response contained no data"
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return { data, ipu };
|
|
771
|
+
},
|
|
772
|
+
async *stream(options) {
|
|
773
|
+
if (tracker.isOverQuota()) {
|
|
774
|
+
throw createQuotaExceededError({
|
|
775
|
+
code: "ENS-C4290",
|
|
776
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
const sseConfig = {
|
|
780
|
+
body: {
|
|
781
|
+
intent: options.intent,
|
|
782
|
+
constraints: options.constraints,
|
|
783
|
+
sessionType
|
|
784
|
+
},
|
|
785
|
+
isAnonymous
|
|
786
|
+
};
|
|
787
|
+
tracker.record(IPU_COSTS.FORGE);
|
|
788
|
+
yield* sseTransport.stream(sseConfig);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// src/inference/cloud-index-proxy.ts
|
|
794
|
+
var DEFAULT_TOP_K = 5;
|
|
795
|
+
function buildIPU2(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
796
|
+
if (isAnonymous) {
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
800
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
801
|
+
}
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
function createCloudIndexProxy(transport, tracker, isAnonymous) {
|
|
805
|
+
return {
|
|
806
|
+
async search(query, topK = DEFAULT_TOP_K) {
|
|
807
|
+
if (tracker.isOverQuota()) {
|
|
808
|
+
throw createQuotaExceededError({
|
|
809
|
+
code: "ENS-C4290",
|
|
810
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
const response = await transport.request({
|
|
814
|
+
method: "POST",
|
|
815
|
+
path: "/v1/semantic-search",
|
|
816
|
+
body: { query, topK },
|
|
817
|
+
ipuCost: IPU_COSTS.SEMANTIC_SEARCH
|
|
818
|
+
});
|
|
819
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
820
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
821
|
+
}
|
|
822
|
+
tracker.record(IPU_COSTS.SEMANTIC_SEARCH);
|
|
823
|
+
const ipu = buildIPU2(
|
|
824
|
+
response.ipuUsed,
|
|
825
|
+
response.ipuRemaining,
|
|
826
|
+
response.ipuCost,
|
|
827
|
+
isAnonymous
|
|
828
|
+
);
|
|
829
|
+
const data = response.data;
|
|
830
|
+
const results = data?.results ?? [];
|
|
831
|
+
return { data: results, ipu };
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// src/routing/cloud-router-proxy.ts
|
|
837
|
+
function buildIPU3(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
838
|
+
if (isAnonymous) {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
842
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
function createCloudRouterProxy(transport, tracker, isAnonymous) {
|
|
847
|
+
return {
|
|
848
|
+
async route(intentHash) {
|
|
849
|
+
if (tracker.isOverQuota()) {
|
|
850
|
+
throw createQuotaExceededError({
|
|
851
|
+
code: "ENS-C4290",
|
|
852
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
const response = await transport.request({
|
|
856
|
+
method: "POST",
|
|
857
|
+
path: "/v1/route",
|
|
858
|
+
body: { intentHash },
|
|
859
|
+
ipuCost: IPU_COSTS.ROUTE
|
|
860
|
+
});
|
|
861
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
862
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
863
|
+
}
|
|
864
|
+
tracker.record(IPU_COSTS.ROUTE);
|
|
865
|
+
const ipu = buildIPU3(
|
|
866
|
+
response.ipuUsed,
|
|
867
|
+
response.ipuRemaining,
|
|
868
|
+
response.ipuCost,
|
|
869
|
+
isAnonymous
|
|
870
|
+
);
|
|
871
|
+
const data = response.data ?? {
|
|
872
|
+
predictions: [],
|
|
873
|
+
metadata: { modelVersion: "unknown", signalCount: 0 }
|
|
874
|
+
};
|
|
875
|
+
return { data, ipu };
|
|
876
|
+
},
|
|
877
|
+
async routeBatch(intentHashes) {
|
|
878
|
+
if (tracker.isOverQuota()) {
|
|
879
|
+
throw createQuotaExceededError({
|
|
880
|
+
code: "ENS-C4290",
|
|
881
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
const batchCost = intentHashes.length * IPU_COSTS.ROUTE_BATCH_PER_INTENT;
|
|
885
|
+
const response = await transport.request({
|
|
886
|
+
method: "POST",
|
|
887
|
+
path: "/v1/route/batch",
|
|
888
|
+
body: { intentHashes },
|
|
889
|
+
ipuCost: batchCost
|
|
890
|
+
});
|
|
891
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
892
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
893
|
+
}
|
|
894
|
+
tracker.record(batchCost);
|
|
895
|
+
const ipu = buildIPU3(
|
|
896
|
+
response.ipuUsed,
|
|
897
|
+
response.ipuRemaining,
|
|
898
|
+
response.ipuCost,
|
|
899
|
+
isAnonymous
|
|
900
|
+
);
|
|
901
|
+
const data = response.data ?? [];
|
|
902
|
+
return { data, ipu };
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/analytics/cloud-analytics-proxy.ts
|
|
908
|
+
function buildIPU4(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
909
|
+
if (isAnonymous) {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
913
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
914
|
+
}
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
function createCloudAnalyticsProxy(transport, tracker, isAnonymous) {
|
|
918
|
+
async function executeAnalyticsRequest(path, query, costConstant) {
|
|
919
|
+
if (tracker.isOverQuota()) {
|
|
920
|
+
throw createQuotaExceededError({
|
|
921
|
+
code: "ENS-C4290",
|
|
922
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
const response = await transport.request({
|
|
926
|
+
method: "POST",
|
|
927
|
+
path,
|
|
928
|
+
body: query,
|
|
929
|
+
ipuCost: costConstant,
|
|
930
|
+
operationTimeout: OPERATION_TIMEOUTS.analytics
|
|
931
|
+
});
|
|
932
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
933
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
934
|
+
}
|
|
935
|
+
tracker.record(costConstant);
|
|
936
|
+
const ipu = buildIPU4(
|
|
937
|
+
response.ipuUsed,
|
|
938
|
+
response.ipuRemaining,
|
|
939
|
+
response.ipuCost,
|
|
940
|
+
isAnonymous
|
|
941
|
+
);
|
|
942
|
+
const data = response.data ?? {
|
|
943
|
+
rows: [],
|
|
944
|
+
queryType: query.queryType
|
|
945
|
+
};
|
|
946
|
+
return { data, ipu };
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
async analytics(query) {
|
|
950
|
+
return executeAnalyticsRequest(
|
|
951
|
+
"/v1/traces/analytics",
|
|
952
|
+
query,
|
|
953
|
+
IPU_COSTS.TRACE_ANALYTICS
|
|
954
|
+
);
|
|
955
|
+
},
|
|
956
|
+
async businessAnalytics(query) {
|
|
957
|
+
return executeAnalyticsRequest(
|
|
958
|
+
"/v1/analytics/query",
|
|
959
|
+
query,
|
|
960
|
+
IPU_COSTS.BUSINESS_ANALYTICS
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// src/traces/trace-submitter.ts
|
|
967
|
+
var CONSENT_DENIED_RESULT = Object.freeze({
|
|
968
|
+
data: Object.freeze({ accepted: false }),
|
|
969
|
+
ipu: null
|
|
970
|
+
});
|
|
971
|
+
function buildIPU5(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
972
|
+
if (isAnonymous) {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
976
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
977
|
+
}
|
|
978
|
+
return null;
|
|
979
|
+
}
|
|
980
|
+
function createTraceSubmitter(transport, tracker, isAnonymous, traceConsent, sessionType) {
|
|
981
|
+
return {
|
|
982
|
+
async submitTrace(trace) {
|
|
983
|
+
if (!traceConsent) {
|
|
984
|
+
return CONSENT_DENIED_RESULT;
|
|
985
|
+
}
|
|
986
|
+
if (!trace.consent.anonymizedAggregation) {
|
|
987
|
+
return CONSENT_DENIED_RESULT;
|
|
988
|
+
}
|
|
989
|
+
const response = await transport.request({
|
|
990
|
+
method: "POST",
|
|
991
|
+
path: "/v1/traces",
|
|
992
|
+
body: { trace, sessionType },
|
|
993
|
+
ipuCost: IPU_COSTS.TRACE_SUBMIT
|
|
994
|
+
});
|
|
995
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
996
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
997
|
+
}
|
|
998
|
+
const ipu = buildIPU5(
|
|
999
|
+
response.ipuUsed,
|
|
1000
|
+
response.ipuRemaining,
|
|
1001
|
+
response.ipuCost,
|
|
1002
|
+
isAnonymous
|
|
1003
|
+
);
|
|
1004
|
+
const accepted = response.data?.accepted ?? true;
|
|
1005
|
+
return { data: { accepted }, ipu };
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/signals/signal-submitter.ts
|
|
1011
|
+
function buildIPU6(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1012
|
+
if (isAnonymous) {
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1016
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1017
|
+
}
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
function createSignalSubmitter(transport, tracker, isAnonymous, sessionType) {
|
|
1021
|
+
return {
|
|
1022
|
+
async submitSignal(signal) {
|
|
1023
|
+
const response = await transport.request({
|
|
1024
|
+
method: "POST",
|
|
1025
|
+
path: "/v1/signals",
|
|
1026
|
+
body: { ...signal, sessionType },
|
|
1027
|
+
ipuCost: IPU_COSTS.SIGNAL_SUBMIT
|
|
1028
|
+
});
|
|
1029
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1030
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1031
|
+
}
|
|
1032
|
+
const ipu = buildIPU6(
|
|
1033
|
+
response.ipuUsed,
|
|
1034
|
+
response.ipuRemaining,
|
|
1035
|
+
response.ipuCost,
|
|
1036
|
+
isAnonymous
|
|
1037
|
+
);
|
|
1038
|
+
const accepted = response.data?.accepted ?? true;
|
|
1039
|
+
return { data: { accepted }, ipu };
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// src/operations/traces-query-proxy.ts
|
|
1045
|
+
function buildQueryString(params) {
|
|
1046
|
+
const entries = [];
|
|
1047
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1048
|
+
if (value !== void 0 && value !== null) {
|
|
1049
|
+
entries.push(
|
|
1050
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return entries.length > 0 ? `?${entries.join("&")}` : "";
|
|
1055
|
+
}
|
|
1056
|
+
function buildIPU7(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1057
|
+
if (isAnonymous) {
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1061
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1062
|
+
}
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
function createTracesQueryProxy(transport, tracker, isAnonymous) {
|
|
1066
|
+
return {
|
|
1067
|
+
async getTraces(options) {
|
|
1068
|
+
const queryString = buildQueryString({
|
|
1069
|
+
cursor: options?.cursor,
|
|
1070
|
+
limit: options?.limit,
|
|
1071
|
+
correlation_id: options?.correlationId,
|
|
1072
|
+
thread_id: options?.threadId
|
|
1073
|
+
});
|
|
1074
|
+
const response = await transport.request({
|
|
1075
|
+
method: "GET",
|
|
1076
|
+
path: `/v1/traces${queryString}`,
|
|
1077
|
+
ipuCost: IPU_COSTS.GET_TRACES
|
|
1078
|
+
});
|
|
1079
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1080
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1081
|
+
}
|
|
1082
|
+
const ipu = buildIPU7(
|
|
1083
|
+
response.ipuUsed,
|
|
1084
|
+
response.ipuRemaining,
|
|
1085
|
+
response.ipuCost,
|
|
1086
|
+
isAnonymous
|
|
1087
|
+
);
|
|
1088
|
+
const data = response.data ?? {
|
|
1089
|
+
items: [],
|
|
1090
|
+
cursor: null,
|
|
1091
|
+
hasMore: false
|
|
1092
|
+
};
|
|
1093
|
+
return { data, ipu };
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/operations/certify-proxy.ts
|
|
1099
|
+
function buildIPU8(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1100
|
+
if (isAnonymous) {
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1104
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
function createCertifyProxy(transport, tracker, isAnonymous) {
|
|
1109
|
+
return {
|
|
1110
|
+
async certify(contractId) {
|
|
1111
|
+
if (tracker.isOverQuota()) {
|
|
1112
|
+
throw createQuotaExceededError({
|
|
1113
|
+
code: "ENS-C4290",
|
|
1114
|
+
message: "IPU quota exceeded (pre-flight check)"
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
const path = `/v1/contracts/${encodeURIComponent(contractId)}/certify`;
|
|
1118
|
+
const response = await transport.request({
|
|
1119
|
+
method: "POST",
|
|
1120
|
+
path,
|
|
1121
|
+
ipuCost: IPU_COSTS.CERTIFY,
|
|
1122
|
+
operationTimeout: OPERATION_TIMEOUTS.certify
|
|
1123
|
+
});
|
|
1124
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1125
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1126
|
+
}
|
|
1127
|
+
tracker.record(IPU_COSTS.CERTIFY);
|
|
1128
|
+
const ipu = buildIPU8(
|
|
1129
|
+
response.ipuUsed,
|
|
1130
|
+
response.ipuRemaining,
|
|
1131
|
+
response.ipuCost,
|
|
1132
|
+
isAnonymous
|
|
1133
|
+
);
|
|
1134
|
+
const data = response.data ?? {
|
|
1135
|
+
status: "pending",
|
|
1136
|
+
pollUrl: `/v1/contracts/${encodeURIComponent(contractId)}`
|
|
1137
|
+
};
|
|
1138
|
+
return { data, ipu };
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// src/operations/ledger-query-proxy.ts
|
|
1144
|
+
function buildQueryString2(params) {
|
|
1145
|
+
const entries = [];
|
|
1146
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1147
|
+
if (value !== void 0 && value !== null) {
|
|
1148
|
+
entries.push(
|
|
1149
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return entries.length > 0 ? `?${entries.join("&")}` : "";
|
|
1154
|
+
}
|
|
1155
|
+
function buildIPU9(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1156
|
+
if (isAnonymous) {
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1160
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1161
|
+
}
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
function createLedgerQueryProxy(transport, tracker, isAnonymous) {
|
|
1165
|
+
return {
|
|
1166
|
+
async getLedger(options) {
|
|
1167
|
+
const queryString = buildQueryString2({
|
|
1168
|
+
cursor: options?.cursor,
|
|
1169
|
+
limit: options?.limit
|
|
1170
|
+
});
|
|
1171
|
+
const response = await transport.request({
|
|
1172
|
+
method: "GET",
|
|
1173
|
+
path: `/v1/usage/ledger${queryString}`,
|
|
1174
|
+
ipuCost: IPU_COSTS.LEDGER_QUERY
|
|
1175
|
+
});
|
|
1176
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1177
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1178
|
+
}
|
|
1179
|
+
const ipu = buildIPU9(
|
|
1180
|
+
response.ipuUsed,
|
|
1181
|
+
response.ipuRemaining,
|
|
1182
|
+
response.ipuCost,
|
|
1183
|
+
isAnonymous
|
|
1184
|
+
);
|
|
1185
|
+
const data = response.data ?? {
|
|
1186
|
+
items: [],
|
|
1187
|
+
cursor: null,
|
|
1188
|
+
hasMore: false
|
|
1189
|
+
};
|
|
1190
|
+
return { data, ipu };
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// src/operations/data-deletion-proxy.ts
|
|
1196
|
+
function buildIPU10(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1197
|
+
if (isAnonymous) {
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1201
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1202
|
+
}
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
function createDataDeletionProxy(transport, tracker, isAnonymous) {
|
|
1206
|
+
return {
|
|
1207
|
+
async deleteProjectData(projectId) {
|
|
1208
|
+
const path = `/v1/project/${encodeURIComponent(projectId)}/data`;
|
|
1209
|
+
const response = await transport.request({
|
|
1210
|
+
method: "DELETE",
|
|
1211
|
+
path,
|
|
1212
|
+
ipuCost: IPU_COSTS.DELETE_PROJECT_DATA
|
|
1213
|
+
});
|
|
1214
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1215
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1216
|
+
}
|
|
1217
|
+
const ipu = buildIPU10(
|
|
1218
|
+
response.ipuUsed,
|
|
1219
|
+
response.ipuRemaining,
|
|
1220
|
+
response.ipuCost,
|
|
1221
|
+
isAnonymous
|
|
1222
|
+
);
|
|
1223
|
+
const accepted = response.data?.accepted ?? true;
|
|
1224
|
+
return { data: { accepted }, ipu };
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/create-cloud-client.ts
|
|
1230
|
+
var DEFAULT_BASE_URL = "https://api.enterstellar.dev";
|
|
1231
|
+
var ANONYMOUS_KEY_PREFIX = "pk_anon_";
|
|
1232
|
+
var DEFAULT_SESSION_TYPE = "app";
|
|
1233
|
+
function buildIPU11(ipuUsed, ipuRemaining, ipuCost, isAnonymous) {
|
|
1234
|
+
if (isAnonymous) {
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
if (ipuUsed !== void 0 && ipuRemaining !== void 0 && ipuCost !== void 0) {
|
|
1238
|
+
return { used: ipuUsed, remaining: ipuRemaining, cost: ipuCost };
|
|
1239
|
+
}
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
function createEnterstellarCloudClient(config) {
|
|
1243
|
+
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
1244
|
+
throw createConfigError("apiKey");
|
|
1245
|
+
}
|
|
1246
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
1247
|
+
const sessionType = config.sessionType ?? DEFAULT_SESSION_TYPE;
|
|
1248
|
+
const traceConsent = config.traceConsent ?? false;
|
|
1249
|
+
const isAnonymous = config.apiKey.startsWith(ANONYMOUS_KEY_PREFIX);
|
|
1250
|
+
const transport = createCloudHttpTransport({
|
|
1251
|
+
endpoint: baseUrl,
|
|
1252
|
+
apiKey: config.apiKey,
|
|
1253
|
+
timeoutMs: config.timeoutMs
|
|
1254
|
+
});
|
|
1255
|
+
const sseTransport = createCloudSSETransport({
|
|
1256
|
+
endpoint: baseUrl,
|
|
1257
|
+
apiKey: config.apiKey,
|
|
1258
|
+
timeoutMs: config.timeoutMs
|
|
1259
|
+
});
|
|
1260
|
+
const tracker = createIPUTracker();
|
|
1261
|
+
const forgeProxy = createCloudForgeProxy(
|
|
1262
|
+
transport,
|
|
1263
|
+
sseTransport,
|
|
1264
|
+
tracker,
|
|
1265
|
+
isAnonymous,
|
|
1266
|
+
sessionType
|
|
1267
|
+
);
|
|
1268
|
+
const indexProxy = createCloudIndexProxy(transport, tracker, isAnonymous);
|
|
1269
|
+
const routerProxy = createCloudRouterProxy(transport, tracker, isAnonymous);
|
|
1270
|
+
const analyticsProxy = createCloudAnalyticsProxy(transport, tracker, isAnonymous);
|
|
1271
|
+
const traceSubmitter = createTraceSubmitter(
|
|
1272
|
+
transport,
|
|
1273
|
+
tracker,
|
|
1274
|
+
isAnonymous,
|
|
1275
|
+
traceConsent,
|
|
1276
|
+
sessionType
|
|
1277
|
+
);
|
|
1278
|
+
const signalSubmitter = createSignalSubmitter(
|
|
1279
|
+
transport,
|
|
1280
|
+
tracker,
|
|
1281
|
+
isAnonymous,
|
|
1282
|
+
sessionType
|
|
1283
|
+
);
|
|
1284
|
+
const tracesQueryProxy = createTracesQueryProxy(transport, tracker, isAnonymous);
|
|
1285
|
+
const certifyProxy = createCertifyProxy(transport, tracker, isAnonymous);
|
|
1286
|
+
const ledgerQueryProxy = createLedgerQueryProxy(transport, tracker, isAnonymous);
|
|
1287
|
+
const dataDeletionProxy = createDataDeletionProxy(transport, tracker, isAnonymous);
|
|
1288
|
+
let disposed = false;
|
|
1289
|
+
function assertNotDisposed() {
|
|
1290
|
+
if (disposed) {
|
|
1291
|
+
throw createDisposedError();
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
function assertNotAnonymous(method) {
|
|
1295
|
+
if (isAnonymous) {
|
|
1296
|
+
throw createAnonymousModeError(method);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const forge = Object.assign(
|
|
1300
|
+
async (options) => {
|
|
1301
|
+
assertNotDisposed();
|
|
1302
|
+
assertNotAnonymous("forge");
|
|
1303
|
+
return forgeProxy.forge(options);
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
stream(options) {
|
|
1307
|
+
assertNotDisposed();
|
|
1308
|
+
assertNotAnonymous("forge.stream");
|
|
1309
|
+
return forgeProxy.stream(options);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
);
|
|
1313
|
+
return {
|
|
1314
|
+
// -------------------------------------------------------------------
|
|
1315
|
+
// Generation (SD6)
|
|
1316
|
+
// -------------------------------------------------------------------
|
|
1317
|
+
forge,
|
|
1318
|
+
// -------------------------------------------------------------------
|
|
1319
|
+
// Search
|
|
1320
|
+
// -------------------------------------------------------------------
|
|
1321
|
+
async search(query, topK) {
|
|
1322
|
+
assertNotDisposed();
|
|
1323
|
+
assertNotAnonymous("search");
|
|
1324
|
+
return indexProxy.search(query, topK);
|
|
1325
|
+
},
|
|
1326
|
+
// -------------------------------------------------------------------
|
|
1327
|
+
// Routing (IR2, IR5)
|
|
1328
|
+
// -------------------------------------------------------------------
|
|
1329
|
+
async route(intentHash) {
|
|
1330
|
+
assertNotDisposed();
|
|
1331
|
+
assertNotAnonymous("route");
|
|
1332
|
+
return routerProxy.route(intentHash);
|
|
1333
|
+
},
|
|
1334
|
+
async routeBatch(intentHashes) {
|
|
1335
|
+
assertNotDisposed();
|
|
1336
|
+
assertNotAnonymous("routeBatch");
|
|
1337
|
+
return routerProxy.routeBatch(intentHashes);
|
|
1338
|
+
},
|
|
1339
|
+
// -------------------------------------------------------------------
|
|
1340
|
+
// Signals (SD1, SD4) — works in anonymous mode
|
|
1341
|
+
// -------------------------------------------------------------------
|
|
1342
|
+
async submitSignal(signal) {
|
|
1343
|
+
assertNotDisposed();
|
|
1344
|
+
return signalSubmitter.submitSignal(signal);
|
|
1345
|
+
},
|
|
1346
|
+
// -------------------------------------------------------------------
|
|
1347
|
+
// Traces (TA2)
|
|
1348
|
+
// -------------------------------------------------------------------
|
|
1349
|
+
async submitTrace(trace) {
|
|
1350
|
+
assertNotDisposed();
|
|
1351
|
+
assertNotAnonymous("submitTrace");
|
|
1352
|
+
return traceSubmitter.submitTrace(trace);
|
|
1353
|
+
},
|
|
1354
|
+
async getTraces(options) {
|
|
1355
|
+
assertNotDisposed();
|
|
1356
|
+
assertNotAnonymous("getTraces");
|
|
1357
|
+
return tracesQueryProxy.getTraces(options);
|
|
1358
|
+
},
|
|
1359
|
+
// -------------------------------------------------------------------
|
|
1360
|
+
// Analytics (TA3, TA5, TA10)
|
|
1361
|
+
// -------------------------------------------------------------------
|
|
1362
|
+
async analytics(query) {
|
|
1363
|
+
assertNotDisposed();
|
|
1364
|
+
assertNotAnonymous("analytics");
|
|
1365
|
+
return analyticsProxy.analytics(query);
|
|
1366
|
+
},
|
|
1367
|
+
async businessAnalytics(query) {
|
|
1368
|
+
assertNotDisposed();
|
|
1369
|
+
assertNotAnonymous("businessAnalytics");
|
|
1370
|
+
return analyticsProxy.businessAnalytics(query);
|
|
1371
|
+
},
|
|
1372
|
+
// -------------------------------------------------------------------
|
|
1373
|
+
// Billing (CL1) — inline, no separate proxy
|
|
1374
|
+
// -------------------------------------------------------------------
|
|
1375
|
+
async getUsage() {
|
|
1376
|
+
assertNotDisposed();
|
|
1377
|
+
assertNotAnonymous("getUsage");
|
|
1378
|
+
const response = await transport.request({
|
|
1379
|
+
method: "GET",
|
|
1380
|
+
path: "/v1/usage",
|
|
1381
|
+
ipuCost: IPU_COSTS.USAGE_QUERY
|
|
1382
|
+
});
|
|
1383
|
+
if (response.ipuUsed !== void 0 && response.ipuRemaining !== void 0) {
|
|
1384
|
+
tracker.reconcile(response.ipuUsed, response.ipuRemaining, response.ipuCost);
|
|
1385
|
+
}
|
|
1386
|
+
const ipu = buildIPU11(
|
|
1387
|
+
response.ipuUsed,
|
|
1388
|
+
response.ipuRemaining,
|
|
1389
|
+
response.ipuCost,
|
|
1390
|
+
isAnonymous
|
|
1391
|
+
);
|
|
1392
|
+
const data = response.data !== null ? {
|
|
1393
|
+
used: response.data.used,
|
|
1394
|
+
limit: response.data.limit,
|
|
1395
|
+
tier: response.data.tier
|
|
1396
|
+
} : { used: 0, limit: 0, tier: "unknown" };
|
|
1397
|
+
return { data, ipu };
|
|
1398
|
+
},
|
|
1399
|
+
async getLedger(options) {
|
|
1400
|
+
assertNotDisposed();
|
|
1401
|
+
assertNotAnonymous("getLedger");
|
|
1402
|
+
return ledgerQueryProxy.getLedger(options);
|
|
1403
|
+
},
|
|
1404
|
+
// -------------------------------------------------------------------
|
|
1405
|
+
// Operations
|
|
1406
|
+
// -------------------------------------------------------------------
|
|
1407
|
+
async certify(contractId) {
|
|
1408
|
+
assertNotDisposed();
|
|
1409
|
+
assertNotAnonymous("certify");
|
|
1410
|
+
return certifyProxy.certify(contractId);
|
|
1411
|
+
},
|
|
1412
|
+
async deleteProjectData(projectId) {
|
|
1413
|
+
assertNotDisposed();
|
|
1414
|
+
assertNotAnonymous("deleteProjectData");
|
|
1415
|
+
return dataDeletionProxy.deleteProjectData(projectId);
|
|
1416
|
+
},
|
|
1417
|
+
// -------------------------------------------------------------------
|
|
1418
|
+
// Lifecycle
|
|
1419
|
+
// -------------------------------------------------------------------
|
|
1420
|
+
dispose() {
|
|
1421
|
+
disposed = true;
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
export { CLOUD_SDK_VERSION, CloudError, IPU_COSTS, createEnterstellarCloudClient };
|
|
1427
|
+
//# sourceMappingURL=index.js.map
|
|
1428
|
+
//# sourceMappingURL=index.js.map
|