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