@opengeni/sdk 3.1.0 → 3.3.2-canary.1
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 +60 -8
- package/dist/accounts.d.ts +5 -1
- package/dist/accounts.js +13 -1
- package/dist/accounts.js.map +1 -1
- package/dist/artifacts.js +6 -6
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +6 -4
- package/dist/{chunk-C2H7HBNP.js → chunk-6NEPJ7NC.js} +2 -2
- package/dist/{chunk-TX3EIENU.js → chunk-BESZFWMU.js} +2 -2
- package/dist/{chunk-YTAWBDO2.js → chunk-E5BNEG2D.js} +2 -1
- package/dist/chunk-E5BNEG2D.js.map +1 -0
- package/dist/{chunk-7ZXBPJZP.js → chunk-GY6RQPCG.js} +282 -43
- package/dist/chunk-GY6RQPCG.js.map +1 -0
- package/dist/{chunk-C4DJPZRU.js → chunk-OO5LPTO7.js} +14 -1
- package/dist/chunk-OO5LPTO7.js.map +1 -0
- package/dist/{chunk-GW3EJ2GP.js → chunk-QTBAMHEF.js} +28 -3
- package/dist/{chunk-GW3EJ2GP.js.map → chunk-QTBAMHEF.js.map} +1 -1
- package/dist/{chunk-VUQZAB3O.js → chunk-TFCLHRZK.js} +2 -2
- package/dist/client.d.ts +47 -11
- package/dist/codex-realtime-controller.js +2 -2
- package/dist/company-profile.d.ts +13 -0
- package/dist/core.d.ts +2 -1
- package/dist/core.js +7 -5
- package/dist/document-authority.js +5 -5
- package/dist/editable-artifacts.js +1 -1
- package/dist/errors.d.ts +12 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +219 -7
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +3 -0
- package/dist/interaction.js +4 -2
- package/dist/model-picker-order.d.ts +1 -0
- package/dist/model-picker-order.js +10 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/realtime.d.ts +3 -1
- package/dist/realtime.js +2 -2
- package/dist/realtime.js.map +1 -1
- package/dist/session-titles.d.ts +28 -0
- package/dist/types.d.ts +90 -3
- package/package.json +6 -2
- package/src/accounts.ts +18 -0
- package/src/browser.ts +2 -0
- package/src/client.ts +407 -35
- package/src/company-profile.ts +13 -0
- package/src/core.ts +2 -0
- package/src/errors.ts +23 -0
- package/src/index.ts +29 -0
- package/src/interaction.ts +32 -0
- package/src/model-picker-order.ts +6 -0
- package/src/realtime.ts +1 -0
- package/src/session-titles.ts +84 -0
- package/src/types.ts +105 -2
- package/dist/chunk-7ZXBPJZP.js.map +0 -1
- package/dist/chunk-C4DJPZRU.js.map +0 -1
- package/dist/chunk-YTAWBDO2.js.map +0 -1
- /package/dist/{chunk-C2H7HBNP.js.map → chunk-6NEPJ7NC.js.map} +0 -0
- /package/dist/{chunk-TX3EIENU.js.map → chunk-BESZFWMU.js.map} +0 -0
- /package/dist/{chunk-VUQZAB3O.js.map → chunk-TFCLHRZK.js.map} +0 -0
package/src/client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
OpenGeniApiContractMismatchError,
|
|
3
3
|
OpenGeniApiError,
|
|
4
|
+
OpenGeniSecureContextRequiredError,
|
|
4
5
|
OpenGeniSessionListCursorError,
|
|
5
6
|
} from "./errors";
|
|
6
7
|
import {
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
} from "./workspace-live-stream";
|
|
24
25
|
import {
|
|
25
26
|
OpenGeniInteractionClient,
|
|
27
|
+
decodeComputerFrameMetadataHeader,
|
|
26
28
|
type AuthRun,
|
|
27
29
|
type AuthRunListOptions,
|
|
28
30
|
type AuthRunListResponse,
|
|
@@ -57,6 +59,7 @@ import {
|
|
|
57
59
|
type ComputerActionReceipt,
|
|
58
60
|
type ComputerActionRequest,
|
|
59
61
|
type ComputerClipboard,
|
|
62
|
+
type ComputerFrame,
|
|
60
63
|
type ComputerObservation,
|
|
61
64
|
type ComputerSession,
|
|
62
65
|
type ComputerSessionAttachment,
|
|
@@ -167,6 +170,14 @@ import type {
|
|
|
167
170
|
ClientConfig,
|
|
168
171
|
WorkspaceModelAccessPolicy,
|
|
169
172
|
WorkspaceModelCatalogResponse,
|
|
173
|
+
WorkspaceGatewayCustomModel,
|
|
174
|
+
WorkspaceGatewayCustomModelsResponse,
|
|
175
|
+
CreateWorkspaceGatewayCustomModelRequest,
|
|
176
|
+
DeleteWorkspaceGatewayCustomModelRequest,
|
|
177
|
+
WorkspaceOpenRouterCustomModel,
|
|
178
|
+
WorkspaceOpenRouterCustomModelsResponse,
|
|
179
|
+
CreateWorkspaceOpenRouterCustomModelRequest,
|
|
180
|
+
DeleteWorkspaceOpenRouterCustomModelRequest,
|
|
170
181
|
WorkspaceRealtimeModelCatalogResponse,
|
|
171
182
|
ClientSessionEventInput,
|
|
172
183
|
UserMessageEventInput,
|
|
@@ -184,6 +195,7 @@ import type {
|
|
|
184
195
|
VerifyPersonalGitHubRepositorySelectionsRequest,
|
|
185
196
|
CreateApiKeyRequest,
|
|
186
197
|
CreateApiKeyResponse,
|
|
198
|
+
CreateOrganizationApiKeyRequest,
|
|
187
199
|
CreateCapabilityCatalogItemRequest,
|
|
188
200
|
InstallSkillRequest,
|
|
189
201
|
InstallLibrarySkillRequest,
|
|
@@ -216,6 +228,8 @@ import type {
|
|
|
216
228
|
ResolveVariableSetAttachmentsResponse,
|
|
217
229
|
CreateRigRequest,
|
|
218
230
|
CreateWorkspaceRequest,
|
|
231
|
+
EnsureWorkspaceRequest,
|
|
232
|
+
EnsureWorkspaceResponse,
|
|
219
233
|
// Enrollment UX (design 11): the click-Grant approve-page lookup/deny + headless
|
|
220
234
|
// enroll-token mint.
|
|
221
235
|
DeviceEnrollmentApproveRequest,
|
|
@@ -293,6 +307,7 @@ import type {
|
|
|
293
307
|
SwapActiveSandboxRequest,
|
|
294
308
|
SwapActiveSandboxResponse,
|
|
295
309
|
ListWorkspaceMembersResponse,
|
|
310
|
+
ListWorkspaceMemberCandidatesResponse,
|
|
296
311
|
InstallPackRequest,
|
|
297
312
|
PackInstallationPreview,
|
|
298
313
|
ListSlackUserLinkAccessRequestsResponse,
|
|
@@ -454,6 +469,7 @@ import type {
|
|
|
454
469
|
RigChange,
|
|
455
470
|
ProposeRigChangeRequest,
|
|
456
471
|
WorkspaceMember,
|
|
472
|
+
WorkspaceMemberCandidate,
|
|
457
473
|
WorkspaceMemorySearchRequest,
|
|
458
474
|
WorkspaceMemorySearchResponse,
|
|
459
475
|
WorkspaceRegisteredPack,
|
|
@@ -504,6 +520,7 @@ import type {
|
|
|
504
520
|
} from "./workspace-learning";
|
|
505
521
|
import type {
|
|
506
522
|
ActivateCompanyProfileRevisionRequest,
|
|
523
|
+
CompanyProfileAgentPolicy,
|
|
507
524
|
CompanyProfileDiffRequest,
|
|
508
525
|
CompanyProfileDiffResponse,
|
|
509
526
|
CompanyProfileListOptions,
|
|
@@ -512,6 +529,7 @@ import type {
|
|
|
512
529
|
CompanyProfileRevision,
|
|
513
530
|
RollbackCompanyProfileRequest,
|
|
514
531
|
UpdateCompanyProfileRequest,
|
|
532
|
+
UpdateCompanyProfileAgentPolicyRequest,
|
|
515
533
|
} from "./company-profile";
|
|
516
534
|
import type {
|
|
517
535
|
WorkspaceStateExportResponse,
|
|
@@ -594,6 +612,12 @@ export type SharedSessionReadOptions = {
|
|
|
594
612
|
* client has a `beginSharedRead` clock.
|
|
595
613
|
*/
|
|
596
614
|
onRequestStart?: ((readGeneration?: number) => void) | undefined;
|
|
615
|
+
/**
|
|
616
|
+
* Stop this caller's interest in the shared read. The native request is
|
|
617
|
+
* aborted only after every caller that joined the same generation has
|
|
618
|
+
* stopped waiting for it.
|
|
619
|
+
*/
|
|
620
|
+
signal?: AbortSignal | undefined;
|
|
597
621
|
};
|
|
598
622
|
|
|
599
623
|
export type GetSessionOptions = SharedSessionReadOptions & {
|
|
@@ -607,6 +631,8 @@ export type GetSessionOptions = SharedSessionReadOptions & {
|
|
|
607
631
|
export type GetSessionLineageOptions = SharedSessionReadOptions;
|
|
608
632
|
|
|
609
633
|
type SingleFlightReadEntry = {
|
|
634
|
+
controller: AbortController;
|
|
635
|
+
consumers: number;
|
|
610
636
|
generation: number;
|
|
611
637
|
promise: Promise<unknown>;
|
|
612
638
|
resolve: (value: unknown) => void;
|
|
@@ -627,6 +653,8 @@ function createSingleFlightReadEntry(
|
|
|
627
653
|
reject = entryReject;
|
|
628
654
|
});
|
|
629
655
|
return {
|
|
656
|
+
controller: new AbortController(),
|
|
657
|
+
consumers: 0,
|
|
630
658
|
generation,
|
|
631
659
|
promise,
|
|
632
660
|
resolve,
|
|
@@ -693,6 +721,29 @@ export type FinalizeTranscriptionRecordingInput = {
|
|
|
693
721
|
signal?: AbortSignal | undefined;
|
|
694
722
|
};
|
|
695
723
|
|
|
724
|
+
function normalizeScheduledTaskMachineTarget<
|
|
725
|
+
T extends CreateScheduledTaskRequest | UpdateScheduledTaskRequest,
|
|
726
|
+
>(request: T): T {
|
|
727
|
+
if (!("agentConfig" in request) || !request.agentConfig?.machineTarget) {
|
|
728
|
+
return request;
|
|
729
|
+
}
|
|
730
|
+
const { workingDir, ...machineTarget } = request.agentConfig.machineTarget;
|
|
731
|
+
if (workingDir === undefined) {
|
|
732
|
+
return request;
|
|
733
|
+
}
|
|
734
|
+
const normalizedWorkingDir = workingDir.trim();
|
|
735
|
+
return {
|
|
736
|
+
...request,
|
|
737
|
+
agentConfig: {
|
|
738
|
+
...request.agentConfig,
|
|
739
|
+
machineTarget: {
|
|
740
|
+
...machineTarget,
|
|
741
|
+
...(normalizedWorkingDir ? { workingDir: normalizedWorkingDir } : {}),
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
696
747
|
/**
|
|
697
748
|
* Typed client for the OpenGeni public API. Framework-agnostic: only needs
|
|
698
749
|
* WHATWG `fetch` + streams, so it runs in Node 18+, Bun, Deno, browsers, and
|
|
@@ -981,7 +1032,11 @@ export class OpenGeniClient {
|
|
|
981
1032
|
options: GetSessionOptions = {},
|
|
982
1033
|
): Promise<Session> {
|
|
983
1034
|
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}`;
|
|
984
|
-
return await this.sharedRead(
|
|
1035
|
+
return await this.sharedRead(
|
|
1036
|
+
path,
|
|
1037
|
+
(signal) => this.requestJson<Session>("GET", path, undefined, {}, { signal }),
|
|
1038
|
+
options,
|
|
1039
|
+
);
|
|
985
1040
|
}
|
|
986
1041
|
|
|
987
1042
|
async updateSession(
|
|
@@ -1135,6 +1190,8 @@ export class OpenGeniClient {
|
|
|
1135
1190
|
pinsOnly?: boolean;
|
|
1136
1191
|
/** Return archived root chats instead of the active session list. */
|
|
1137
1192
|
archivedOnly?: boolean;
|
|
1193
|
+
/** Stop this caller's finite page read when its owning route is abandoned. */
|
|
1194
|
+
signal?: AbortSignal | undefined;
|
|
1138
1195
|
} = {},
|
|
1139
1196
|
): Promise<SessionListResponse> {
|
|
1140
1197
|
const search = options.search?.trim();
|
|
@@ -1152,6 +1209,7 @@ export class OpenGeniClient {
|
|
|
1152
1209
|
...(options.pinsOnly ? { pinsOnly: "true" } : {}),
|
|
1153
1210
|
...(options.archivedOnly ? { archivedOnly: "true" } : {}),
|
|
1154
1211
|
},
|
|
1212
|
+
{ signal: options.signal },
|
|
1155
1213
|
);
|
|
1156
1214
|
} catch (error) {
|
|
1157
1215
|
if (error instanceof OpenGeniApiError && error.status === 410) {
|
|
@@ -1303,30 +1361,35 @@ export class OpenGeniClient {
|
|
|
1303
1361
|
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/lineage`;
|
|
1304
1362
|
return await this.sharedRead(
|
|
1305
1363
|
path,
|
|
1306
|
-
() => this.requestJson<SessionLineageResponse>("GET", path),
|
|
1364
|
+
(signal) => this.requestJson<SessionLineageResponse>("GET", path, undefined, {}, { signal }),
|
|
1307
1365
|
options,
|
|
1308
1366
|
);
|
|
1309
1367
|
}
|
|
1310
1368
|
|
|
1311
1369
|
private sharedRead<T>(
|
|
1312
1370
|
key: string,
|
|
1313
|
-
read: () => Promise<T>,
|
|
1371
|
+
read: (signal: AbortSignal) => Promise<T>,
|
|
1314
1372
|
options: GetSessionOptions = {},
|
|
1315
1373
|
): Promise<T> {
|
|
1374
|
+
if (options.signal?.aborted) {
|
|
1375
|
+
return Promise.reject(
|
|
1376
|
+
options.signal.reason ?? new DOMException("Request aborted", "AbortError"),
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1316
1379
|
const existing = this.active.get(key);
|
|
1317
1380
|
if (existing) {
|
|
1318
1381
|
if (!options.fresh) {
|
|
1319
1382
|
this.observeRead(existing, options.onRequestStart);
|
|
1320
|
-
return
|
|
1383
|
+
return this.consumeSharedRead(key, existing, options.signal);
|
|
1321
1384
|
}
|
|
1322
1385
|
const requiredGeneration = existing.generation + 1;
|
|
1323
1386
|
const queued = this.queued.get(key);
|
|
1324
1387
|
if (queued && queued.generation >= requiredGeneration) {
|
|
1325
1388
|
this.observeRead(queued, options.onRequestStart);
|
|
1326
|
-
return
|
|
1389
|
+
return this.consumeSharedRead(key, queued, options.signal);
|
|
1327
1390
|
}
|
|
1328
1391
|
const predecessor = queued?.promise ?? existing.promise;
|
|
1329
|
-
return this.queueRead(key, predecessor, requiredGeneration, read, options
|
|
1392
|
+
return this.queueRead(key, predecessor, requiredGeneration, read, options);
|
|
1330
1393
|
}
|
|
1331
1394
|
// The active entry clears in its finally before reactions on its public
|
|
1332
1395
|
// promise run. During that settlement gap a successor may already be
|
|
@@ -1335,31 +1398,27 @@ export class OpenGeniClient {
|
|
|
1335
1398
|
const queued = this.queued.get(key);
|
|
1336
1399
|
if (queued && (!options.fresh || !queued.started)) {
|
|
1337
1400
|
this.observeRead(queued, options.onRequestStart);
|
|
1338
|
-
return
|
|
1401
|
+
return this.consumeSharedRead(key, queued, options.signal);
|
|
1339
1402
|
}
|
|
1340
1403
|
if (queued) {
|
|
1341
|
-
return this.queueRead(
|
|
1342
|
-
key,
|
|
1343
|
-
queued.promise,
|
|
1344
|
-
queued.generation + 1,
|
|
1345
|
-
read,
|
|
1346
|
-
options.onRequestStart,
|
|
1347
|
-
);
|
|
1404
|
+
return this.queueRead(key, queued.promise, queued.generation + 1, read, options);
|
|
1348
1405
|
}
|
|
1349
1406
|
const generation = (this.generations.get(key) ?? 0) + 1;
|
|
1350
1407
|
const entry = createSingleFlightReadEntry(generation, options.onRequestStart);
|
|
1408
|
+
const consumed = this.consumeSharedRead<T>(key, entry, options.signal);
|
|
1351
1409
|
this.launchRead(key, entry, read);
|
|
1352
|
-
return
|
|
1410
|
+
return consumed;
|
|
1353
1411
|
}
|
|
1354
1412
|
|
|
1355
1413
|
private queueRead<T>(
|
|
1356
1414
|
key: string,
|
|
1357
1415
|
predecessor: Promise<unknown>,
|
|
1358
1416
|
generation: number,
|
|
1359
|
-
read: () => Promise<T>,
|
|
1360
|
-
|
|
1417
|
+
read: (signal: AbortSignal) => Promise<T>,
|
|
1418
|
+
options: GetSessionOptions,
|
|
1361
1419
|
): Promise<T> {
|
|
1362
|
-
const entry = createSingleFlightReadEntry(generation, onRequestStart);
|
|
1420
|
+
const entry = createSingleFlightReadEntry(generation, options.onRequestStart);
|
|
1421
|
+
const consumed = this.consumeSharedRead<T>(key, entry, options.signal);
|
|
1363
1422
|
this.queued.set(key, entry);
|
|
1364
1423
|
const launch = () => this.launchRead(key, entry, read);
|
|
1365
1424
|
void predecessor.then(launch, launch);
|
|
@@ -1369,10 +1428,15 @@ export class OpenGeniClient {
|
|
|
1369
1428
|
if (!this.active.has(key)) this.generations.delete(key);
|
|
1370
1429
|
};
|
|
1371
1430
|
void entry.promise.then(clear, clear);
|
|
1372
|
-
return
|
|
1431
|
+
return consumed;
|
|
1373
1432
|
}
|
|
1374
1433
|
|
|
1375
|
-
private launchRead<T>(
|
|
1434
|
+
private launchRead<T>(
|
|
1435
|
+
key: string,
|
|
1436
|
+
entry: SingleFlightReadEntry,
|
|
1437
|
+
read: (signal: AbortSignal) => Promise<T>,
|
|
1438
|
+
): void {
|
|
1439
|
+
if (entry.controller.signal.aborted) return;
|
|
1376
1440
|
entry.started = true;
|
|
1377
1441
|
this.generations.set(key, entry.generation);
|
|
1378
1442
|
this.active.set(key, entry);
|
|
@@ -1396,7 +1460,7 @@ export class OpenGeniClient {
|
|
|
1396
1460
|
};
|
|
1397
1461
|
let request: Promise<T>;
|
|
1398
1462
|
try {
|
|
1399
|
-
request = read();
|
|
1463
|
+
request = read(entry.controller.signal);
|
|
1400
1464
|
} catch (error) {
|
|
1401
1465
|
settle();
|
|
1402
1466
|
entry.reject(error);
|
|
@@ -1414,6 +1478,33 @@ export class OpenGeniClient {
|
|
|
1414
1478
|
);
|
|
1415
1479
|
}
|
|
1416
1480
|
|
|
1481
|
+
private consumeSharedRead<T>(
|
|
1482
|
+
key: string,
|
|
1483
|
+
entry: SingleFlightReadEntry,
|
|
1484
|
+
signal: AbortSignal | undefined,
|
|
1485
|
+
): Promise<T> {
|
|
1486
|
+
entry.consumers += 1;
|
|
1487
|
+
let released = false;
|
|
1488
|
+
const release = () => {
|
|
1489
|
+
if (released) return;
|
|
1490
|
+
released = true;
|
|
1491
|
+
entry.consumers -= 1;
|
|
1492
|
+
if (!signal?.aborted || entry.consumers > 0) return;
|
|
1493
|
+
const reason = signal.reason;
|
|
1494
|
+
entry.controller.abort(reason);
|
|
1495
|
+
if (this.active.get(key) === entry) this.active.delete(key);
|
|
1496
|
+
if (this.queued.get(key) === entry) this.queued.delete(key);
|
|
1497
|
+
if (!this.active.has(key) && !this.queued.has(key)) this.generations.delete(key);
|
|
1498
|
+
entry.reject(reason);
|
|
1499
|
+
};
|
|
1500
|
+
signal?.addEventListener("abort", release, { once: true });
|
|
1501
|
+
const consumed = awaitWithAbort(entry.promise as Promise<T>, signal);
|
|
1502
|
+
return consumed.finally(() => {
|
|
1503
|
+
signal?.removeEventListener("abort", release);
|
|
1504
|
+
release();
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1417
1508
|
private observeRead(
|
|
1418
1509
|
entry: SingleFlightReadEntry,
|
|
1419
1510
|
listener: ((readGeneration?: number) => void) | undefined,
|
|
@@ -1557,7 +1648,7 @@ export class OpenGeniClient {
|
|
|
1557
1648
|
async listTurns(
|
|
1558
1649
|
workspaceId: string,
|
|
1559
1650
|
sessionId: string,
|
|
1560
|
-
options: { limit?: number; latestStarted?: boolean } = {},
|
|
1651
|
+
options: { limit?: number; latestStarted?: boolean; signal?: AbortSignal | undefined } = {},
|
|
1561
1652
|
): Promise<SessionTurn[]> {
|
|
1562
1653
|
return await this.requestJson<SessionTurn[]>(
|
|
1563
1654
|
"GET",
|
|
@@ -1567,13 +1658,19 @@ export class OpenGeniClient {
|
|
|
1567
1658
|
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
1568
1659
|
...(options.latestStarted ? { latestStarted: "1" } : {}),
|
|
1569
1660
|
},
|
|
1661
|
+
{ signal: options.signal },
|
|
1570
1662
|
);
|
|
1571
1663
|
}
|
|
1572
1664
|
|
|
1573
1665
|
/** Newest turn that durably emitted `turn.started`, or null before any admission. */
|
|
1574
|
-
async getLatestStartedTurn(
|
|
1666
|
+
async getLatestStartedTurn(
|
|
1667
|
+
workspaceId: string,
|
|
1668
|
+
sessionId: string,
|
|
1669
|
+
options: OpenGeniRequestOptions = {},
|
|
1670
|
+
): Promise<SessionTurn | null> {
|
|
1575
1671
|
const turns = await this.listTurns(workspaceId, sessionId, {
|
|
1576
1672
|
latestStarted: true,
|
|
1673
|
+
signal: options.signal,
|
|
1577
1674
|
});
|
|
1578
1675
|
return turns[0] ?? null;
|
|
1579
1676
|
}
|
|
@@ -2147,10 +2244,16 @@ export class OpenGeniClient {
|
|
|
2147
2244
|
);
|
|
2148
2245
|
}
|
|
2149
2246
|
|
|
2150
|
-
async getComposerDraft(
|
|
2247
|
+
async getComposerDraft(
|
|
2248
|
+
workspaceId: string,
|
|
2249
|
+
sessionId: string,
|
|
2250
|
+
options: OpenGeniRequestOptions = {},
|
|
2251
|
+
): Promise<ComposerDraft> {
|
|
2151
2252
|
return await this.requestSessionCommand<ComposerDraft>(
|
|
2152
2253
|
"GET",
|
|
2153
2254
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/composer-draft`,
|
|
2255
|
+
undefined,
|
|
2256
|
+
options,
|
|
2154
2257
|
);
|
|
2155
2258
|
}
|
|
2156
2259
|
|
|
@@ -2424,9 +2527,17 @@ export class OpenGeniClient {
|
|
|
2424
2527
|
// --- Goals -------------------------------------------------------------------
|
|
2425
2528
|
|
|
2426
2529
|
/** The session's goal. 404s when the session never had one. */
|
|
2427
|
-
async getGoal(
|
|
2530
|
+
async getGoal(
|
|
2531
|
+
workspaceId: string,
|
|
2532
|
+
sessionId: string,
|
|
2533
|
+
options: SharedSessionReadOptions = {},
|
|
2534
|
+
): Promise<SessionGoal> {
|
|
2428
2535
|
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`;
|
|
2429
|
-
return await this.sharedRead(
|
|
2536
|
+
return await this.sharedRead(
|
|
2537
|
+
path,
|
|
2538
|
+
(signal) => this.requestJson<SessionGoal>("GET", path, undefined, {}, { signal }),
|
|
2539
|
+
options,
|
|
2540
|
+
);
|
|
2430
2541
|
}
|
|
2431
2542
|
|
|
2432
2543
|
async updateGoal(
|
|
@@ -3787,6 +3898,48 @@ export class OpenGeniClient {
|
|
|
3787
3898
|
);
|
|
3788
3899
|
}
|
|
3789
3900
|
|
|
3901
|
+
async captureComputerTarget(
|
|
3902
|
+
workspaceId: string,
|
|
3903
|
+
computerSessionId: string,
|
|
3904
|
+
targetId: string,
|
|
3905
|
+
options: OpenGeniRequestOptions = {},
|
|
3906
|
+
): Promise<ComputerFrame> {
|
|
3907
|
+
const response = await this.requestResponse(
|
|
3908
|
+
"GET",
|
|
3909
|
+
`/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/targets/${encodeURIComponent(targetId)}/screenshot`,
|
|
3910
|
+
{},
|
|
3911
|
+
options,
|
|
3912
|
+
);
|
|
3913
|
+
const mediaType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
3914
|
+
if (mediaType !== "image/jpeg" && mediaType !== "image/png") {
|
|
3915
|
+
await cancelResponseBody(response, "computer frame media type is invalid");
|
|
3916
|
+
throw new OpenGeniApiError(502, "computer frame media type is invalid");
|
|
3917
|
+
}
|
|
3918
|
+
const metadataHeader = response.headers.get("x-opengeni-computer-frame");
|
|
3919
|
+
if (!metadataHeader || metadataHeader.length > 32 * 1024) {
|
|
3920
|
+
await cancelResponseBody(response, "computer frame metadata is invalid");
|
|
3921
|
+
throw new OpenGeniApiError(502, "computer frame metadata is invalid");
|
|
3922
|
+
}
|
|
3923
|
+
let metadata: ReturnType<typeof decodeComputerFrameMetadataHeader>;
|
|
3924
|
+
try {
|
|
3925
|
+
metadata = decodeComputerFrameMetadataHeader(metadataHeader);
|
|
3926
|
+
} catch {
|
|
3927
|
+
await cancelResponseBody(response, "computer frame metadata is invalid");
|
|
3928
|
+
throw new OpenGeniApiError(502, "computer frame metadata is invalid");
|
|
3929
|
+
}
|
|
3930
|
+
const bytes = await readBoundedResponseBytes(response, 256 * 1024, null);
|
|
3931
|
+
const mismatchReason = computerFrameEvidenceMismatchReason(metadata, {
|
|
3932
|
+
computerSessionId,
|
|
3933
|
+
targetId,
|
|
3934
|
+
mediaType,
|
|
3935
|
+
sha256: await sha256Hex(bytes),
|
|
3936
|
+
});
|
|
3937
|
+
if (mismatchReason) {
|
|
3938
|
+
throw new OpenGeniApiError(502, "computer frame evidence does not match its request");
|
|
3939
|
+
}
|
|
3940
|
+
return { ...metadata, data: bytes };
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3790
3943
|
async actInComputer(
|
|
3791
3944
|
workspaceId: string,
|
|
3792
3945
|
computerSessionId: string,
|
|
@@ -3870,8 +4023,14 @@ export class OpenGeniClient {
|
|
|
3870
4023
|
* expected to authenticate. Drives a composer's model picker without prior
|
|
3871
4024
|
* knowledge of the host setup; safe to call before any auth is established.
|
|
3872
4025
|
*/
|
|
3873
|
-
async getClientConfig(): Promise<ClientConfig> {
|
|
3874
|
-
const config = await this.requestJson<ClientConfig>(
|
|
4026
|
+
async getClientConfig(options: OpenGeniRequestOptions = {}): Promise<ClientConfig> {
|
|
4027
|
+
const config = await this.requestJson<ClientConfig>(
|
|
4028
|
+
"GET",
|
|
4029
|
+
"/v1/config/client",
|
|
4030
|
+
undefined,
|
|
4031
|
+
{},
|
|
4032
|
+
options,
|
|
4033
|
+
);
|
|
3875
4034
|
if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
|
|
3876
4035
|
throw new OpenGeniApiContractMismatchError(
|
|
3877
4036
|
OPENGENI_API_CONTRACT_REVISION,
|
|
@@ -3882,10 +4041,100 @@ export class OpenGeniClient {
|
|
|
3882
4041
|
}
|
|
3883
4042
|
|
|
3884
4043
|
/** Authenticated model definitions plus workspace-specific selectability. */
|
|
3885
|
-
async getWorkspaceModelCatalog(
|
|
4044
|
+
async getWorkspaceModelCatalog(
|
|
4045
|
+
workspaceId: string,
|
|
4046
|
+
options: OpenGeniRequestOptions = {},
|
|
4047
|
+
): Promise<WorkspaceModelCatalogResponse> {
|
|
3886
4048
|
return await this.requestJson<WorkspaceModelCatalogResponse>(
|
|
3887
4049
|
"GET",
|
|
3888
4050
|
`/v1/workspaces/${workspaceId}/model-catalog`,
|
|
4051
|
+
undefined,
|
|
4052
|
+
{},
|
|
4053
|
+
options,
|
|
4054
|
+
);
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
/** List workspace-owned unpinned Vercel AI Gateway model slugs. */
|
|
4058
|
+
async listWorkspaceGatewayCustomModels(
|
|
4059
|
+
workspaceId: string,
|
|
4060
|
+
): Promise<WorkspaceGatewayCustomModelsResponse> {
|
|
4061
|
+
return await this.requestJson<WorkspaceGatewayCustomModelsResponse>(
|
|
4062
|
+
"GET",
|
|
4063
|
+
`/v1/workspaces/${workspaceId}/gateway-custom-models`,
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4067
|
+
/** Add one exact upstream Vercel AI Gateway slug. */
|
|
4068
|
+
async createWorkspaceGatewayCustomModel(
|
|
4069
|
+
workspaceId: string,
|
|
4070
|
+
request: CreateWorkspaceGatewayCustomModelRequest,
|
|
4071
|
+
): Promise<WorkspaceGatewayCustomModel> {
|
|
4072
|
+
return await this.requestJson<WorkspaceGatewayCustomModel>(
|
|
4073
|
+
"POST",
|
|
4074
|
+
`/v1/workspaces/${workspaceId}/gateway-custom-models`,
|
|
4075
|
+
request,
|
|
4076
|
+
);
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
/** Remove one workspace custom Gateway model by its stable row id. */
|
|
4080
|
+
async deleteWorkspaceGatewayCustomModel(
|
|
4081
|
+
workspaceId: string,
|
|
4082
|
+
customModelId: string,
|
|
4083
|
+
request: DeleteWorkspaceGatewayCustomModelRequest,
|
|
4084
|
+
): Promise<void> {
|
|
4085
|
+
if (
|
|
4086
|
+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
|
|
4087
|
+
customModelId,
|
|
4088
|
+
)
|
|
4089
|
+
) {
|
|
4090
|
+
throw new TypeError("customModelId must be a UUID");
|
|
4091
|
+
}
|
|
4092
|
+
await this.requestVoid(
|
|
4093
|
+
"DELETE",
|
|
4094
|
+
`/v1/workspaces/${workspaceId}/gateway-custom-models/${encodeURIComponent(customModelId)}`,
|
|
4095
|
+
request,
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
/** List workspace-owned unpinned OpenRouter model slugs. */
|
|
4100
|
+
async listWorkspaceOpenRouterCustomModels(
|
|
4101
|
+
workspaceId: string,
|
|
4102
|
+
): Promise<WorkspaceOpenRouterCustomModelsResponse> {
|
|
4103
|
+
return await this.requestJson<WorkspaceOpenRouterCustomModelsResponse>(
|
|
4104
|
+
"GET",
|
|
4105
|
+
`/v1/workspaces/${workspaceId}/openrouter-custom-models`,
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
/** Add one exact upstream OpenRouter slug. */
|
|
4110
|
+
async createWorkspaceOpenRouterCustomModel(
|
|
4111
|
+
workspaceId: string,
|
|
4112
|
+
request: CreateWorkspaceOpenRouterCustomModelRequest,
|
|
4113
|
+
): Promise<WorkspaceOpenRouterCustomModel> {
|
|
4114
|
+
return await this.requestJson<WorkspaceOpenRouterCustomModel>(
|
|
4115
|
+
"POST",
|
|
4116
|
+
`/v1/workspaces/${workspaceId}/openrouter-custom-models`,
|
|
4117
|
+
request,
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
/** Remove one workspace custom OpenRouter model by its stable row id. */
|
|
4122
|
+
async deleteWorkspaceOpenRouterCustomModel(
|
|
4123
|
+
workspaceId: string,
|
|
4124
|
+
customModelId: string,
|
|
4125
|
+
request: DeleteWorkspaceOpenRouterCustomModelRequest,
|
|
4126
|
+
): Promise<void> {
|
|
4127
|
+
if (
|
|
4128
|
+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
|
|
4129
|
+
customModelId,
|
|
4130
|
+
)
|
|
4131
|
+
) {
|
|
4132
|
+
throw new TypeError("customModelId must be a UUID");
|
|
4133
|
+
}
|
|
4134
|
+
await this.requestVoid(
|
|
4135
|
+
"DELETE",
|
|
4136
|
+
`/v1/workspaces/${workspaceId}/openrouter-custom-models/${encodeURIComponent(customModelId)}`,
|
|
4137
|
+
request,
|
|
3889
4138
|
);
|
|
3890
4139
|
}
|
|
3891
4140
|
|
|
@@ -3912,10 +4161,14 @@ export class OpenGeniClient {
|
|
|
3912
4161
|
/** Authenticated realtime voice models and credential readiness. */
|
|
3913
4162
|
async getWorkspaceRealtimeModelCatalog(
|
|
3914
4163
|
workspaceId: string,
|
|
4164
|
+
options: OpenGeniRequestOptions = {},
|
|
3915
4165
|
): Promise<WorkspaceRealtimeModelCatalogResponse> {
|
|
3916
4166
|
return await this.requestJson<WorkspaceRealtimeModelCatalogResponse>(
|
|
3917
4167
|
"GET",
|
|
3918
4168
|
`/v1/workspaces/${workspaceId}/realtime-model-catalog`,
|
|
4169
|
+
undefined,
|
|
4170
|
+
{},
|
|
4171
|
+
options,
|
|
3919
4172
|
);
|
|
3920
4173
|
}
|
|
3921
4174
|
|
|
@@ -4271,6 +4524,18 @@ export class OpenGeniClient {
|
|
|
4271
4524
|
return await this.requestJson<Workspace>("POST", "/v1/workspaces", request);
|
|
4272
4525
|
}
|
|
4273
4526
|
|
|
4527
|
+
/**
|
|
4528
|
+
* Create an organization workspace once for a stable external tenant
|
|
4529
|
+
* identity, or return the existing workspace without overwriting it.
|
|
4530
|
+
*/
|
|
4531
|
+
async ensureWorkspace(request: EnsureWorkspaceRequest): Promise<EnsureWorkspaceResponse> {
|
|
4532
|
+
return await this.requestJson<EnsureWorkspaceResponse>(
|
|
4533
|
+
"PUT",
|
|
4534
|
+
"/v1/workspaces/external",
|
|
4535
|
+
request,
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4274
4539
|
async getWorkspace(workspaceId: string): Promise<Workspace> {
|
|
4275
4540
|
return await this.requestJson<Workspace>("GET", `/v1/workspaces/${workspaceId}`);
|
|
4276
4541
|
}
|
|
@@ -4531,6 +4796,24 @@ export class OpenGeniClient {
|
|
|
4531
4796
|
);
|
|
4532
4797
|
}
|
|
4533
4798
|
|
|
4799
|
+
async getCompanyProfileAgentPolicy(workspaceId: string): Promise<CompanyProfileAgentPolicy> {
|
|
4800
|
+
return await this.requestJson<CompanyProfileAgentPolicy>(
|
|
4801
|
+
"GET",
|
|
4802
|
+
`/v1/workspaces/${workspaceId}/company-profile/agent-policy`,
|
|
4803
|
+
);
|
|
4804
|
+
}
|
|
4805
|
+
|
|
4806
|
+
async updateCompanyProfileAgentPolicy(
|
|
4807
|
+
workspaceId: string,
|
|
4808
|
+
request: UpdateCompanyProfileAgentPolicyRequest,
|
|
4809
|
+
): Promise<CompanyProfileAgentPolicy> {
|
|
4810
|
+
return await this.requestJson<CompanyProfileAgentPolicy>(
|
|
4811
|
+
"PATCH",
|
|
4812
|
+
`/v1/workspaces/${workspaceId}/company-profile/agent-policy`,
|
|
4813
|
+
request,
|
|
4814
|
+
);
|
|
4815
|
+
}
|
|
4816
|
+
|
|
4534
4817
|
async updateCompanyProfile(
|
|
4535
4818
|
workspaceId: string,
|
|
4536
4819
|
request: UpdateCompanyProfileRequest,
|
|
@@ -4721,9 +5004,17 @@ export class OpenGeniClient {
|
|
|
4721
5004
|
return response.members;
|
|
4722
5005
|
}
|
|
4723
5006
|
|
|
5007
|
+
/** Active organization members who can be added to this workspace. */
|
|
5008
|
+
async listWorkspaceMemberCandidates(workspaceId: string): Promise<WorkspaceMemberCandidate[]> {
|
|
5009
|
+
const response = await this.requestJson<ListWorkspaceMemberCandidatesResponse>(
|
|
5010
|
+
"GET",
|
|
5011
|
+
`/v1/workspaces/${workspaceId}/member-candidates`,
|
|
5012
|
+
);
|
|
5013
|
+
return response.members;
|
|
5014
|
+
}
|
|
5015
|
+
|
|
4724
5016
|
/**
|
|
4725
|
-
* Add
|
|
4726
|
-
* exists (email invites for unknown users are deferred).
|
|
5017
|
+
* Add one active member selected from the workspace candidate inventory.
|
|
4727
5018
|
*/
|
|
4728
5019
|
async addWorkspaceMember(
|
|
4729
5020
|
workspaceId: string,
|
|
@@ -4848,7 +5139,7 @@ export class OpenGeniClient {
|
|
|
4848
5139
|
return await this.requestJson<ScheduledTask>(
|
|
4849
5140
|
"POST",
|
|
4850
5141
|
`/v1/workspaces/${workspaceId}/scheduled-tasks`,
|
|
4851
|
-
request,
|
|
5142
|
+
normalizeScheduledTaskMachineTarget(request),
|
|
4852
5143
|
);
|
|
4853
5144
|
}
|
|
4854
5145
|
|
|
@@ -4860,7 +5151,7 @@ export class OpenGeniClient {
|
|
|
4860
5151
|
return await this.requestJson<ScheduledTask>(
|
|
4861
5152
|
"PATCH",
|
|
4862
5153
|
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`,
|
|
4863
|
-
request,
|
|
5154
|
+
normalizeScheduledTaskMachineTarget(request),
|
|
4864
5155
|
);
|
|
4865
5156
|
}
|
|
4866
5157
|
|
|
@@ -5280,6 +5571,7 @@ export class OpenGeniClient {
|
|
|
5280
5571
|
) {
|
|
5281
5572
|
throw new Error("File upload timeout must be a positive number");
|
|
5282
5573
|
}
|
|
5574
|
+
assertBrowserFileUploadSecureContext();
|
|
5283
5575
|
const withTimeout = async <T>(
|
|
5284
5576
|
timeoutMs: number,
|
|
5285
5577
|
operation: (signal: AbortSignal) => Promise<T>,
|
|
@@ -6755,6 +7047,34 @@ export class OpenGeniClient {
|
|
|
6755
7047
|
);
|
|
6756
7048
|
}
|
|
6757
7049
|
|
|
7050
|
+
async listOrganizationApiKeys(organizationId: string): Promise<ApiKey[]> {
|
|
7051
|
+
const response = await this.requestJson<ListApiKeysResponse>(
|
|
7052
|
+
"GET",
|
|
7053
|
+
`/v1/organizations/${organizationId}/api-keys`,
|
|
7054
|
+
);
|
|
7055
|
+
return response.apiKeys;
|
|
7056
|
+
}
|
|
7057
|
+
|
|
7058
|
+
/** The returned `token` is shown once; only its prefix is stored. */
|
|
7059
|
+
async createOrganizationApiKey(
|
|
7060
|
+
organizationId: string,
|
|
7061
|
+
request: CreateOrganizationApiKeyRequest,
|
|
7062
|
+
): Promise<CreateApiKeyResponse> {
|
|
7063
|
+
return await this.requestJson<CreateApiKeyResponse>(
|
|
7064
|
+
"POST",
|
|
7065
|
+
`/v1/organizations/${organizationId}/api-keys`,
|
|
7066
|
+
request,
|
|
7067
|
+
);
|
|
7068
|
+
}
|
|
7069
|
+
|
|
7070
|
+
/** Revoke an organization-scoped API key. Returns the revoked key. */
|
|
7071
|
+
async deleteOrganizationApiKey(organizationId: string, apiKeyId: string): Promise<ApiKey> {
|
|
7072
|
+
return await this.requestJson<ApiKey>(
|
|
7073
|
+
"DELETE",
|
|
7074
|
+
`/v1/organizations/${organizationId}/api-keys/${apiKeyId}`,
|
|
7075
|
+
);
|
|
7076
|
+
}
|
|
7077
|
+
|
|
6758
7078
|
// --- Billing (account-scoped) --------------------------------------------------------------------
|
|
6759
7079
|
|
|
6760
7080
|
async getBilling(options: { accountId?: string } = {}): Promise<BillingSummary> {
|
|
@@ -7105,14 +7425,20 @@ export class OpenGeniClient {
|
|
|
7105
7425
|
}
|
|
7106
7426
|
|
|
7107
7427
|
/** Contract-checked JSON transport shared by opt-in typed SDK clients. */
|
|
7108
|
-
private async requestSessionCommand<T>(
|
|
7428
|
+
private async requestSessionCommand<T>(
|
|
7429
|
+
method: string,
|
|
7430
|
+
path: string,
|
|
7431
|
+
body?: unknown,
|
|
7432
|
+
options: OpenGeniRequestOptions = {},
|
|
7433
|
+
): Promise<T> {
|
|
7109
7434
|
return await this.requestJson<T>(
|
|
7110
7435
|
method,
|
|
7111
7436
|
path,
|
|
7112
7437
|
body,
|
|
7113
7438
|
{},
|
|
7114
7439
|
{
|
|
7115
|
-
|
|
7440
|
+
signal: options.signal,
|
|
7441
|
+
timeoutMs: options.timeoutMs ?? this.sessionCommandTimeoutMs,
|
|
7116
7442
|
},
|
|
7117
7443
|
);
|
|
7118
7444
|
}
|
|
@@ -7469,6 +7795,28 @@ function mutationTransportError(correlationId: string): OpenGeniApiError {
|
|
|
7469
7795
|
});
|
|
7470
7796
|
}
|
|
7471
7797
|
|
|
7798
|
+
function assertBrowserFileUploadSecureContext(): void {
|
|
7799
|
+
const secureContext =
|
|
7800
|
+
typeof window !== "undefined"
|
|
7801
|
+
? window.isSecureContext
|
|
7802
|
+
: typeof globalThis.isSecureContext === "boolean"
|
|
7803
|
+
? globalThis.isSecureContext
|
|
7804
|
+
: undefined;
|
|
7805
|
+
// The framework-agnostic SDK also runs in Node, Bun, Deno, and edge
|
|
7806
|
+
// runtimes. Preserve their existing upload behavior; this typed HTTPS error
|
|
7807
|
+
// is only meaningful where the browser exposes a secure-context state.
|
|
7808
|
+
if (secureContext === undefined) return;
|
|
7809
|
+
if (!secureContext) {
|
|
7810
|
+
throw new OpenGeniSecureContextRequiredError("insecure_context");
|
|
7811
|
+
}
|
|
7812
|
+
if (
|
|
7813
|
+
typeof globalThis.crypto === "undefined" ||
|
|
7814
|
+
typeof globalThis.crypto.subtle?.digest !== "function"
|
|
7815
|
+
) {
|
|
7816
|
+
throw new OpenGeniSecureContextRequiredError("web_crypto_unavailable");
|
|
7817
|
+
}
|
|
7818
|
+
}
|
|
7819
|
+
|
|
7472
7820
|
async function sha256ForUpload(body: Blob | ArrayBuffer | string): Promise<string> {
|
|
7473
7821
|
const bytes =
|
|
7474
7822
|
typeof body === "string"
|
|
@@ -7486,6 +7834,30 @@ async function sha256Hex(bytes: Uint8Array): Promise<string> {
|
|
|
7486
7834
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7487
7835
|
}
|
|
7488
7836
|
|
|
7837
|
+
type ComputerFrameEvidenceMismatchReason =
|
|
7838
|
+
| "frame_session_mismatch"
|
|
7839
|
+
| "frame_target_mismatch"
|
|
7840
|
+
| "frame_media_mismatch"
|
|
7841
|
+
| "frame_digest_mismatch";
|
|
7842
|
+
|
|
7843
|
+
function computerFrameEvidenceMismatchReason(
|
|
7844
|
+
metadata: ReturnType<typeof decodeComputerFrameMetadataHeader>,
|
|
7845
|
+
expected: {
|
|
7846
|
+
computerSessionId: string;
|
|
7847
|
+
targetId: string;
|
|
7848
|
+
mediaType: "image/jpeg" | "image/png";
|
|
7849
|
+
sha256: string;
|
|
7850
|
+
},
|
|
7851
|
+
): ComputerFrameEvidenceMismatchReason | null {
|
|
7852
|
+
if (metadata.computerSessionId !== expected.computerSessionId) {
|
|
7853
|
+
return "frame_session_mismatch";
|
|
7854
|
+
}
|
|
7855
|
+
if (metadata.targetId !== expected.targetId) return "frame_target_mismatch";
|
|
7856
|
+
if (metadata.mediaType !== expected.mediaType) return "frame_media_mismatch";
|
|
7857
|
+
if (metadata.sha256 !== expected.sha256) return "frame_digest_mismatch";
|
|
7858
|
+
return null;
|
|
7859
|
+
}
|
|
7860
|
+
|
|
7489
7861
|
async function cancelResponseBody(response: Response, reason: string): Promise<void> {
|
|
7490
7862
|
await response.body?.cancel(reason).catch(() => undefined);
|
|
7491
7863
|
}
|