@opengeni/sdk 0.44.6 → 0.48.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 +1 -1
- package/dist/{chunk-ONKUBP7A.js → chunk-MBGBLVUV.js} +489 -70
- package/dist/chunk-MBGBLVUV.js.map +1 -0
- package/dist/client.d.ts +61 -5
- package/dist/core.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/stream.d.ts +15 -0
- package/dist/transcription.d.ts +2 -2
- package/dist/types.d.ts +139 -10
- package/dist/workspace-control-stream.d.ts +1 -1
- package/package.json +1 -1
- package/src/client.ts +594 -58
- package/src/index.ts +9 -0
- package/src/stream.ts +102 -4
- package/src/transcription.ts +2 -2
- package/src/types.ts +181 -7
- package/src/workspace-control-stream.ts +15 -5
- package/dist/chunk-ONKUBP7A.js.map +0 -1
|
@@ -82,6 +82,9 @@ async function* streamSessionEvents(transport, options = {}) {
|
|
|
82
82
|
const reconnect = options.reconnect ?? true;
|
|
83
83
|
const baseDelayMs = options.reconnectDelayMs ?? 500;
|
|
84
84
|
const maxDelayMs = options.maxReconnectDelayMs ?? 1e4;
|
|
85
|
+
const jitterRatio = options.reconnectJitterRatio ?? 0.2;
|
|
86
|
+
const beforeLiveTimeoutMs = options.beforeLiveTimeoutMs ?? 15e3;
|
|
87
|
+
const heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? 45e3;
|
|
85
88
|
const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
86
89
|
let cursor = options.after ?? 0;
|
|
87
90
|
let failedAttempts = 0;
|
|
@@ -96,9 +99,15 @@ async function* streamSessionEvents(transport, options = {}) {
|
|
|
96
99
|
everConnected = true;
|
|
97
100
|
failedAttempts = 0;
|
|
98
101
|
delayMs = baseDelayMs;
|
|
99
|
-
|
|
102
|
+
try {
|
|
103
|
+
options.onOpen?.();
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
await runBeforeLive(options.beforeLive, beforeLiveTimeoutMs, signal);
|
|
100
107
|
options.onStateChange?.("live");
|
|
101
|
-
for await (const message of parseSseStream(
|
|
108
|
+
for await (const message of parseSseStream(
|
|
109
|
+
withStreamInactivityTimeout(body, heartbeatTimeoutMs, signal)
|
|
110
|
+
)) {
|
|
102
111
|
if (signal?.aborted) {
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
@@ -122,7 +131,7 @@ async function* streamSessionEvents(transport, options = {}) {
|
|
|
122
131
|
return;
|
|
123
132
|
}
|
|
124
133
|
if (cursor === cursorAtOpen) {
|
|
125
|
-
await sleep(baseDelayMs, signal);
|
|
134
|
+
await sleep(jitteredDelay(baseDelayMs, jitterRatio), signal);
|
|
126
135
|
}
|
|
127
136
|
continue;
|
|
128
137
|
} catch (error) {
|
|
@@ -139,10 +148,76 @@ async function* streamSessionEvents(transport, options = {}) {
|
|
|
139
148
|
);
|
|
140
149
|
}
|
|
141
150
|
}
|
|
142
|
-
await sleep(delayMs, signal);
|
|
151
|
+
await sleep(jitteredDelay(delayMs, jitterRatio), signal);
|
|
143
152
|
delayMs = Math.min(Math.max(delayMs * 2, baseDelayMs), maxDelayMs);
|
|
144
153
|
}
|
|
145
154
|
}
|
|
155
|
+
async function runBeforeLive(beforeLive, timeoutMs, signal) {
|
|
156
|
+
if (!beforeLive) return;
|
|
157
|
+
let timer;
|
|
158
|
+
let abortListener;
|
|
159
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
160
|
+
timer = setTimeout(
|
|
161
|
+
() => reject(new TypeError(`stream reconciliation timed out after ${timeoutMs}ms`)),
|
|
162
|
+
timeoutMs
|
|
163
|
+
);
|
|
164
|
+
if (signal) {
|
|
165
|
+
abortListener = () => reject(new DOMException("Aborted", "AbortError"));
|
|
166
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
try {
|
|
170
|
+
await Promise.race([Promise.resolve().then(beforeLive), timeout]);
|
|
171
|
+
} finally {
|
|
172
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
173
|
+
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function jitteredDelay(delayMs, ratio) {
|
|
177
|
+
if (delayMs <= 0 || ratio <= 0) return delayMs;
|
|
178
|
+
const boundedRatio = Math.min(ratio, 1);
|
|
179
|
+
const spread = delayMs * boundedRatio;
|
|
180
|
+
return Math.max(0, delayMs - spread + Math.random() * spread * 2);
|
|
181
|
+
}
|
|
182
|
+
function withStreamInactivityTimeout(stream, timeoutMs, signal) {
|
|
183
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
|
|
184
|
+
throw new RangeError("stream heartbeat timeout must be a positive safe integer");
|
|
185
|
+
}
|
|
186
|
+
const reader = stream.getReader();
|
|
187
|
+
return new ReadableStream({
|
|
188
|
+
pull: async (controller) => {
|
|
189
|
+
let timer;
|
|
190
|
+
let abortListener;
|
|
191
|
+
try {
|
|
192
|
+
const result = await Promise.race([
|
|
193
|
+
reader.read(),
|
|
194
|
+
new Promise((_resolve, reject) => {
|
|
195
|
+
timer = setTimeout(
|
|
196
|
+
() => reject(new TypeError(`event stream heartbeat timed out after ${timeoutMs}ms`)),
|
|
197
|
+
timeoutMs
|
|
198
|
+
);
|
|
199
|
+
if (signal) {
|
|
200
|
+
abortListener = () => reject(new DOMException("Aborted", "AbortError"));
|
|
201
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
]);
|
|
205
|
+
if (result.done) controller.close();
|
|
206
|
+
else controller.enqueue(result.value);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
void reader.cancel(error).catch(() => {
|
|
209
|
+
});
|
|
210
|
+
controller.error(error);
|
|
211
|
+
} finally {
|
|
212
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
213
|
+
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
cancel: async (reason) => {
|
|
217
|
+
await reader.cancel(reason);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
146
221
|
async function* backfillEvents(transport, fromExclusive, toInclusive) {
|
|
147
222
|
let cursor = fromExclusive;
|
|
148
223
|
while (cursor < toInclusive) {
|
|
@@ -197,6 +272,9 @@ async function* streamWorkspaceControlEvents(transport, options = {}) {
|
|
|
197
272
|
const reconnect = options.reconnect ?? true;
|
|
198
273
|
const baseDelayMs = options.reconnectDelayMs ?? 500;
|
|
199
274
|
const maxDelayMs = options.maxReconnectDelayMs ?? 1e4;
|
|
275
|
+
const jitterRatio = options.reconnectJitterRatio ?? 0.2;
|
|
276
|
+
const beforeLiveTimeoutMs = options.beforeLiveTimeoutMs ?? 15e3;
|
|
277
|
+
const heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? 45e3;
|
|
200
278
|
const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;
|
|
201
279
|
let cursor = options.after ?? 0;
|
|
202
280
|
let failures = 0;
|
|
@@ -211,9 +289,11 @@ async function* streamWorkspaceControlEvents(transport, options = {}) {
|
|
|
211
289
|
everConnected = true;
|
|
212
290
|
failures = 0;
|
|
213
291
|
delayMs = baseDelayMs;
|
|
214
|
-
await options.beforeLive
|
|
292
|
+
await runBeforeLive(options.beforeLive, beforeLiveTimeoutMs, signal);
|
|
215
293
|
options.onStateChange?.("live");
|
|
216
|
-
for await (const message of parseSseStream(
|
|
294
|
+
for await (const message of parseSseStream(
|
|
295
|
+
withStreamInactivityTimeout(body, heartbeatTimeoutMs, signal)
|
|
296
|
+
)) {
|
|
217
297
|
if (signal?.aborted) return;
|
|
218
298
|
const event = parseWorkspaceControlEvent(message.data);
|
|
219
299
|
if (!event || event.sequence <= cursor) continue;
|
|
@@ -221,7 +301,7 @@ async function* streamWorkspaceControlEvents(transport, options = {}) {
|
|
|
221
301
|
yield event;
|
|
222
302
|
}
|
|
223
303
|
if (!reconnect) return;
|
|
224
|
-
if (cursor === cursorAtOpen) await sleep2(baseDelayMs, signal);
|
|
304
|
+
if (cursor === cursorAtOpen) await sleep2(jitteredDelay(baseDelayMs, jitterRatio), signal);
|
|
225
305
|
continue;
|
|
226
306
|
} catch (error) {
|
|
227
307
|
if (signal?.aborted || isAbortError(error)) return;
|
|
@@ -233,7 +313,7 @@ async function* streamWorkspaceControlEvents(transport, options = {}) {
|
|
|
233
313
|
);
|
|
234
314
|
}
|
|
235
315
|
}
|
|
236
|
-
await sleep2(delayMs, signal);
|
|
316
|
+
await sleep2(jitteredDelay(delayMs, jitterRatio), signal);
|
|
237
317
|
delayMs = Math.min(Math.max(delayMs * 2, baseDelayMs), maxDelayMs);
|
|
238
318
|
}
|
|
239
319
|
}
|
|
@@ -421,8 +501,14 @@ var KNOWN_PERMISSIONS = [
|
|
|
421
501
|
"connections:write",
|
|
422
502
|
"environments:manage",
|
|
423
503
|
"environments:use",
|
|
504
|
+
"variable-sets:list",
|
|
505
|
+
"variable-sets:read",
|
|
506
|
+
"variable-sets:write",
|
|
424
507
|
"variable-sets:manage",
|
|
425
508
|
"variable-sets:use",
|
|
509
|
+
"secrets:list",
|
|
510
|
+
"secrets:read",
|
|
511
|
+
"secrets:write",
|
|
426
512
|
"mcp_servers:attach",
|
|
427
513
|
"toolspace:call",
|
|
428
514
|
"goals:manage",
|
|
@@ -438,6 +524,7 @@ var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
|
|
|
438
524
|
var OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id";
|
|
439
525
|
var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
|
|
440
526
|
var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
|
|
527
|
+
var COMPUTER_SCREENSHOT_MAX_BYTES = 32 * 1024 * 1024;
|
|
441
528
|
var KNOWN_USAGE_EVENT_TYPES = [
|
|
442
529
|
"agent_run.created",
|
|
443
530
|
"agent_run.completed",
|
|
@@ -465,6 +552,8 @@ var OpenGeniClient = class {
|
|
|
465
552
|
baseUrl;
|
|
466
553
|
options;
|
|
467
554
|
fetchImpl;
|
|
555
|
+
readInFlight = /* @__PURE__ */ new Map();
|
|
556
|
+
readTrailing = /* @__PURE__ */ new Map();
|
|
468
557
|
constructor(options) {
|
|
469
558
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
470
559
|
this.options = options;
|
|
@@ -476,7 +565,11 @@ var OpenGeniClient = class {
|
|
|
476
565
|
const correlationId = crypto.randomUUID();
|
|
477
566
|
const form = new FormData();
|
|
478
567
|
const filename = filenameForAudioMimeType(input.mimeType);
|
|
479
|
-
const audio = input.audio instanceof File ? input.audio : input.audio instanceof Uint8Array ? new File([Uint8Array.from(input.audio)], filename, {
|
|
568
|
+
const audio = input.audio instanceof File ? input.audio : input.audio instanceof Uint8Array ? new File([Uint8Array.from(input.audio)], filename, {
|
|
569
|
+
type: input.mimeType
|
|
570
|
+
}) : new File([input.audio], filename, {
|
|
571
|
+
type: input.mimeType || input.audio.type
|
|
572
|
+
});
|
|
480
573
|
form.append("audio", audio, filename);
|
|
481
574
|
form.append("mimeType", input.mimeType);
|
|
482
575
|
if (input.durationSeconds !== void 0) {
|
|
@@ -486,7 +579,10 @@ var OpenGeniClient = class {
|
|
|
486
579
|
try {
|
|
487
580
|
response = await this.fetchImpl(this.url(`/v1/workspaces/${workspaceId}/transcriptions`), {
|
|
488
581
|
method: "POST",
|
|
489
|
-
headers: {
|
|
582
|
+
headers: {
|
|
583
|
+
...this.headers(correlationId),
|
|
584
|
+
Accept: "application/json"
|
|
585
|
+
},
|
|
490
586
|
body: form,
|
|
491
587
|
...input.signal ? { signal: input.signal } : {}
|
|
492
588
|
});
|
|
@@ -495,7 +591,11 @@ var OpenGeniClient = class {
|
|
|
495
591
|
throw mutationTransportError(correlationId);
|
|
496
592
|
}
|
|
497
593
|
assertApiContractResponse(response);
|
|
498
|
-
if (!response.ok)
|
|
594
|
+
if (!response.ok)
|
|
595
|
+
throw await apiErrorFromResponse(response, {
|
|
596
|
+
method: "POST",
|
|
597
|
+
correlationId
|
|
598
|
+
});
|
|
499
599
|
await assertJsonResponse(response, { method: "POST", correlationId });
|
|
500
600
|
let body;
|
|
501
601
|
try {
|
|
@@ -516,6 +616,123 @@ var OpenGeniClient = class {
|
|
|
516
616
|
}
|
|
517
617
|
return body;
|
|
518
618
|
}
|
|
619
|
+
async createTranscriptionRecording(workspaceId, input) {
|
|
620
|
+
return transcriptionRecordingResponse(
|
|
621
|
+
await this.requestJson(
|
|
622
|
+
"POST",
|
|
623
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings`,
|
|
624
|
+
{ recordingId: input.recordingId, mimeType: input.mimeType },
|
|
625
|
+
{},
|
|
626
|
+
input.signal ? { signal: input.signal } : {}
|
|
627
|
+
)
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
async getTranscriptionRecording(workspaceId, recordingId, options = {}) {
|
|
631
|
+
return transcriptionRecordingResponse(
|
|
632
|
+
await this.requestJson(
|
|
633
|
+
"GET",
|
|
634
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings/${recordingId}`,
|
|
635
|
+
void 0,
|
|
636
|
+
{},
|
|
637
|
+
options.signal ? { signal: options.signal } : {}
|
|
638
|
+
)
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
async listTranscriptionRecordings(workspaceId, options = {}) {
|
|
642
|
+
const body = await this.requestJson(
|
|
643
|
+
"GET",
|
|
644
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings`,
|
|
645
|
+
void 0,
|
|
646
|
+
{},
|
|
647
|
+
options.signal ? { signal: options.signal } : {}
|
|
648
|
+
);
|
|
649
|
+
if (!isTranscriptionRecordingListResponse(body)) {
|
|
650
|
+
throw new OpenGeniApiError(502, "Invalid transcription recording list response.", {
|
|
651
|
+
code: "invalid_response"
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
return body;
|
|
655
|
+
}
|
|
656
|
+
async uploadTranscriptionRecordingChunk(workspaceId, recordingId, chunkNumber, input) {
|
|
657
|
+
const correlationId = crypto.randomUUID();
|
|
658
|
+
let response;
|
|
659
|
+
try {
|
|
660
|
+
response = await this.fetchImpl(
|
|
661
|
+
this.url(
|
|
662
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings/${recordingId}/chunks/${chunkNumber}`
|
|
663
|
+
),
|
|
664
|
+
{
|
|
665
|
+
method: "PUT",
|
|
666
|
+
headers: {
|
|
667
|
+
...this.headers(correlationId),
|
|
668
|
+
Accept: "application/json",
|
|
669
|
+
"Content-Type": input.mimeType,
|
|
670
|
+
"x-opengeni-chunk-sha256": input.sha256,
|
|
671
|
+
"x-opengeni-chunk-start-milliseconds": String(input.startMilliseconds),
|
|
672
|
+
"x-opengeni-chunk-duration-milliseconds": String(input.durationMilliseconds)
|
|
673
|
+
},
|
|
674
|
+
body: input.audio instanceof Uint8Array ? Uint8Array.from(input.audio) : input.audio,
|
|
675
|
+
...input.signal ? { signal: input.signal } : {}
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
} catch (error) {
|
|
679
|
+
if (input.signal?.aborted) throw error;
|
|
680
|
+
throw mutationTransportError(correlationId);
|
|
681
|
+
}
|
|
682
|
+
assertApiContractResponse(response);
|
|
683
|
+
if (!response.ok)
|
|
684
|
+
throw await apiErrorFromResponse(response, {
|
|
685
|
+
method: "PUT",
|
|
686
|
+
correlationId
|
|
687
|
+
});
|
|
688
|
+
await assertJsonResponse(response, { method: "PUT", correlationId });
|
|
689
|
+
const body = await response.json().catch(() => null);
|
|
690
|
+
if (!isUploadTranscriptionRecordingChunkResponse(body)) {
|
|
691
|
+
throw new OpenGeniApiError(response.status, "Invalid transcription chunk response.", {
|
|
692
|
+
code: "invalid_response",
|
|
693
|
+
mutation: true,
|
|
694
|
+
correlationId
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
return body;
|
|
698
|
+
}
|
|
699
|
+
async finalizeTranscriptionRecording(workspaceId, recordingId, input) {
|
|
700
|
+
return transcriptionRecordingResponse(
|
|
701
|
+
await this.requestJson(
|
|
702
|
+
"POST",
|
|
703
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings/${recordingId}/finalize`,
|
|
704
|
+
{
|
|
705
|
+
chunkCount: input.chunkCount,
|
|
706
|
+
totalBytes: input.totalBytes,
|
|
707
|
+
totalDurationMilliseconds: input.totalDurationMilliseconds
|
|
708
|
+
},
|
|
709
|
+
{},
|
|
710
|
+
input.signal ? { signal: input.signal } : {}
|
|
711
|
+
)
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
async processNextTranscriptionRecordingSegment(workspaceId, recordingId, options = {}) {
|
|
715
|
+
return transcriptionRecordingResponse(
|
|
716
|
+
await this.requestJson(
|
|
717
|
+
"POST",
|
|
718
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings/${recordingId}/process-next`,
|
|
719
|
+
{},
|
|
720
|
+
{},
|
|
721
|
+
options.signal ? { signal: options.signal } : {}
|
|
722
|
+
)
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
async discardTranscriptionRecording(workspaceId, recordingId, options = {}) {
|
|
726
|
+
return transcriptionRecordingResponse(
|
|
727
|
+
await this.requestJson(
|
|
728
|
+
"DELETE",
|
|
729
|
+
`/v1/workspaces/${workspaceId}/transcription-recordings/${recordingId}`,
|
|
730
|
+
void 0,
|
|
731
|
+
{},
|
|
732
|
+
options.signal ? { signal: options.signal } : {}
|
|
733
|
+
)
|
|
734
|
+
);
|
|
735
|
+
}
|
|
519
736
|
async createSession(workspaceId, request) {
|
|
520
737
|
return await this.requestJson(
|
|
521
738
|
"POST",
|
|
@@ -536,11 +753,9 @@ var OpenGeniClient = class {
|
|
|
536
753
|
request
|
|
537
754
|
);
|
|
538
755
|
}
|
|
539
|
-
async getSession(workspaceId, sessionId) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}`
|
|
543
|
-
);
|
|
756
|
+
async getSession(workspaceId, sessionId, options = {}) {
|
|
757
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}`;
|
|
758
|
+
return await this.singleFlightRead(path, () => this.requestJson("GET", path), options);
|
|
544
759
|
}
|
|
545
760
|
async updateSession(workspaceId, sessionId, request) {
|
|
546
761
|
return await this.requestJson(
|
|
@@ -633,10 +848,34 @@ var OpenGeniClient = class {
|
|
|
633
848
|
);
|
|
634
849
|
}
|
|
635
850
|
async getSessionLineage(workspaceId, sessionId) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
851
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/lineage`;
|
|
852
|
+
return await this.singleFlightRead(
|
|
853
|
+
path,
|
|
854
|
+
() => this.requestJson("GET", path)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
singleFlightRead(key, read, options = {}) {
|
|
858
|
+
const existing = this.readInFlight.get(key);
|
|
859
|
+
if (existing) {
|
|
860
|
+
if (!options.fresh) return existing;
|
|
861
|
+
const queued = this.readTrailing.get(key);
|
|
862
|
+
if (queued) return queued;
|
|
863
|
+
const trailing = existing.then(
|
|
864
|
+
() => this.singleFlightRead(key, read),
|
|
865
|
+
() => this.singleFlightRead(key, read)
|
|
866
|
+
);
|
|
867
|
+
this.readTrailing.set(key, trailing);
|
|
868
|
+
const clear = () => {
|
|
869
|
+
if (this.readTrailing.get(key) === trailing) this.readTrailing.delete(key);
|
|
870
|
+
};
|
|
871
|
+
void trailing.then(clear, clear);
|
|
872
|
+
return trailing;
|
|
873
|
+
}
|
|
874
|
+
const promise = read().finally(() => {
|
|
875
|
+
if (this.readInFlight.get(key) === promise) this.readInFlight.delete(key);
|
|
876
|
+
});
|
|
877
|
+
this.readInFlight.set(key, promise);
|
|
878
|
+
return promise;
|
|
640
879
|
}
|
|
641
880
|
/** Negotiate one server-mediated connected-Codex GPT-Live V3 WebRTC call. */
|
|
642
881
|
async negotiateCodexRealtimeWebrtc(workspaceId, sessionId, request, options = {}) {
|
|
@@ -713,7 +952,9 @@ var OpenGeniClient = class {
|
|
|
713
952
|
}
|
|
714
953
|
/** Newest turn that durably emitted `turn.started`, or null before any admission. */
|
|
715
954
|
async getLatestStartedTurn(workspaceId, sessionId) {
|
|
716
|
-
const turns = await this.listTurns(workspaceId, sessionId, {
|
|
955
|
+
const turns = await this.listTurns(workspaceId, sessionId, {
|
|
956
|
+
latestStarted: true
|
|
957
|
+
});
|
|
717
958
|
return turns[0] ?? null;
|
|
718
959
|
}
|
|
719
960
|
// --- Bring-your-own-compute: Machines dashboard + metrics (M10) ------------
|
|
@@ -747,6 +988,19 @@ var OpenGeniClient = class {
|
|
|
747
988
|
);
|
|
748
989
|
return response.samples;
|
|
749
990
|
}
|
|
991
|
+
/**
|
|
992
|
+
* Remove one connected self-hosted machine enrollment. The control-plane
|
|
993
|
+
* operation works while the agent is offline, revokes future reconnects,
|
|
994
|
+
* retains history, and returns a typed blocker when active route/lease or
|
|
995
|
+
* recovery dependencies make removal unsafe. `idempotencyKey` is replay-safe.
|
|
996
|
+
*/
|
|
997
|
+
async removeEnrollment(workspaceId, enrollmentId, request = {}) {
|
|
998
|
+
return await this.requestJson(
|
|
999
|
+
"POST",
|
|
1000
|
+
`/v1/workspaces/${workspaceId}/enrollments/${enrollmentId}/revoke`,
|
|
1001
|
+
request
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
750
1004
|
// --- Self-hosted enrollment UX (design 11) --------------------------------
|
|
751
1005
|
/**
|
|
752
1006
|
* Resolve a pending device-enrollment flow by its user_code for the click-Grant
|
|
@@ -1031,9 +1285,10 @@ var OpenGeniClient = class {
|
|
|
1031
1285
|
}
|
|
1032
1286
|
// --- Turn queue ------------------------------------------------------------
|
|
1033
1287
|
async getQueue(workspaceId, sessionId) {
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1288
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue`;
|
|
1289
|
+
return await this.singleFlightRead(
|
|
1290
|
+
path,
|
|
1291
|
+
() => this.requestJson("GET", path)
|
|
1037
1292
|
);
|
|
1038
1293
|
}
|
|
1039
1294
|
async moveQueueItem(workspaceId, sessionId, turnId, request) {
|
|
@@ -1201,10 +1456,8 @@ var OpenGeniClient = class {
|
|
|
1201
1456
|
// --- Goals -------------------------------------------------------------------
|
|
1202
1457
|
/** The session's goal. 404s when the session never had one. */
|
|
1203
1458
|
async getGoal(workspaceId, sessionId) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`
|
|
1207
|
-
);
|
|
1459
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`;
|
|
1460
|
+
return await this.singleFlightRead(path, () => this.requestJson("GET", path));
|
|
1208
1461
|
}
|
|
1209
1462
|
async updateGoal(workspaceId, sessionId, request) {
|
|
1210
1463
|
return await this.requestJson(
|
|
@@ -1264,6 +1517,16 @@ var OpenGeniClient = class {
|
|
|
1264
1517
|
options
|
|
1265
1518
|
);
|
|
1266
1519
|
}
|
|
1520
|
+
/** FileSystem: hydrate several independent directories behind one sandbox lease. */
|
|
1521
|
+
async fsListBatch(workspaceId, sessionId, request, options = {}) {
|
|
1522
|
+
return await this.requestJson(
|
|
1523
|
+
"POST",
|
|
1524
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list-batch`,
|
|
1525
|
+
request,
|
|
1526
|
+
{},
|
|
1527
|
+
options
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1267
1530
|
/** FileSystem: read a file (text or base64; binary-safe, size-capped). */
|
|
1268
1531
|
async fsRead(workspaceId, sessionId, request, options = {}) {
|
|
1269
1532
|
return await this.requestJson(
|
|
@@ -1326,6 +1589,16 @@ var OpenGeniClient = class {
|
|
|
1326
1589
|
options
|
|
1327
1590
|
);
|
|
1328
1591
|
}
|
|
1592
|
+
/** Git: read status and optional diffs for several repositories behind one sandbox lease. */
|
|
1593
|
+
async gitReadBatch(workspaceId, sessionId, request, options = {}) {
|
|
1594
|
+
return await this.requestJson(
|
|
1595
|
+
"POST",
|
|
1596
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/read-batch`,
|
|
1597
|
+
request,
|
|
1598
|
+
{},
|
|
1599
|
+
options
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1329
1602
|
/** Git: commit log. */
|
|
1330
1603
|
async gitLog(workspaceId, sessionId, request = {}) {
|
|
1331
1604
|
return await this.requestJson(
|
|
@@ -1798,7 +2071,8 @@ var OpenGeniClient = class {
|
|
|
1798
2071
|
);
|
|
1799
2072
|
}
|
|
1800
2073
|
// --- VariableSets --------------------------------------------------------------
|
|
1801
|
-
//
|
|
2074
|
+
// Generic reads return name/version metadata only. Plaintext uses one
|
|
2075
|
+
// dedicated permissioned endpoint.
|
|
1802
2076
|
async listVariableSets(workspaceId) {
|
|
1803
2077
|
return await this.requestJson(
|
|
1804
2078
|
"GET",
|
|
@@ -1818,6 +2092,12 @@ var OpenGeniClient = class {
|
|
|
1818
2092
|
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`
|
|
1819
2093
|
);
|
|
1820
2094
|
}
|
|
2095
|
+
async getVariableSetVariable(workspaceId, variableSetId, name) {
|
|
2096
|
+
return await this.requestJson(
|
|
2097
|
+
"GET",
|
|
2098
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}/variables/${encodeURIComponent(name)}`
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
1821
2101
|
async updateVariableSet(workspaceId, variableSetId, request) {
|
|
1822
2102
|
return await this.requestJson(
|
|
1823
2103
|
"PATCH",
|
|
@@ -1831,7 +2111,7 @@ var OpenGeniClient = class {
|
|
|
1831
2111
|
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`
|
|
1832
2112
|
);
|
|
1833
2113
|
}
|
|
1834
|
-
/** Create or rotate a variable.
|
|
2114
|
+
/** Create or rotate a variable. Generic reads never return its value. */
|
|
1835
2115
|
async setVariableSetVariable(workspaceId, variableSetId, name, value) {
|
|
1836
2116
|
return await this.requestJson(
|
|
1837
2117
|
"PUT",
|
|
@@ -1963,18 +2243,23 @@ var OpenGeniClient = class {
|
|
|
1963
2243
|
}
|
|
1964
2244
|
// --- Files -----------------------------------------------------------------------
|
|
1965
2245
|
/** Step 1 of the upload flow: returns the pre-signed PUT target. */
|
|
1966
|
-
async beginFileUpload(workspaceId, request) {
|
|
2246
|
+
async beginFileUpload(workspaceId, request, options = {}) {
|
|
1967
2247
|
return await this.requestJson(
|
|
1968
2248
|
"POST",
|
|
1969
2249
|
`/v1/workspaces/${workspaceId}/files/uploads`,
|
|
1970
|
-
request
|
|
2250
|
+
request,
|
|
2251
|
+
{},
|
|
2252
|
+
options
|
|
1971
2253
|
);
|
|
1972
2254
|
}
|
|
1973
2255
|
/** Step 3 of the upload flow: server verifies the object and marks it ready. */
|
|
1974
|
-
async completeFileUpload(workspaceId, uploadId) {
|
|
2256
|
+
async completeFileUpload(workspaceId, uploadId, options = {}) {
|
|
1975
2257
|
const response = await this.requestJson(
|
|
1976
2258
|
"POST",
|
|
1977
|
-
`/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete
|
|
2259
|
+
`/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete`,
|
|
2260
|
+
void 0,
|
|
2261
|
+
{},
|
|
2262
|
+
options
|
|
1978
2263
|
);
|
|
1979
2264
|
return response.file;
|
|
1980
2265
|
}
|
|
@@ -1984,35 +2269,68 @@ var OpenGeniClient = class {
|
|
|
1984
2269
|
* -> complete. Returns the ready `FileAsset`.
|
|
1985
2270
|
*/
|
|
1986
2271
|
async uploadFile(workspaceId, input) {
|
|
2272
|
+
if (input.timeoutMs !== void 0 && (!Number.isFinite(input.timeoutMs) || input.timeoutMs <= 0)) {
|
|
2273
|
+
throw new Error("File upload timeout must be a positive number");
|
|
2274
|
+
}
|
|
2275
|
+
const withTimeout = async (timeoutMs, operation) => {
|
|
2276
|
+
const controller = new AbortController();
|
|
2277
|
+
let timeout;
|
|
2278
|
+
const timedOut = new Promise((_resolve, reject) => {
|
|
2279
|
+
timeout = setTimeout(() => {
|
|
2280
|
+
controller.abort();
|
|
2281
|
+
reject(new Error("File upload timed out. Retry the upload."));
|
|
2282
|
+
}, timeoutMs);
|
|
2283
|
+
});
|
|
2284
|
+
try {
|
|
2285
|
+
return await Promise.race([operation(controller.signal), timedOut]);
|
|
2286
|
+
} finally {
|
|
2287
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
1987
2290
|
const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data instanceof ArrayBuffer ? input.data.slice(0) : input.data;
|
|
1988
2291
|
const sizeBytes = typeof body === "string" ? new TextEncoder().encode(body).byteLength : body instanceof Blob ? body.size : body.byteLength;
|
|
1989
2292
|
const sha256 = input.sha256 ?? await sha256ForUpload(body);
|
|
1990
|
-
const upload = await
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2293
|
+
const upload = await withTimeout(
|
|
2294
|
+
3e4,
|
|
2295
|
+
async (signal) => await this.beginFileUpload(
|
|
2296
|
+
workspaceId,
|
|
2297
|
+
{
|
|
2298
|
+
filename: input.filename,
|
|
2299
|
+
contentType: input.contentType,
|
|
2300
|
+
sizeBytes,
|
|
2301
|
+
sha256
|
|
2302
|
+
},
|
|
2303
|
+
{ signal }
|
|
2304
|
+
)
|
|
2305
|
+
);
|
|
2306
|
+
const transferTimeoutMs = input.timeoutMs ?? Math.max(12e4, Math.ceil(sizeBytes / (256 * 1024)) * 1e3);
|
|
2307
|
+
const putResponse = await withTimeout(
|
|
2308
|
+
transferTimeoutMs,
|
|
2309
|
+
async (signal) => await this.fetchImpl(upload.putUrl, {
|
|
2310
|
+
method: "PUT",
|
|
2311
|
+
// Signed object-storage URLs carry their own short-lived authority.
|
|
2312
|
+
// Browser cookies and HTTP auth must never accompany this cross-origin
|
|
2313
|
+
// request: credentialed fetches are incompatible with wildcard CORS and
|
|
2314
|
+
// can leak ambient credentials to a caller-selected storage endpoint.
|
|
2315
|
+
credentials: "omit",
|
|
2316
|
+
// The backend's requiredHeaders already carry the canonical lowercase
|
|
2317
|
+
// `content-type` for every storage backend (Azure/S3/GCS). Do NOT also set
|
|
2318
|
+
// a `Content-Type` key here: WHATWG Headers treats the two casings as the
|
|
2319
|
+
// same header and comma-joins their values (e.g. "text/plain, text/plain"),
|
|
2320
|
+
// which the object store persists verbatim and COMPLETE then rejects (422),
|
|
2321
|
+
// and which breaks S3's presigned-URL signature.
|
|
2322
|
+
headers: { ...upload.requiredHeaders },
|
|
2323
|
+
body,
|
|
2324
|
+
signal
|
|
2325
|
+
})
|
|
2326
|
+
);
|
|
2012
2327
|
if (!putResponse.ok) {
|
|
2013
2328
|
throw await apiErrorFromResponse(putResponse, { method: "PUT" });
|
|
2014
2329
|
}
|
|
2015
|
-
return await
|
|
2330
|
+
return await withTimeout(
|
|
2331
|
+
3e4,
|
|
2332
|
+
async (signal) => await this.completeFileUpload(workspaceId, upload.uploadId, { signal })
|
|
2333
|
+
);
|
|
2016
2334
|
}
|
|
2017
2335
|
async getFile(workspaceId, fileId) {
|
|
2018
2336
|
return await this.requestJson(
|
|
@@ -2032,22 +2350,83 @@ var OpenGeniClient = class {
|
|
|
2032
2350
|
* deliberately does not use the ordinary signed file-download URL.
|
|
2033
2351
|
*/
|
|
2034
2352
|
async getRetainedArtifactContent(workspaceId, artifactId, options = {}) {
|
|
2353
|
+
return await this.getRetainedArtifactContentAtPath(
|
|
2354
|
+
`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`,
|
|
2355
|
+
options
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
async getSessionRetainedArtifact(workspaceId, sessionId, artifactId) {
|
|
2359
|
+
return await this.requestJson(
|
|
2360
|
+
"GET",
|
|
2361
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/artifacts/${artifactId}`
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
async getSessionRetainedArtifactContent(workspaceId, sessionId, artifactId, options = {}) {
|
|
2365
|
+
return await this.getRetainedArtifactContentAtPath(
|
|
2366
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/artifacts/${artifactId}/content`,
|
|
2367
|
+
options
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
/** Assemble one retained screenshot from bounded authenticated API ranges. */
|
|
2371
|
+
async downloadRetainedScreenshot(workspaceId, sessionId, artifactId, options = {}) {
|
|
2372
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
2373
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0 || maxRetries > 3) {
|
|
2374
|
+
throw new RangeError("retained screenshot maxRetries must be an integer from 0 to 3");
|
|
2375
|
+
}
|
|
2376
|
+
const metadata = await this.getSessionRetainedArtifact(workspaceId, sessionId, artifactId);
|
|
2377
|
+
if (!metadata.available) return { metadata, bytes: null };
|
|
2378
|
+
if (metadata.kind !== "computer_screenshot" || metadata.contentType !== "image/png" || !metadata.dimensions || metadata.originalBytes <= 0 || metadata.originalBytes > COMPUTER_SCREENSHOT_MAX_BYTES) {
|
|
2379
|
+
throw new OpenGeniApiError(502, "retained screenshot metadata is invalid");
|
|
2380
|
+
}
|
|
2381
|
+
const bytes = new Uint8Array(metadata.originalBytes);
|
|
2382
|
+
const pageBytes = Math.min(metadata.retrieval.maxRangeBytes, RETAINED_OUTPUT_MAX_PAGE_BYTES);
|
|
2383
|
+
if (!Number.isSafeInteger(pageBytes) || pageBytes <= 0) {
|
|
2384
|
+
throw new OpenGeniApiError(502, "retained screenshot range metadata is invalid");
|
|
2385
|
+
}
|
|
2386
|
+
for (let start = 0; start < bytes.byteLength; start += pageBytes) {
|
|
2387
|
+
options.signal?.throwIfAborted();
|
|
2388
|
+
const end = Math.min(start + pageBytes, bytes.byteLength) - 1;
|
|
2389
|
+
let page = null;
|
|
2390
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
2391
|
+
try {
|
|
2392
|
+
page = await this.getSessionRetainedArtifactContent(workspaceId, sessionId, artifactId, {
|
|
2393
|
+
range: `bytes=${start}-${end}`,
|
|
2394
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2395
|
+
});
|
|
2396
|
+
break;
|
|
2397
|
+
} catch (error) {
|
|
2398
|
+
options.signal?.throwIfAborted();
|
|
2399
|
+
if (attempt >= maxRetries || error instanceof OpenGeniApiError && error.status >= 400 && error.status < 500) {
|
|
2400
|
+
throw error;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
if (!page) throw new OpenGeniApiError(502, "retained screenshot range retry exhausted");
|
|
2405
|
+
const expectedLength = end - start + 1;
|
|
2406
|
+
if (page.status !== 206 || page.contentType !== metadata.contentType || page.contentLength !== expectedLength || page.contentRange !== `bytes ${start}-${end}/${metadata.originalBytes}`) {
|
|
2407
|
+
throw new OpenGeniApiError(502, "retained screenshot range response is invalid");
|
|
2408
|
+
}
|
|
2409
|
+
bytes.set(page.bytes, start);
|
|
2410
|
+
}
|
|
2411
|
+
if (await sha256Hex(bytes) !== metadata.sha256) {
|
|
2412
|
+
throw new OpenGeniApiError(502, "retained screenshot checksum mismatch");
|
|
2413
|
+
}
|
|
2414
|
+
return { metadata, bytes };
|
|
2415
|
+
}
|
|
2416
|
+
async getRetainedArtifactContentAtPath(path, options) {
|
|
2035
2417
|
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
2036
2418
|
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
2037
2419
|
}
|
|
2038
2420
|
const correlationId = crypto.randomUUID();
|
|
2039
|
-
const response = await this.fetchImpl(
|
|
2040
|
-
|
|
2041
|
-
{
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
...options.signal ? { signal: options.signal } : {}
|
|
2049
|
-
}
|
|
2050
|
-
);
|
|
2421
|
+
const response = await this.fetchImpl(this.url(path), {
|
|
2422
|
+
method: "GET",
|
|
2423
|
+
headers: {
|
|
2424
|
+
...this.headers(correlationId),
|
|
2425
|
+
Accept: "application/octet-stream",
|
|
2426
|
+
...options.range ? { Range: options.range } : {}
|
|
2427
|
+
},
|
|
2428
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2429
|
+
});
|
|
2051
2430
|
try {
|
|
2052
2431
|
assertApiContractResponse(response);
|
|
2053
2432
|
} catch (error) {
|
|
@@ -2727,6 +3106,41 @@ function isTranscribeAudioResponse(value) {
|
|
|
2727
3106
|
const record = value;
|
|
2728
3107
|
return typeof record.text === "string" && Array.isArray(record.languages) && record.languages.every((language) => typeof language === "string");
|
|
2729
3108
|
}
|
|
3109
|
+
function isUploadTranscriptionRecordingChunkResponse(value) {
|
|
3110
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3111
|
+
const record = value;
|
|
3112
|
+
if (!isTranscriptionRecordingResponse({
|
|
3113
|
+
recording: record.recording,
|
|
3114
|
+
segments: []
|
|
3115
|
+
})) {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
if (!record.chunk || typeof record.chunk !== "object" || Array.isArray(record.chunk)) {
|
|
3119
|
+
return false;
|
|
3120
|
+
}
|
|
3121
|
+
const chunk = record.chunk;
|
|
3122
|
+
return typeof chunk.chunkNumber === "number" && typeof chunk.byteLength === "number" && typeof chunk.sha256 === "string" && typeof chunk.startMilliseconds === "number" && typeof chunk.durationMilliseconds === "number" && typeof chunk.deduplicated === "boolean";
|
|
3123
|
+
}
|
|
3124
|
+
function isTranscriptionRecordingResponse(value) {
|
|
3125
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3126
|
+
const response = value;
|
|
3127
|
+
if (!response.recording || typeof response.recording !== "object" || Array.isArray(response.recording)) {
|
|
3128
|
+
return false;
|
|
3129
|
+
}
|
|
3130
|
+
const recording = response.recording;
|
|
3131
|
+
return typeof recording.id === "string" && typeof recording.workspaceId === "string" && typeof recording.mimeType === "string" && typeof recording.state === "string" && typeof recording.nextChunkNumber === "number" && typeof recording.chunkCount === "number" && typeof recording.totalBytes === "number" && typeof recording.totalDurationMilliseconds === "number" && typeof recording.segmentCount === "number" && typeof recording.completedSegmentCount === "number" && (recording.transcriptText === null || typeof recording.transcriptText === "string") && Array.isArray(recording.languages) && (recording.errorCode === null || typeof recording.errorCode === "string") && typeof recording.retryable === "boolean" && typeof recording.objectsCleaned === "boolean" && typeof recording.createdAt === "string" && typeof recording.updatedAt === "string" && typeof recording.expiresAt === "string" && (response.retryAfterMilliseconds === void 0 || typeof response.retryAfterMilliseconds === "number" && Number.isInteger(response.retryAfterMilliseconds) && response.retryAfterMilliseconds > 0 && response.retryAfterMilliseconds <= 6e4) && Array.isArray(response.segments);
|
|
3132
|
+
}
|
|
3133
|
+
function transcriptionRecordingResponse(value) {
|
|
3134
|
+
if (isTranscriptionRecordingResponse(value)) return value;
|
|
3135
|
+
throw new OpenGeniApiError(502, "Invalid transcription recording response.", {
|
|
3136
|
+
code: "invalid_response"
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
function isTranscriptionRecordingListResponse(value) {
|
|
3140
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3141
|
+
const recordings = value.recordings;
|
|
3142
|
+
return Array.isArray(recordings) && recordings.length <= 50 && recordings.every((recording) => isTranscriptionRecordingResponse({ recording, segments: [] }));
|
|
3143
|
+
}
|
|
2730
3144
|
function filenameForAudioMimeType(mimeType) {
|
|
2731
3145
|
const bare = mimeType.trim().toLowerCase().split(";")[0] ?? "audio/webm";
|
|
2732
3146
|
switch (bare) {
|
|
@@ -2802,6 +3216,11 @@ async function sha256ForUpload(body) {
|
|
|
2802
3216
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
2803
3217
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2804
3218
|
}
|
|
3219
|
+
async function sha256Hex(bytes) {
|
|
3220
|
+
const owned = Uint8Array.from(bytes);
|
|
3221
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", owned.buffer);
|
|
3222
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3223
|
+
}
|
|
2805
3224
|
async function cancelResponseBody(response, reason) {
|
|
2806
3225
|
await response.body?.cancel(reason).catch(() => void 0);
|
|
2807
3226
|
}
|
|
@@ -3094,4 +3513,4 @@ export {
|
|
|
3094
3513
|
authorizeTranscriptionAdapter,
|
|
3095
3514
|
createTranscriptionSessionRequest
|
|
3096
3515
|
};
|
|
3097
|
-
//# sourceMappingURL=chunk-
|
|
3516
|
+
//# sourceMappingURL=chunk-MBGBLVUV.js.map
|