@opengeni/sdk 0.20.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/index.d.ts +466 -7
- package/dist/index.js +281 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +368 -49
- package/src/errors.ts +22 -1
- package/src/index.ts +43 -0
- package/src/types.ts +545 -5
package/dist/index.js
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var OpenGeniApiError = class extends Error {
|
|
3
3
|
status;
|
|
4
|
+
code;
|
|
4
5
|
body;
|
|
5
6
|
constructor(status, body) {
|
|
6
|
-
|
|
7
|
+
const decoded = decodeApiErrorBody(body);
|
|
8
|
+
super(`OpenGeni API ${status}: ${decoded.message ?? (body || "(empty body)")}`);
|
|
7
9
|
this.name = "OpenGeniApiError";
|
|
8
10
|
this.status = status;
|
|
11
|
+
this.code = decoded.code;
|
|
9
12
|
this.body = body;
|
|
10
13
|
}
|
|
11
14
|
};
|
|
15
|
+
function decodeApiErrorBody(body) {
|
|
16
|
+
if (!body) return {};
|
|
17
|
+
try {
|
|
18
|
+
const decoded = JSON.parse(body);
|
|
19
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return {};
|
|
20
|
+
const record = decoded;
|
|
21
|
+
return {
|
|
22
|
+
...typeof record.code === "string" && record.code ? { code: record.code } : {},
|
|
23
|
+
...typeof record.message === "string" && record.message ? { message: record.message } : {}
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var OpenGeniSessionListCursorError = class extends OpenGeniApiError {
|
|
30
|
+
};
|
|
12
31
|
var OpenGeniApiContractMismatchError = class extends Error {
|
|
13
32
|
expected;
|
|
14
33
|
actual;
|
|
@@ -319,6 +338,7 @@ var SESSION_EVENT_TYPES = [
|
|
|
319
338
|
"agent.reasoning.delta",
|
|
320
339
|
"agent.toolCall.created",
|
|
321
340
|
"agent.toolCall.output",
|
|
341
|
+
"agent.model.request",
|
|
322
342
|
"agent.model.usage",
|
|
323
343
|
"tool.auth_needed",
|
|
324
344
|
"credential.auth_needed",
|
|
@@ -370,10 +390,13 @@ var SESSION_EVENT_TYPES = [
|
|
|
370
390
|
"terminal.pty.output.delta",
|
|
371
391
|
"terminal.pty.exited",
|
|
372
392
|
"session.title_set",
|
|
393
|
+
"session.mcp.approval_policy.updated",
|
|
373
394
|
// Multi-account Codex (P1): the session's inference account changed.
|
|
374
395
|
"codex.account.switched",
|
|
375
396
|
// credential allocator metadata-only per-turn credential selection audit.
|
|
376
397
|
"codex.credential.selected",
|
|
398
|
+
// Bounded, identity-free deterministic shadow/replay decision.
|
|
399
|
+
"codex.fleet.decision",
|
|
377
400
|
// credential allocator durable zero-capacity wait lifecycle. These are system/runtime
|
|
378
401
|
// events, never synthetic user messages.
|
|
379
402
|
"codex.capacity.waiting",
|
|
@@ -449,6 +472,8 @@ var KNOWN_PERMISSIONS = [
|
|
|
449
472
|
];
|
|
450
473
|
var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
|
|
451
474
|
var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
|
|
475
|
+
var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
|
|
476
|
+
var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
|
|
452
477
|
var KNOWN_USAGE_EVENT_TYPES = [
|
|
453
478
|
"agent_run.created",
|
|
454
479
|
"agent_run.completed",
|
|
@@ -465,6 +490,13 @@ var KNOWN_USAGE_EVENT_TYPES = [
|
|
|
465
490
|
];
|
|
466
491
|
|
|
467
492
|
// src/client.ts
|
|
493
|
+
function sessionListQuery(options) {
|
|
494
|
+
const { limit, parentSessionId } = options;
|
|
495
|
+
return {
|
|
496
|
+
...limit === void 0 ? {} : { limit: String(limit) },
|
|
497
|
+
...parentSessionId === void 0 ? {} : { parentSessionId: parentSessionId ?? "null" }
|
|
498
|
+
};
|
|
499
|
+
}
|
|
468
500
|
var OpenGeniClient = class {
|
|
469
501
|
baseUrl;
|
|
470
502
|
options;
|
|
@@ -482,6 +514,19 @@ var OpenGeniClient = class {
|
|
|
482
514
|
request
|
|
483
515
|
);
|
|
484
516
|
}
|
|
517
|
+
async getNewSessionDraft(workspaceId) {
|
|
518
|
+
return await this.requestJson(
|
|
519
|
+
"GET",
|
|
520
|
+
`/v1/workspaces/${workspaceId}/new-session-draft`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
async saveNewSessionDraft(workspaceId, request) {
|
|
524
|
+
return await this.requestJson(
|
|
525
|
+
"PUT",
|
|
526
|
+
`/v1/workspaces/${workspaceId}/new-session-draft`,
|
|
527
|
+
request
|
|
528
|
+
);
|
|
529
|
+
}
|
|
485
530
|
async getSession(workspaceId, sessionId) {
|
|
486
531
|
return await this.requestJson(
|
|
487
532
|
"GET",
|
|
@@ -495,36 +540,66 @@ var OpenGeniClient = class {
|
|
|
495
540
|
request
|
|
496
541
|
);
|
|
497
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Replace one attached MCP server's approval policy. The change is captured
|
|
545
|
+
* by the next claimed attempt; already-claimed work keeps its immutable
|
|
546
|
+
* policy snapshot.
|
|
547
|
+
*/
|
|
548
|
+
async updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, request) {
|
|
549
|
+
return await this.requestJson(
|
|
550
|
+
"PATCH",
|
|
551
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/mcp-servers/${encodeURIComponent(serverId)}/approval-policy`,
|
|
552
|
+
request
|
|
553
|
+
);
|
|
554
|
+
}
|
|
498
555
|
async listSessions(workspaceId, options = {}) {
|
|
556
|
+
if (options.search?.trim()) {
|
|
557
|
+
const page = await this.listSessionPage(workspaceId, options);
|
|
558
|
+
return [...page.pinned, ...page.sessions];
|
|
559
|
+
}
|
|
499
560
|
return await this.requestJson(
|
|
500
561
|
"GET",
|
|
501
562
|
`/v1/workspaces/${workspaceId}/sessions`,
|
|
502
563
|
void 0,
|
|
503
|
-
|
|
504
|
-
...options.limit !== void 0 ? { limit: String(options.limit) } : {},
|
|
505
|
-
...options.search?.trim() ? { search: options.search.trim() } : {},
|
|
506
|
-
...Object.prototype.hasOwnProperty.call(options, "parentSessionId") && options.parentSessionId !== void 0 ? {
|
|
507
|
-
parentSessionId: options.parentSessionId === null ? "null" : String(options.parentSessionId)
|
|
508
|
-
} : {}
|
|
509
|
-
}
|
|
564
|
+
sessionListQuery(options)
|
|
510
565
|
);
|
|
511
566
|
}
|
|
512
567
|
/** Pin-aware ordinary-session page with a stable keyset cursor. */
|
|
513
568
|
async listSessionPage(workspaceId, options = {}) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
569
|
+
const search = options.search?.trim();
|
|
570
|
+
let response;
|
|
571
|
+
try {
|
|
572
|
+
response = await this.requestJson(
|
|
573
|
+
"GET",
|
|
574
|
+
`/v1/workspaces/${workspaceId}/sessions`,
|
|
575
|
+
void 0,
|
|
576
|
+
{
|
|
577
|
+
view: "page",
|
|
578
|
+
...sessionListQuery(options),
|
|
579
|
+
...options.cursor !== void 0 ? { cursor: options.cursor } : {},
|
|
580
|
+
...search ? { search } : {},
|
|
581
|
+
...options.pinsOnly ? { pinsOnly: "true" } : {}
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
if (error instanceof OpenGeniApiError && error.status === 410) {
|
|
586
|
+
throw new OpenGeniSessionListCursorError(error.status, error.body);
|
|
526
587
|
}
|
|
527
|
-
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
if (Array.isArray(response)) {
|
|
591
|
+
if (options.cursor) {
|
|
592
|
+
throw new Error("The connected OpenGeni API does not support stable session-page cursors");
|
|
593
|
+
}
|
|
594
|
+
if (search) {
|
|
595
|
+
throw new Error("The connected OpenGeni API does not support session search");
|
|
596
|
+
}
|
|
597
|
+
if (options.pinsOnly) {
|
|
598
|
+
throw new Error("The connected OpenGeni API does not support pins-only session lists");
|
|
599
|
+
}
|
|
600
|
+
return { pinned: [], sessions: response, nextCursor: null };
|
|
601
|
+
}
|
|
602
|
+
return response;
|
|
528
603
|
}
|
|
529
604
|
/** Set this authenticated member's personal workspace pin for a session. */
|
|
530
605
|
async updateSessionPin(workspaceId, sessionId, request) {
|
|
@@ -673,26 +748,30 @@ var OpenGeniClient = class {
|
|
|
673
748
|
async listEvents(workspaceId, sessionId, options = {}) {
|
|
674
749
|
return (await this.listEventPage(workspaceId, sessionId, options)).events;
|
|
675
750
|
}
|
|
676
|
-
/** Bounded durable/monitoring page plus exact projection and cursor facts. */
|
|
677
751
|
async listEventPage(workspaceId, sessionId, options = {}) {
|
|
678
752
|
if (options.latest && ["includeTypes", "excludeTypes", "includeClasses", "excludeClasses"].some(
|
|
679
753
|
(name) => Object.prototype.hasOwnProperty.call(options, name)
|
|
680
754
|
)) {
|
|
681
755
|
throw new TypeError("latest cannot be combined with event filters");
|
|
682
756
|
}
|
|
757
|
+
if (options.resultMode === "compact" && !options.latest) {
|
|
758
|
+
throw new TypeError("resultMode=compact requires latest");
|
|
759
|
+
}
|
|
760
|
+
const listOptions = options.resultMode === "compact" ? null : options;
|
|
683
761
|
const response = await this.fetchImpl(
|
|
684
762
|
this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
|
|
685
|
-
...
|
|
686
|
-
...
|
|
687
|
-
...
|
|
688
|
-
...
|
|
763
|
+
...listOptions?.after !== void 0 ? { after: String(listOptions.after) } : {},
|
|
764
|
+
...listOptions?.before !== void 0 ? { before: String(listOptions.before) } : {},
|
|
765
|
+
...listOptions?.limit !== void 0 ? { limit: String(listOptions.limit) } : {},
|
|
766
|
+
...listOptions?.compact ? { compact: "1" } : {},
|
|
689
767
|
...options.mode ? { mode: options.mode } : {},
|
|
690
|
-
...
|
|
768
|
+
...listOptions?.direction ? { direction: listOptions.direction } : {},
|
|
691
769
|
...options.payloadMode ? { payloadMode: options.payloadMode } : {},
|
|
692
|
-
...options.
|
|
693
|
-
...
|
|
694
|
-
...
|
|
695
|
-
...
|
|
770
|
+
...options.resultMode ? { resultMode: options.resultMode } : {},
|
|
771
|
+
...listOptions?.includeTypes?.length ? { includeTypes: listOptions.includeTypes.join(",") } : {},
|
|
772
|
+
...listOptions?.excludeTypes?.length ? { excludeTypes: listOptions.excludeTypes.join(",") } : {},
|
|
773
|
+
...listOptions?.includeClasses?.length ? { includeClasses: listOptions.includeClasses.join(",") } : {},
|
|
774
|
+
...listOptions?.excludeClasses?.length ? { excludeClasses: listOptions.excludeClasses.join(",") } : {},
|
|
696
775
|
...options.latest ? { latest: options.latest } : {}
|
|
697
776
|
}),
|
|
698
777
|
{
|
|
@@ -702,7 +781,11 @@ var OpenGeniClient = class {
|
|
|
702
781
|
);
|
|
703
782
|
assertApiContractResponse(response);
|
|
704
783
|
if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
|
|
705
|
-
const
|
|
784
|
+
const body = await response.json();
|
|
785
|
+
if (options.resultMode === "compact") {
|
|
786
|
+
return body;
|
|
787
|
+
}
|
|
788
|
+
const events = body;
|
|
706
789
|
const integerHeader = (name) => {
|
|
707
790
|
const raw = response.headers.get(name);
|
|
708
791
|
if (raw === null) return null;
|
|
@@ -735,6 +818,18 @@ var OpenGeniClient = class {
|
|
|
735
818
|
forensicExact: response.headers.get("X-OpenGeni-Forensic-Exact") === "true"
|
|
736
819
|
};
|
|
737
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Fetch the authoritative newest-sequence semantic result directly. This is
|
|
823
|
+
* the callback-loss recovery path: it reads one compact durable result and
|
|
824
|
+
* never creates a model turn. `latest: "receipt"` aliases `tool_receipt`;
|
|
825
|
+
* turn generation remains scoped retry metadata.
|
|
826
|
+
*/
|
|
827
|
+
async getLatestEventResult(workspaceId, sessionId, options = { latest: "terminal" }) {
|
|
828
|
+
return await this.listEventPage(workspaceId, sessionId, {
|
|
829
|
+
...options,
|
|
830
|
+
resultMode: "compact"
|
|
831
|
+
});
|
|
832
|
+
}
|
|
738
833
|
/** POST a user/control event to the session. Returns the accepted event. */
|
|
739
834
|
async sendEvent(workspaceId, sessionId, event) {
|
|
740
835
|
return await this.requestJson(
|
|
@@ -1261,6 +1356,13 @@ var OpenGeniClient = class {
|
|
|
1261
1356
|
}
|
|
1262
1357
|
return config;
|
|
1263
1358
|
}
|
|
1359
|
+
/** Authenticated model definitions plus workspace-specific selectability. */
|
|
1360
|
+
async getWorkspaceModelCatalog(workspaceId) {
|
|
1361
|
+
return await this.requestJson(
|
|
1362
|
+
"GET",
|
|
1363
|
+
`/v1/workspaces/${workspaceId}/model-catalog`
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1264
1366
|
/** The caller's access context: subject, account + workspace grants, defaults. */
|
|
1265
1367
|
async getAccessContext() {
|
|
1266
1368
|
return await this.requestJson("GET", "/v1/access/me");
|
|
@@ -1561,13 +1663,14 @@ var OpenGeniClient = class {
|
|
|
1561
1663
|
* -> complete. Returns the ready `FileAsset`.
|
|
1562
1664
|
*/
|
|
1563
1665
|
async uploadFile(workspaceId, input) {
|
|
1564
|
-
const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data;
|
|
1666
|
+
const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data instanceof ArrayBuffer ? input.data.slice(0) : input.data;
|
|
1565
1667
|
const sizeBytes = typeof body === "string" ? new TextEncoder().encode(body).byteLength : body instanceof Blob ? body.size : body.byteLength;
|
|
1668
|
+
const sha256 = input.sha256 ?? await sha256ForUpload(body);
|
|
1566
1669
|
const upload = await this.beginFileUpload(workspaceId, {
|
|
1567
1670
|
filename: input.filename,
|
|
1568
1671
|
contentType: input.contentType,
|
|
1569
1672
|
sizeBytes,
|
|
1570
|
-
|
|
1673
|
+
sha256
|
|
1571
1674
|
});
|
|
1572
1675
|
const putResponse = await this.fetchImpl(upload.putUrl, {
|
|
1573
1676
|
method: "PUT",
|
|
@@ -1591,6 +1694,71 @@ var OpenGeniClient = class {
|
|
|
1591
1694
|
`/v1/workspaces/${workspaceId}/files/${fileId}`
|
|
1592
1695
|
);
|
|
1593
1696
|
}
|
|
1697
|
+
/** Read provider-neutral retained evidence metadata; never returns a storage location. */
|
|
1698
|
+
async getRetainedArtifact(workspaceId, artifactId) {
|
|
1699
|
+
return await this.requestJson(
|
|
1700
|
+
"GET",
|
|
1701
|
+
`/v1/workspaces/${workspaceId}/artifacts/${artifactId}`
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Read at most one authenticated retained-evidence range from the API. This
|
|
1706
|
+
* deliberately does not use the ordinary signed file-download URL.
|
|
1707
|
+
*/
|
|
1708
|
+
async getRetainedArtifactContent(workspaceId, artifactId, options = {}) {
|
|
1709
|
+
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
1710
|
+
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
1711
|
+
}
|
|
1712
|
+
const response = await this.fetchImpl(
|
|
1713
|
+
this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
|
|
1714
|
+
{
|
|
1715
|
+
method: "GET",
|
|
1716
|
+
headers: {
|
|
1717
|
+
...this.headers(),
|
|
1718
|
+
Accept: "application/octet-stream",
|
|
1719
|
+
...options.range ? { Range: options.range } : {}
|
|
1720
|
+
},
|
|
1721
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1722
|
+
}
|
|
1723
|
+
);
|
|
1724
|
+
try {
|
|
1725
|
+
assertApiContractResponse(response);
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
await cancelResponseBody(response, "retained artifact API contract mismatch");
|
|
1728
|
+
throw error;
|
|
1729
|
+
}
|
|
1730
|
+
if (!response.ok) {
|
|
1731
|
+
throw new OpenGeniApiError(response.status, await safeBoundedText(response));
|
|
1732
|
+
}
|
|
1733
|
+
if (response.status !== 200 && response.status !== 206) {
|
|
1734
|
+
await cancelResponseBody(response, "unexpected retained artifact response status");
|
|
1735
|
+
throw new OpenGeniApiError(response.status, "unexpected retained artifact response status");
|
|
1736
|
+
}
|
|
1737
|
+
if (response.headers.get("accept-ranges") !== "bytes") {
|
|
1738
|
+
await cancelResponseBody(response, "retained artifact response omitted byte-range support");
|
|
1739
|
+
throw new OpenGeniApiError(502, "retained artifact response omitted byte-range support");
|
|
1740
|
+
}
|
|
1741
|
+
let declaredLength;
|
|
1742
|
+
try {
|
|
1743
|
+
declaredLength = parseBoundedContentLength(response.headers.get("content-length"));
|
|
1744
|
+
} catch (error) {
|
|
1745
|
+
await cancelResponseBody(response, "invalid retained artifact content-length");
|
|
1746
|
+
throw error;
|
|
1747
|
+
}
|
|
1748
|
+
const bytes = await readBoundedResponseBytes(
|
|
1749
|
+
response,
|
|
1750
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
1751
|
+
declaredLength
|
|
1752
|
+
);
|
|
1753
|
+
return {
|
|
1754
|
+
bytes,
|
|
1755
|
+
status: response.status,
|
|
1756
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
1757
|
+
contentLength: bytes.byteLength,
|
|
1758
|
+
contentRange: response.headers.get("content-range"),
|
|
1759
|
+
acceptRanges: "bytes"
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1594
1762
|
/** Mint a short-lived signed download URL for a ready file. */
|
|
1595
1763
|
async createFileDownloadUrl(workspaceId, fileId) {
|
|
1596
1764
|
return await this.requestJson(
|
|
@@ -1984,6 +2152,13 @@ var OpenGeniClient = class {
|
|
|
1984
2152
|
`/v1/workspaces/${workspaceId}/codex/usage/refresh`
|
|
1985
2153
|
);
|
|
1986
2154
|
}
|
|
2155
|
+
/** Live independently-settled quota + reset-credit overview for every account. */
|
|
2156
|
+
async codexOverview(workspaceId) {
|
|
2157
|
+
return await this.requestJson(
|
|
2158
|
+
"GET",
|
|
2159
|
+
`/v1/workspaces/${workspaceId}/codex/overview`
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
1987
2162
|
/** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */
|
|
1988
2163
|
async codexDisconnect(workspaceId) {
|
|
1989
2164
|
return await this.requestJson(
|
|
@@ -2013,6 +2188,14 @@ var OpenGeniClient = class {
|
|
|
2013
2188
|
patch
|
|
2014
2189
|
);
|
|
2015
2190
|
}
|
|
2191
|
+
/** Toggle only NEW automatic allocations under independent allocator OCC. */
|
|
2192
|
+
async setCodexAccountAllocator(workspaceId, accountId, input) {
|
|
2193
|
+
return await this.requestJson(
|
|
2194
|
+
"PATCH",
|
|
2195
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/allocator`,
|
|
2196
|
+
input
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2016
2199
|
/** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */
|
|
2017
2200
|
async disconnectCodexAccount(workspaceId, accountId) {
|
|
2018
2201
|
return await this.requestJson(
|
|
@@ -2076,6 +2259,11 @@ function assertApiContractResponse(response) {
|
|
|
2076
2259
|
throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
|
|
2077
2260
|
}
|
|
2078
2261
|
}
|
|
2262
|
+
async function sha256ForUpload(body) {
|
|
2263
|
+
const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body instanceof Blob ? new Uint8Array(await body.arrayBuffer()) : new Uint8Array(body);
|
|
2264
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
2265
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2266
|
+
}
|
|
2079
2267
|
async function safeText(response) {
|
|
2080
2268
|
try {
|
|
2081
2269
|
return await response.text();
|
|
@@ -2083,6 +2271,62 @@ async function safeText(response) {
|
|
|
2083
2271
|
return "";
|
|
2084
2272
|
}
|
|
2085
2273
|
}
|
|
2274
|
+
async function safeBoundedText(response) {
|
|
2275
|
+
try {
|
|
2276
|
+
return new TextDecoder().decode(await readBoundedResponseBytes(response, 64 * 1024, null));
|
|
2277
|
+
} catch {
|
|
2278
|
+
return "";
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
async function cancelResponseBody(response, reason) {
|
|
2282
|
+
await response.body?.cancel(reason).catch(() => void 0);
|
|
2283
|
+
}
|
|
2284
|
+
function parseBoundedContentLength(value) {
|
|
2285
|
+
if (value === null) return null;
|
|
2286
|
+
if (!/^\d+$/.test(value)) {
|
|
2287
|
+
throw new OpenGeniApiError(502, "invalid retained artifact content-length");
|
|
2288
|
+
}
|
|
2289
|
+
const length = Number(value);
|
|
2290
|
+
if (!Number.isSafeInteger(length) || length > RETAINED_OUTPUT_MAX_PAGE_BYTES) {
|
|
2291
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2292
|
+
}
|
|
2293
|
+
return length;
|
|
2294
|
+
}
|
|
2295
|
+
async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
|
|
2296
|
+
if (!response.body) {
|
|
2297
|
+
if (expectedBytes !== null && expectedBytes !== 0) {
|
|
2298
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2299
|
+
}
|
|
2300
|
+
return new Uint8Array();
|
|
2301
|
+
}
|
|
2302
|
+
const reader = response.body.getReader();
|
|
2303
|
+
const chunks = [];
|
|
2304
|
+
let totalBytes = 0;
|
|
2305
|
+
try {
|
|
2306
|
+
while (true) {
|
|
2307
|
+
const { done, value } = await reader.read();
|
|
2308
|
+
if (done) break;
|
|
2309
|
+
totalBytes += value.byteLength;
|
|
2310
|
+
if (totalBytes > maxBytes) {
|
|
2311
|
+
await reader.cancel("retained artifact response exceeded the SDK byte limit").catch(() => void 0);
|
|
2312
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2313
|
+
}
|
|
2314
|
+
chunks.push(value);
|
|
2315
|
+
}
|
|
2316
|
+
} finally {
|
|
2317
|
+
reader.releaseLock();
|
|
2318
|
+
}
|
|
2319
|
+
if (expectedBytes !== null && totalBytes !== expectedBytes) {
|
|
2320
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2321
|
+
}
|
|
2322
|
+
const bytes = new Uint8Array(totalBytes);
|
|
2323
|
+
let offset = 0;
|
|
2324
|
+
for (const chunk of chunks) {
|
|
2325
|
+
bytes.set(chunk, offset);
|
|
2326
|
+
offset += chunk.byteLength;
|
|
2327
|
+
}
|
|
2328
|
+
return bytes;
|
|
2329
|
+
}
|
|
2086
2330
|
|
|
2087
2331
|
// src/proxy.ts
|
|
2088
2332
|
function formatSseEvent(event) {
|
|
@@ -2488,7 +2732,10 @@ export {
|
|
|
2488
2732
|
OpenGeniApiContractMismatchError,
|
|
2489
2733
|
OpenGeniApiError,
|
|
2490
2734
|
OpenGeniClient,
|
|
2735
|
+
OpenGeniSessionListCursorError,
|
|
2491
2736
|
OpenGeniStreamError,
|
|
2737
|
+
RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
|
|
2738
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
2492
2739
|
SESSION_EVENT_TYPES,
|
|
2493
2740
|
TTYD_SUBPROTOCOL,
|
|
2494
2741
|
TtydClientCommand,
|