@opengeni/sdk 0.46.0 → 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-QKV5OCLJ.js → chunk-MBGBLVUV.js} +346 -72
- package/dist/chunk-MBGBLVUV.js.map +1 -0
- package/dist/client.d.ts +27 -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 +80 -10
- package/dist/workspace-control-stream.d.ts +1 -1
- package/package.json +1 -1
- package/src/client.ts +344 -60
- package/src/index.ts +9 -0
- package/src/stream.ts +102 -4
- package/src/transcription.ts +2 -2
- package/src/types.ts +89 -7
- package/src/workspace-control-stream.ts +15 -5
- package/dist/chunk-QKV5OCLJ.js.map +0 -1
package/README.md
CHANGED
|
@@ -408,7 +408,7 @@ Every public endpoint group has typed methods:
|
|
|
408
408
|
| Turn queue | `getQueue`, `moveQueueItem`, `editQueueItem`, `steerQueueItem`, `deleteQueueItem` |
|
|
409
409
|
| Goal | `getGoal`, `updateGoal`, `pauseGoal`, `resumeGoal` |
|
|
410
410
|
| Scheduled tasks | `createScheduledTask`, `listScheduledTasks`, `getScheduledTask`, `updateScheduledTask`, `pauseScheduledTask`, `resumeScheduledTask`, `triggerScheduledTask`, `deleteScheduledTask`, `listScheduledTaskRuns` |
|
|
411
|
-
| Variable sets | `
|
|
411
|
+
| Variable sets | `listVariableSets`, `createVariableSet`, `getVariableSet`, `updateVariableSet`, `deleteVariableSet`, `setVariableSetVariable`, `deleteVariableSetVariable`; generic reads are metadata-only, while dedicated permissioned exact-value reads are part of the held client train |
|
|
412
412
|
| Files | `uploadFile`, `beginFileUpload`, `completeFileUpload`, `getFile`, `createFileDownloadUrl` |
|
|
413
413
|
| Documents | `createDocumentBase`, `listDocumentBases`, `getDocumentBase`, `addDocument`, `listDocuments`, `reindexDocument`, `searchDocuments`, `searchKnowledge` (effective organization + workspace + immutable initiating-user personal scope) |
|
|
414
414
|
| Packs | `listPacks`, `registerPack`, `getPack`, `enablePack`, `deletePack`, `listPackInstallations` |
|
|
@@ -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 {
|
|
@@ -580,7 +680,11 @@ var OpenGeniClient = class {
|
|
|
580
680
|
throw mutationTransportError(correlationId);
|
|
581
681
|
}
|
|
582
682
|
assertApiContractResponse(response);
|
|
583
|
-
if (!response.ok)
|
|
683
|
+
if (!response.ok)
|
|
684
|
+
throw await apiErrorFromResponse(response, {
|
|
685
|
+
method: "PUT",
|
|
686
|
+
correlationId
|
|
687
|
+
});
|
|
584
688
|
await assertJsonResponse(response, { method: "PUT", correlationId });
|
|
585
689
|
const body = await response.json().catch(() => null);
|
|
586
690
|
if (!isUploadTranscriptionRecordingChunkResponse(body)) {
|
|
@@ -649,11 +753,9 @@ var OpenGeniClient = class {
|
|
|
649
753
|
request
|
|
650
754
|
);
|
|
651
755
|
}
|
|
652
|
-
async getSession(workspaceId, sessionId) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}`
|
|
656
|
-
);
|
|
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);
|
|
657
759
|
}
|
|
658
760
|
async updateSession(workspaceId, sessionId, request) {
|
|
659
761
|
return await this.requestJson(
|
|
@@ -746,10 +848,34 @@ var OpenGeniClient = class {
|
|
|
746
848
|
);
|
|
747
849
|
}
|
|
748
850
|
async getSessionLineage(workspaceId, sessionId) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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;
|
|
753
879
|
}
|
|
754
880
|
/** Negotiate one server-mediated connected-Codex GPT-Live V3 WebRTC call. */
|
|
755
881
|
async negotiateCodexRealtimeWebrtc(workspaceId, sessionId, request, options = {}) {
|
|
@@ -826,7 +952,9 @@ var OpenGeniClient = class {
|
|
|
826
952
|
}
|
|
827
953
|
/** Newest turn that durably emitted `turn.started`, or null before any admission. */
|
|
828
954
|
async getLatestStartedTurn(workspaceId, sessionId) {
|
|
829
|
-
const turns = await this.listTurns(workspaceId, sessionId, {
|
|
955
|
+
const turns = await this.listTurns(workspaceId, sessionId, {
|
|
956
|
+
latestStarted: true
|
|
957
|
+
});
|
|
830
958
|
return turns[0] ?? null;
|
|
831
959
|
}
|
|
832
960
|
// --- Bring-your-own-compute: Machines dashboard + metrics (M10) ------------
|
|
@@ -860,6 +988,19 @@ var OpenGeniClient = class {
|
|
|
860
988
|
);
|
|
861
989
|
return response.samples;
|
|
862
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
|
+
}
|
|
863
1004
|
// --- Self-hosted enrollment UX (design 11) --------------------------------
|
|
864
1005
|
/**
|
|
865
1006
|
* Resolve a pending device-enrollment flow by its user_code for the click-Grant
|
|
@@ -1144,9 +1285,10 @@ var OpenGeniClient = class {
|
|
|
1144
1285
|
}
|
|
1145
1286
|
// --- Turn queue ------------------------------------------------------------
|
|
1146
1287
|
async getQueue(workspaceId, sessionId) {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1288
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue`;
|
|
1289
|
+
return await this.singleFlightRead(
|
|
1290
|
+
path,
|
|
1291
|
+
() => this.requestJson("GET", path)
|
|
1150
1292
|
);
|
|
1151
1293
|
}
|
|
1152
1294
|
async moveQueueItem(workspaceId, sessionId, turnId, request) {
|
|
@@ -1314,10 +1456,8 @@ var OpenGeniClient = class {
|
|
|
1314
1456
|
// --- Goals -------------------------------------------------------------------
|
|
1315
1457
|
/** The session's goal. 404s when the session never had one. */
|
|
1316
1458
|
async getGoal(workspaceId, sessionId) {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`
|
|
1320
|
-
);
|
|
1459
|
+
const path = `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`;
|
|
1460
|
+
return await this.singleFlightRead(path, () => this.requestJson("GET", path));
|
|
1321
1461
|
}
|
|
1322
1462
|
async updateGoal(workspaceId, sessionId, request) {
|
|
1323
1463
|
return await this.requestJson(
|
|
@@ -1377,6 +1517,16 @@ var OpenGeniClient = class {
|
|
|
1377
1517
|
options
|
|
1378
1518
|
);
|
|
1379
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
|
+
}
|
|
1380
1530
|
/** FileSystem: read a file (text or base64; binary-safe, size-capped). */
|
|
1381
1531
|
async fsRead(workspaceId, sessionId, request, options = {}) {
|
|
1382
1532
|
return await this.requestJson(
|
|
@@ -1439,6 +1589,16 @@ var OpenGeniClient = class {
|
|
|
1439
1589
|
options
|
|
1440
1590
|
);
|
|
1441
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
|
+
}
|
|
1442
1602
|
/** Git: commit log. */
|
|
1443
1603
|
async gitLog(workspaceId, sessionId, request = {}) {
|
|
1444
1604
|
return await this.requestJson(
|
|
@@ -1911,7 +2071,8 @@ var OpenGeniClient = class {
|
|
|
1911
2071
|
);
|
|
1912
2072
|
}
|
|
1913
2073
|
// --- VariableSets --------------------------------------------------------------
|
|
1914
|
-
//
|
|
2074
|
+
// Generic reads return name/version metadata only. Plaintext uses one
|
|
2075
|
+
// dedicated permissioned endpoint.
|
|
1915
2076
|
async listVariableSets(workspaceId) {
|
|
1916
2077
|
return await this.requestJson(
|
|
1917
2078
|
"GET",
|
|
@@ -1931,6 +2092,12 @@ var OpenGeniClient = class {
|
|
|
1931
2092
|
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`
|
|
1932
2093
|
);
|
|
1933
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
|
+
}
|
|
1934
2101
|
async updateVariableSet(workspaceId, variableSetId, request) {
|
|
1935
2102
|
return await this.requestJson(
|
|
1936
2103
|
"PATCH",
|
|
@@ -1944,7 +2111,7 @@ var OpenGeniClient = class {
|
|
|
1944
2111
|
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`
|
|
1945
2112
|
);
|
|
1946
2113
|
}
|
|
1947
|
-
/** Create or rotate a variable.
|
|
2114
|
+
/** Create or rotate a variable. Generic reads never return its value. */
|
|
1948
2115
|
async setVariableSetVariable(workspaceId, variableSetId, name, value) {
|
|
1949
2116
|
return await this.requestJson(
|
|
1950
2117
|
"PUT",
|
|
@@ -2076,18 +2243,23 @@ var OpenGeniClient = class {
|
|
|
2076
2243
|
}
|
|
2077
2244
|
// --- Files -----------------------------------------------------------------------
|
|
2078
2245
|
/** Step 1 of the upload flow: returns the pre-signed PUT target. */
|
|
2079
|
-
async beginFileUpload(workspaceId, request) {
|
|
2246
|
+
async beginFileUpload(workspaceId, request, options = {}) {
|
|
2080
2247
|
return await this.requestJson(
|
|
2081
2248
|
"POST",
|
|
2082
2249
|
`/v1/workspaces/${workspaceId}/files/uploads`,
|
|
2083
|
-
request
|
|
2250
|
+
request,
|
|
2251
|
+
{},
|
|
2252
|
+
options
|
|
2084
2253
|
);
|
|
2085
2254
|
}
|
|
2086
2255
|
/** Step 3 of the upload flow: server verifies the object and marks it ready. */
|
|
2087
|
-
async completeFileUpload(workspaceId, uploadId) {
|
|
2256
|
+
async completeFileUpload(workspaceId, uploadId, options = {}) {
|
|
2088
2257
|
const response = await this.requestJson(
|
|
2089
2258
|
"POST",
|
|
2090
|
-
`/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete
|
|
2259
|
+
`/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete`,
|
|
2260
|
+
void 0,
|
|
2261
|
+
{},
|
|
2262
|
+
options
|
|
2091
2263
|
);
|
|
2092
2264
|
return response.file;
|
|
2093
2265
|
}
|
|
@@ -2097,35 +2269,68 @@ var OpenGeniClient = class {
|
|
|
2097
2269
|
* -> complete. Returns the ready `FileAsset`.
|
|
2098
2270
|
*/
|
|
2099
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
|
+
};
|
|
2100
2290
|
const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data instanceof ArrayBuffer ? input.data.slice(0) : input.data;
|
|
2101
2291
|
const sizeBytes = typeof body === "string" ? new TextEncoder().encode(body).byteLength : body instanceof Blob ? body.size : body.byteLength;
|
|
2102
2292
|
const sha256 = input.sha256 ?? await sha256ForUpload(body);
|
|
2103
|
-
const upload = await
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
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
|
+
);
|
|
2125
2327
|
if (!putResponse.ok) {
|
|
2126
2328
|
throw await apiErrorFromResponse(putResponse, { method: "PUT" });
|
|
2127
2329
|
}
|
|
2128
|
-
return await
|
|
2330
|
+
return await withTimeout(
|
|
2331
|
+
3e4,
|
|
2332
|
+
async (signal) => await this.completeFileUpload(workspaceId, upload.uploadId, { signal })
|
|
2333
|
+
);
|
|
2129
2334
|
}
|
|
2130
2335
|
async getFile(workspaceId, fileId) {
|
|
2131
2336
|
return await this.requestJson(
|
|
@@ -2145,22 +2350,83 @@ var OpenGeniClient = class {
|
|
|
2145
2350
|
* deliberately does not use the ordinary signed file-download URL.
|
|
2146
2351
|
*/
|
|
2147
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) {
|
|
2148
2417
|
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
2149
2418
|
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
2150
2419
|
}
|
|
2151
2420
|
const correlationId = crypto.randomUUID();
|
|
2152
|
-
const response = await this.fetchImpl(
|
|
2153
|
-
|
|
2154
|
-
{
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
...options.signal ? { signal: options.signal } : {}
|
|
2162
|
-
}
|
|
2163
|
-
);
|
|
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
|
+
});
|
|
2164
2430
|
try {
|
|
2165
2431
|
assertApiContractResponse(response);
|
|
2166
2432
|
} catch (error) {
|
|
@@ -2843,7 +3109,10 @@ function isTranscribeAudioResponse(value) {
|
|
|
2843
3109
|
function isUploadTranscriptionRecordingChunkResponse(value) {
|
|
2844
3110
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2845
3111
|
const record = value;
|
|
2846
|
-
if (!isTranscriptionRecordingResponse({
|
|
3112
|
+
if (!isTranscriptionRecordingResponse({
|
|
3113
|
+
recording: record.recording,
|
|
3114
|
+
segments: []
|
|
3115
|
+
})) {
|
|
2847
3116
|
return false;
|
|
2848
3117
|
}
|
|
2849
3118
|
if (!record.chunk || typeof record.chunk !== "object" || Array.isArray(record.chunk)) {
|
|
@@ -2947,6 +3216,11 @@ async function sha256ForUpload(body) {
|
|
|
2947
3216
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
2948
3217
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2949
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
|
+
}
|
|
2950
3224
|
async function cancelResponseBody(response, reason) {
|
|
2951
3225
|
await response.body?.cancel(reason).catch(() => void 0);
|
|
2952
3226
|
}
|
|
@@ -3239,4 +3513,4 @@ export {
|
|
|
3239
3513
|
authorizeTranscriptionAdapter,
|
|
3240
3514
|
createTranscriptionSessionRequest
|
|
3241
3515
|
};
|
|
3242
|
-
//# sourceMappingURL=chunk-
|
|
3516
|
+
//# sourceMappingURL=chunk-MBGBLVUV.js.map
|