@opengeni/sdk 0.15.0 → 0.23.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 +49 -6
- package/dist/index.d.ts +781 -29
- package/dist/index.js +562 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +468 -27
- package/src/index.ts +85 -0
- package/src/transcription.ts +496 -0
- package/src/types.ts +701 -23
package/src/client.ts
CHANGED
|
@@ -16,6 +16,8 @@ import type {
|
|
|
16
16
|
CodexAccount,
|
|
17
17
|
CodexAccountsResponse,
|
|
18
18
|
CodexRotationSettings,
|
|
19
|
+
CodexOverviewResponse,
|
|
20
|
+
CodexAllocatorUpdate,
|
|
19
21
|
CodexConnectionStatus,
|
|
20
22
|
CodexConnectPoll,
|
|
21
23
|
CodexConnectStart,
|
|
@@ -28,6 +30,7 @@ import type {
|
|
|
28
30
|
CapabilityInstallation,
|
|
29
31
|
AddDocumentRequest,
|
|
30
32
|
ClientConfig,
|
|
33
|
+
WorkspaceModelCatalogResponse,
|
|
31
34
|
ClientSessionEventInput,
|
|
32
35
|
CompactSessionContextResult,
|
|
33
36
|
CompleteFileUploadResponse,
|
|
@@ -46,6 +49,7 @@ import type {
|
|
|
46
49
|
CreateKnowledgeMemoryRequest,
|
|
47
50
|
CreateScheduledTaskRequest,
|
|
48
51
|
CreateSessionRequest,
|
|
52
|
+
CreateSessionResponse,
|
|
49
53
|
CreateVariableSetRequest,
|
|
50
54
|
CreateRigRequest,
|
|
51
55
|
CreateWorkspaceRequest,
|
|
@@ -81,6 +85,9 @@ import type {
|
|
|
81
85
|
ListWorkspaceMembersResponse,
|
|
82
86
|
PackInstallation,
|
|
83
87
|
ReasoningEffort,
|
|
88
|
+
RetainedArtifactContent,
|
|
89
|
+
RetainedArtifactContentOptions,
|
|
90
|
+
RetainedArtifactMetadata,
|
|
84
91
|
RegisterCapabilityPackRequest,
|
|
85
92
|
ResourceRef,
|
|
86
93
|
ScheduledTask,
|
|
@@ -89,9 +96,16 @@ import type {
|
|
|
89
96
|
SessionListResponse,
|
|
90
97
|
UpdateSessionPinRequest,
|
|
91
98
|
SessionEvent,
|
|
99
|
+
SessionEventCompactResult,
|
|
100
|
+
SessionEventCompactResultOptions,
|
|
101
|
+
SessionEventListOptions,
|
|
102
|
+
SessionEventPage,
|
|
92
103
|
SessionGoal,
|
|
104
|
+
SessionHumanInputRequest,
|
|
93
105
|
SessionLineageResponse,
|
|
94
106
|
SessionMcpCredentialUpdateInput,
|
|
107
|
+
UpdateSessionMcpApprovalPolicyRequest,
|
|
108
|
+
UpdateSessionMcpApprovalPolicyResponse,
|
|
95
109
|
SessionQueueSnapshot,
|
|
96
110
|
SessionQueueMutationResponse,
|
|
97
111
|
ComposerDraft,
|
|
@@ -104,6 +118,7 @@ import type {
|
|
|
104
118
|
WorkspaceInferenceControlResponse,
|
|
105
119
|
WorkspaceControlEvent,
|
|
106
120
|
SessionTurn,
|
|
121
|
+
SubmitHumanInputResponseRequest,
|
|
107
122
|
// Stream surfacing (Phase 5): capability negotiation + viewer lifecycle + config.
|
|
108
123
|
SessionCapabilities,
|
|
109
124
|
AttachViewerRequest,
|
|
@@ -133,7 +148,7 @@ import type {
|
|
|
133
148
|
GitLogResponse,
|
|
134
149
|
GitShowRequest,
|
|
135
150
|
GitShowResponse,
|
|
136
|
-
// Workbench v2 turn-end capture reads (M2
|
|
151
|
+
// Workbench v2 turn-end capture reads (M2).
|
|
137
152
|
GetWorkspaceCaptureResponse,
|
|
138
153
|
GetWorkspaceCaptureFileResponse,
|
|
139
154
|
TerminalExecRequest,
|
|
@@ -172,10 +187,21 @@ import type {
|
|
|
172
187
|
OAuthStartRequest,
|
|
173
188
|
OAuthStartResponse,
|
|
174
189
|
} from "./types";
|
|
175
|
-
import {
|
|
190
|
+
import {
|
|
191
|
+
OPENGENI_API_CONTRACT_HEADER,
|
|
192
|
+
OPENGENI_API_CONTRACT_REVISION,
|
|
193
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
194
|
+
} from "./types";
|
|
176
195
|
|
|
177
196
|
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
178
197
|
|
|
198
|
+
export type WorkspaceControlEventPage = {
|
|
199
|
+
events: WorkspaceControlEvent[];
|
|
200
|
+
bytes: number;
|
|
201
|
+
truncated: boolean;
|
|
202
|
+
nextAfter: number | null;
|
|
203
|
+
};
|
|
204
|
+
|
|
179
205
|
export type OpenGeniClientOptions = {
|
|
180
206
|
/** Base URL of the OpenGeni API, e.g. `https://api.example.com`. */
|
|
181
207
|
baseUrl: string;
|
|
@@ -187,8 +213,15 @@ export type OpenGeniClientOptions = {
|
|
|
187
213
|
fetch?: FetchLike;
|
|
188
214
|
};
|
|
189
215
|
|
|
216
|
+
/** Per-request cancellation for identity-scoped, side-effect-free reads. */
|
|
217
|
+
export type OpenGeniRequestOptions = {
|
|
218
|
+
signal?: AbortSignal | undefined;
|
|
219
|
+
};
|
|
220
|
+
|
|
190
221
|
export type SendMessageInput = {
|
|
191
222
|
text: string;
|
|
223
|
+
/** System instructions scoped to this exact turn; never visible timeline text. */
|
|
224
|
+
turnInstructions?: string;
|
|
192
225
|
resources?: ResourceRef[];
|
|
193
226
|
tools?: ToolRef[];
|
|
194
227
|
model?: string;
|
|
@@ -225,8 +258,11 @@ export class OpenGeniClient {
|
|
|
225
258
|
|
|
226
259
|
// --- Session lifecycle ---------------------------------------------------
|
|
227
260
|
|
|
228
|
-
async createSession(
|
|
229
|
-
|
|
261
|
+
async createSession(
|
|
262
|
+
workspaceId: string,
|
|
263
|
+
request: CreateSessionRequest,
|
|
264
|
+
): Promise<CreateSessionResponse> {
|
|
265
|
+
return await this.requestJson<CreateSessionResponse>(
|
|
230
266
|
"POST",
|
|
231
267
|
`/v1/workspaces/${workspaceId}/sessions`,
|
|
232
268
|
request,
|
|
@@ -252,6 +288,24 @@ export class OpenGeniClient {
|
|
|
252
288
|
);
|
|
253
289
|
}
|
|
254
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Replace one attached MCP server's approval policy. The change is captured
|
|
293
|
+
* by the next claimed attempt; already-claimed work keeps its immutable
|
|
294
|
+
* policy snapshot.
|
|
295
|
+
*/
|
|
296
|
+
async updateSessionMcpApprovalPolicy(
|
|
297
|
+
workspaceId: string,
|
|
298
|
+
sessionId: string,
|
|
299
|
+
serverId: string,
|
|
300
|
+
request: UpdateSessionMcpApprovalPolicyRequest,
|
|
301
|
+
): Promise<UpdateSessionMcpApprovalPolicyResponse> {
|
|
302
|
+
return await this.requestJson<UpdateSessionMcpApprovalPolicyResponse>(
|
|
303
|
+
"PATCH",
|
|
304
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/mcp-servers/${encodeURIComponent(serverId)}/approval-policy`,
|
|
305
|
+
request,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
255
309
|
async listSessions(
|
|
256
310
|
workspaceId: string,
|
|
257
311
|
options: {
|
|
@@ -353,7 +407,7 @@ export class OpenGeniClient {
|
|
|
353
407
|
*/
|
|
354
408
|
async listMachines(
|
|
355
409
|
workspaceId: string,
|
|
356
|
-
options: { sessionId?: string } = {},
|
|
410
|
+
options: { sessionId?: string; signal?: AbortSignal } = {},
|
|
357
411
|
): Promise<MachinesResponse> {
|
|
358
412
|
return await this.requestJson<MachinesResponse>(
|
|
359
413
|
"GET",
|
|
@@ -362,6 +416,7 @@ export class OpenGeniClient {
|
|
|
362
416
|
{
|
|
363
417
|
...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),
|
|
364
418
|
},
|
|
419
|
+
{ signal: options.signal },
|
|
365
420
|
);
|
|
366
421
|
}
|
|
367
422
|
|
|
@@ -493,27 +548,142 @@ export class OpenGeniClient {
|
|
|
493
548
|
// --- Events: replay, send, stream ----------------------------------------
|
|
494
549
|
|
|
495
550
|
/**
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
551
|
+
* Return the events from one bounded page. With no cursor, this uses the safe
|
|
552
|
+
* semantic monitoring tail; pass explicit forensic options and a cursor for
|
|
553
|
+
* retained audit replay. Use `listEventPage` when projection, coverage, or
|
|
554
|
+
* resume-cursor facts are required.
|
|
500
555
|
*/
|
|
501
556
|
async listEvents(
|
|
502
557
|
workspaceId: string,
|
|
503
558
|
sessionId: string,
|
|
504
|
-
options:
|
|
559
|
+
options: SessionEventListOptions = {},
|
|
505
560
|
): Promise<SessionEvent[]> {
|
|
506
|
-
return await this.
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
561
|
+
return (await this.listEventPage(workspaceId, sessionId, options)).events;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** Bounded durable/monitoring page plus exact projection and cursor facts. */
|
|
565
|
+
async listEventPage(
|
|
566
|
+
workspaceId: string,
|
|
567
|
+
sessionId: string,
|
|
568
|
+
options: SessionEventCompactResultOptions,
|
|
569
|
+
): Promise<SessionEventCompactResult | null>;
|
|
570
|
+
async listEventPage(
|
|
571
|
+
workspaceId: string,
|
|
572
|
+
sessionId: string,
|
|
573
|
+
options?: SessionEventListOptions,
|
|
574
|
+
): Promise<SessionEventPage>;
|
|
575
|
+
async listEventPage(
|
|
576
|
+
workspaceId: string,
|
|
577
|
+
sessionId: string,
|
|
578
|
+
options: SessionEventListOptions | SessionEventCompactResultOptions = {},
|
|
579
|
+
): Promise<SessionEventPage | SessionEventCompactResult | null> {
|
|
580
|
+
if (
|
|
581
|
+
options.latest &&
|
|
582
|
+
["includeTypes", "excludeTypes", "includeClasses", "excludeClasses"].some((name) =>
|
|
583
|
+
Object.prototype.hasOwnProperty.call(options, name),
|
|
584
|
+
)
|
|
585
|
+
) {
|
|
586
|
+
throw new TypeError("latest cannot be combined with event filters");
|
|
587
|
+
}
|
|
588
|
+
if (options.resultMode === "compact" && !options.latest) {
|
|
589
|
+
throw new TypeError("resultMode=compact requires latest");
|
|
590
|
+
}
|
|
591
|
+
const listOptions: SessionEventListOptions | null =
|
|
592
|
+
options.resultMode === "compact" ? null : options;
|
|
593
|
+
const response = await this.fetchImpl(
|
|
594
|
+
this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
|
|
595
|
+
...(listOptions?.after !== undefined ? { after: String(listOptions.after) } : {}),
|
|
596
|
+
...(listOptions?.before !== undefined ? { before: String(listOptions.before) } : {}),
|
|
597
|
+
...(listOptions?.limit !== undefined ? { limit: String(listOptions.limit) } : {}),
|
|
598
|
+
...(listOptions?.compact ? { compact: "1" } : {}),
|
|
599
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
600
|
+
...(listOptions?.direction ? { direction: listOptions.direction } : {}),
|
|
601
|
+
...(options.payloadMode ? { payloadMode: options.payloadMode } : {}),
|
|
602
|
+
...(options.resultMode ? { resultMode: options.resultMode } : {}),
|
|
603
|
+
...(listOptions?.includeTypes?.length
|
|
604
|
+
? { includeTypes: listOptions.includeTypes.join(",") }
|
|
605
|
+
: {}),
|
|
606
|
+
...(listOptions?.excludeTypes?.length
|
|
607
|
+
? { excludeTypes: listOptions.excludeTypes.join(",") }
|
|
608
|
+
: {}),
|
|
609
|
+
...(listOptions?.includeClasses?.length
|
|
610
|
+
? { includeClasses: listOptions.includeClasses.join(",") }
|
|
611
|
+
: {}),
|
|
612
|
+
...(listOptions?.excludeClasses?.length
|
|
613
|
+
? { excludeClasses: listOptions.excludeClasses.join(",") }
|
|
614
|
+
: {}),
|
|
615
|
+
...(options.latest ? { latest: options.latest } : {}),
|
|
616
|
+
}),
|
|
510
617
|
{
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
514
|
-
...(options.compact ? { compact: "1" } : {}),
|
|
618
|
+
method: "GET",
|
|
619
|
+
headers: { ...this.headers(), Accept: "application/json" },
|
|
515
620
|
},
|
|
516
621
|
);
|
|
622
|
+
assertApiContractResponse(response);
|
|
623
|
+
if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
|
|
624
|
+
const body = await response.json();
|
|
625
|
+
if (options.resultMode === "compact") {
|
|
626
|
+
return body as SessionEventCompactResult;
|
|
627
|
+
}
|
|
628
|
+
const events = body as SessionEvent[];
|
|
629
|
+
const integerHeader = (name: string): number | null => {
|
|
630
|
+
const raw = response.headers.get(name);
|
|
631
|
+
if (raw === null) return null;
|
|
632
|
+
const value = Number(raw);
|
|
633
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
634
|
+
};
|
|
635
|
+
const mode =
|
|
636
|
+
response.headers.get("X-OpenGeni-Event-Mode") === "forensic" ? "forensic" : "monitoring";
|
|
637
|
+
const direction =
|
|
638
|
+
response.headers.get("X-OpenGeni-Event-Direction") === "after" ? "after" : "before";
|
|
639
|
+
const payloadHeader = response.headers.get("X-OpenGeni-Payload-Mode");
|
|
640
|
+
const payloadMode =
|
|
641
|
+
payloadHeader === "none" || payloadHeader === "full" ? payloadHeader : "summary";
|
|
642
|
+
const first = integerHeader("X-OpenGeni-Covered-First");
|
|
643
|
+
const last = integerHeader("X-OpenGeni-Covered-Last");
|
|
644
|
+
const bytes =
|
|
645
|
+
integerHeader("X-OpenGeni-Page-Bytes") ??
|
|
646
|
+
new TextEncoder().encode(JSON.stringify(events)).byteLength;
|
|
647
|
+
const maxBytes = integerHeader("X-OpenGeni-Page-Max-Bytes") ?? 1024 * 1024;
|
|
648
|
+
const truncatedByHeader = response.headers.get("X-OpenGeni-Truncated-By");
|
|
649
|
+
const truncatedBy =
|
|
650
|
+
truncatedByHeader === "count" ||
|
|
651
|
+
truncatedByHeader === "bytes" ||
|
|
652
|
+
truncatedByHeader === "http_bytes"
|
|
653
|
+
? truncatedByHeader
|
|
654
|
+
: null;
|
|
655
|
+
return {
|
|
656
|
+
events,
|
|
657
|
+
mode,
|
|
658
|
+
payloadMode,
|
|
659
|
+
direction,
|
|
660
|
+
bytes,
|
|
661
|
+
maxBytes,
|
|
662
|
+
truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
|
|
663
|
+
hasMore: response.headers.get("X-OpenGeni-Has-More") === "true",
|
|
664
|
+
truncatedBy,
|
|
665
|
+
coveredSequence: first === null || last === null ? null : { first, last },
|
|
666
|
+
nextAfter: integerHeader("X-OpenGeni-Next-After"),
|
|
667
|
+
nextBefore: integerHeader("X-OpenGeni-Next-Before"),
|
|
668
|
+
forensicExact: response.headers.get("X-OpenGeni-Forensic-Exact") === "true",
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Fetch the authoritative newest-sequence semantic result directly. This is
|
|
674
|
+
* the callback-loss recovery path: it reads one compact durable result and
|
|
675
|
+
* never creates a model turn. `latest: "receipt"` aliases `tool_receipt`;
|
|
676
|
+
* turn generation remains scoped retry metadata.
|
|
677
|
+
*/
|
|
678
|
+
async getLatestEventResult(
|
|
679
|
+
workspaceId: string,
|
|
680
|
+
sessionId: string,
|
|
681
|
+
options: Omit<SessionEventCompactResultOptions, "resultMode"> = { latest: "terminal" },
|
|
682
|
+
): Promise<SessionEventCompactResult | null> {
|
|
683
|
+
return await this.listEventPage(workspaceId, sessionId, {
|
|
684
|
+
...options,
|
|
685
|
+
resultMode: "compact",
|
|
686
|
+
});
|
|
517
687
|
}
|
|
518
688
|
|
|
519
689
|
/** POST a user/control event to the session. Returns the accepted event. */
|
|
@@ -574,6 +744,47 @@ export class OpenGeniClient {
|
|
|
574
744
|
});
|
|
575
745
|
}
|
|
576
746
|
|
|
747
|
+
async listHumanInputRequests(
|
|
748
|
+
workspaceId: string,
|
|
749
|
+
sessionId: string,
|
|
750
|
+
options: {
|
|
751
|
+
status?: SessionHumanInputRequest["status"];
|
|
752
|
+
} = {},
|
|
753
|
+
): Promise<SessionHumanInputRequest[]> {
|
|
754
|
+
const result = await this.requestJson<{ requests: SessionHumanInputRequest[] }>(
|
|
755
|
+
"GET",
|
|
756
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests`,
|
|
757
|
+
undefined,
|
|
758
|
+
options.status ? { status: options.status } : undefined,
|
|
759
|
+
);
|
|
760
|
+
return result.requests;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
async getHumanInputRequest(
|
|
764
|
+
workspaceId: string,
|
|
765
|
+
sessionId: string,
|
|
766
|
+
requestId: string,
|
|
767
|
+
): Promise<SessionHumanInputRequest> {
|
|
768
|
+
return await this.requestJson<SessionHumanInputRequest>(
|
|
769
|
+
"GET",
|
|
770
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests/${requestId}`,
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async submitHumanInputResponse(
|
|
775
|
+
workspaceId: string,
|
|
776
|
+
sessionId: string,
|
|
777
|
+
requestId: string,
|
|
778
|
+
response: SubmitHumanInputResponseRequest,
|
|
779
|
+
options: { clientEventId?: string } = {},
|
|
780
|
+
): Promise<SessionEvent> {
|
|
781
|
+
return await this.sendEvent(workspaceId, sessionId, {
|
|
782
|
+
type: "user.humanInputResponse",
|
|
783
|
+
...(options.clientEventId ? { clientEventId: options.clientEventId } : {}),
|
|
784
|
+
payload: { requestId, response },
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
577
788
|
/**
|
|
578
789
|
* Live-stream a session's events with automatic reconnect, resume from the
|
|
579
790
|
* last seen sequence, gap backfill, and duplicate suppression. See
|
|
@@ -754,15 +965,45 @@ export class OpenGeniClient {
|
|
|
754
965
|
workspaceId: string,
|
|
755
966
|
options: { after?: number; limit?: number } = {},
|
|
756
967
|
): Promise<WorkspaceControlEvent[]> {
|
|
757
|
-
return await this.
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
968
|
+
return (await this.listWorkspaceControlEventPage(workspaceId, options)).events;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/** Count/byte-bounded page plus an explicit continuation cursor. */
|
|
972
|
+
async listWorkspaceControlEventPage(
|
|
973
|
+
workspaceId: string,
|
|
974
|
+
options: { after?: number; limit?: number } = {},
|
|
975
|
+
): Promise<WorkspaceControlEventPage> {
|
|
976
|
+
const response = await this.fetchImpl(
|
|
977
|
+
this.url(`/v1/workspaces/${workspaceId}/control-events`, {
|
|
762
978
|
...(options.after !== undefined ? { after: String(options.after) } : {}),
|
|
763
979
|
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
980
|
+
}),
|
|
981
|
+
{
|
|
982
|
+
method: "GET",
|
|
983
|
+
headers: { ...this.headers(), Accept: "application/json" },
|
|
764
984
|
},
|
|
765
985
|
);
|
|
986
|
+
assertApiContractResponse(response);
|
|
987
|
+
if (!response.ok) {
|
|
988
|
+
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
989
|
+
}
|
|
990
|
+
const events = (await response.json()) as WorkspaceControlEvent[];
|
|
991
|
+
const bytesHeader = response.headers.get("X-OpenGeni-Page-Bytes");
|
|
992
|
+
const nextHeader = response.headers.get("X-OpenGeni-Next-After");
|
|
993
|
+
const parsedBytes = bytesHeader === null ? Number.NaN : Number(bytesHeader);
|
|
994
|
+
const parsedNext = nextHeader === null ? null : Number(nextHeader);
|
|
995
|
+
return {
|
|
996
|
+
events,
|
|
997
|
+
bytes:
|
|
998
|
+
Number.isSafeInteger(parsedBytes) && parsedBytes >= 0
|
|
999
|
+
? parsedBytes
|
|
1000
|
+
: new TextEncoder().encode(JSON.stringify(events)).byteLength,
|
|
1001
|
+
truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
|
|
1002
|
+
nextAfter:
|
|
1003
|
+
parsedNext !== null && Number.isSafeInteger(parsedNext) && parsedNext >= 0
|
|
1004
|
+
? parsedNext
|
|
1005
|
+
: null,
|
|
1006
|
+
};
|
|
766
1007
|
}
|
|
767
1008
|
|
|
768
1009
|
streamWorkspaceControlEvents(
|
|
@@ -903,11 +1144,14 @@ export class OpenGeniClient {
|
|
|
903
1144
|
workspaceId: string,
|
|
904
1145
|
sessionId: string,
|
|
905
1146
|
request: FsListRequest = {},
|
|
1147
|
+
options: OpenGeniRequestOptions = {},
|
|
906
1148
|
): Promise<FsListResponse> {
|
|
907
1149
|
return await this.requestJson<FsListResponse>(
|
|
908
1150
|
"POST",
|
|
909
1151
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`,
|
|
910
1152
|
request,
|
|
1153
|
+
{},
|
|
1154
|
+
options,
|
|
911
1155
|
);
|
|
912
1156
|
}
|
|
913
1157
|
|
|
@@ -916,11 +1160,14 @@ export class OpenGeniClient {
|
|
|
916
1160
|
workspaceId: string,
|
|
917
1161
|
sessionId: string,
|
|
918
1162
|
request: FsReadRequest,
|
|
1163
|
+
options: OpenGeniRequestOptions = {},
|
|
919
1164
|
): Promise<FsReadResponse> {
|
|
920
1165
|
return await this.requestJson<FsReadResponse>(
|
|
921
1166
|
"POST",
|
|
922
1167
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`,
|
|
923
1168
|
request,
|
|
1169
|
+
{},
|
|
1170
|
+
options,
|
|
924
1171
|
);
|
|
925
1172
|
}
|
|
926
1173
|
|
|
@@ -981,11 +1228,14 @@ export class OpenGeniClient {
|
|
|
981
1228
|
workspaceId: string,
|
|
982
1229
|
sessionId: string,
|
|
983
1230
|
request: GitStatusRequest = {},
|
|
1231
|
+
options: OpenGeniRequestOptions = {},
|
|
984
1232
|
): Promise<GitStatusResponse> {
|
|
985
1233
|
return await this.requestJson<GitStatusResponse>(
|
|
986
1234
|
"POST",
|
|
987
1235
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`,
|
|
988
1236
|
request,
|
|
1237
|
+
{},
|
|
1238
|
+
options,
|
|
989
1239
|
);
|
|
990
1240
|
}
|
|
991
1241
|
|
|
@@ -994,11 +1244,14 @@ export class OpenGeniClient {
|
|
|
994
1244
|
workspaceId: string,
|
|
995
1245
|
sessionId: string,
|
|
996
1246
|
request: GitDiffRequest = {},
|
|
1247
|
+
options: OpenGeniRequestOptions = {},
|
|
997
1248
|
): Promise<GitDiffResponse> {
|
|
998
1249
|
return await this.requestJson<GitDiffResponse>(
|
|
999
1250
|
"POST",
|
|
1000
1251
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`,
|
|
1001
1252
|
request,
|
|
1253
|
+
{},
|
|
1254
|
+
options,
|
|
1002
1255
|
);
|
|
1003
1256
|
}
|
|
1004
1257
|
|
|
@@ -1035,10 +1288,14 @@ export class OpenGeniClient {
|
|
|
1035
1288
|
async getWorkspaceCapture(
|
|
1036
1289
|
workspaceId: string,
|
|
1037
1290
|
sessionId: string,
|
|
1291
|
+
options: OpenGeniRequestOptions = {},
|
|
1038
1292
|
): Promise<GetWorkspaceCaptureResponse> {
|
|
1039
1293
|
return await this.requestJson<GetWorkspaceCaptureResponse>(
|
|
1040
1294
|
"GET",
|
|
1041
1295
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture`,
|
|
1296
|
+
undefined,
|
|
1297
|
+
{},
|
|
1298
|
+
options,
|
|
1042
1299
|
);
|
|
1043
1300
|
}
|
|
1044
1301
|
|
|
@@ -1050,6 +1307,7 @@ export class OpenGeniClient {
|
|
|
1050
1307
|
sessionId: string,
|
|
1051
1308
|
path: string,
|
|
1052
1309
|
revision?: number,
|
|
1310
|
+
options: OpenGeniRequestOptions = {},
|
|
1053
1311
|
): Promise<GetWorkspaceCaptureFileResponse> {
|
|
1054
1312
|
const query: Record<string, string> = { path };
|
|
1055
1313
|
if (revision !== undefined) query.revision = String(revision);
|
|
@@ -1058,6 +1316,7 @@ export class OpenGeniClient {
|
|
|
1058
1316
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture/file`,
|
|
1059
1317
|
undefined,
|
|
1060
1318
|
query,
|
|
1319
|
+
options,
|
|
1061
1320
|
);
|
|
1062
1321
|
}
|
|
1063
1322
|
|
|
@@ -1142,10 +1401,14 @@ export class OpenGeniClient {
|
|
|
1142
1401
|
async getStreamCapabilities(
|
|
1143
1402
|
workspaceId: string,
|
|
1144
1403
|
sessionId: string,
|
|
1404
|
+
options: OpenGeniRequestOptions = {},
|
|
1145
1405
|
): Promise<SessionCapabilities> {
|
|
1146
1406
|
return await this.requestJson<SessionCapabilities>(
|
|
1147
1407
|
"GET",
|
|
1148
1408
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`,
|
|
1409
|
+
undefined,
|
|
1410
|
+
{},
|
|
1411
|
+
options,
|
|
1149
1412
|
);
|
|
1150
1413
|
}
|
|
1151
1414
|
|
|
@@ -1228,6 +1491,14 @@ export class OpenGeniClient {
|
|
|
1228
1491
|
return config;
|
|
1229
1492
|
}
|
|
1230
1493
|
|
|
1494
|
+
/** Authenticated model definitions plus workspace-specific selectability. */
|
|
1495
|
+
async getWorkspaceModelCatalog(workspaceId: string): Promise<WorkspaceModelCatalogResponse> {
|
|
1496
|
+
return await this.requestJson<WorkspaceModelCatalogResponse>(
|
|
1497
|
+
"GET",
|
|
1498
|
+
`/v1/workspaces/${workspaceId}/model-catalog`,
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1231
1502
|
/** The caller's access context: subject, account + workspace grants, defaults. */
|
|
1232
1503
|
async getAccessContext(): Promise<AccessContext> {
|
|
1233
1504
|
return await this.requestJson<AccessContext>("GET", "/v1/access/me");
|
|
@@ -1688,6 +1959,80 @@ export class OpenGeniClient {
|
|
|
1688
1959
|
);
|
|
1689
1960
|
}
|
|
1690
1961
|
|
|
1962
|
+
/** Read provider-neutral retained evidence metadata; never returns a storage location. */
|
|
1963
|
+
async getRetainedArtifact(
|
|
1964
|
+
workspaceId: string,
|
|
1965
|
+
artifactId: string,
|
|
1966
|
+
): Promise<RetainedArtifactMetadata> {
|
|
1967
|
+
return await this.requestJson<RetainedArtifactMetadata>(
|
|
1968
|
+
"GET",
|
|
1969
|
+
`/v1/workspaces/${workspaceId}/artifacts/${artifactId}`,
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Read at most one authenticated retained-evidence range from the API. This
|
|
1975
|
+
* deliberately does not use the ordinary signed file-download URL.
|
|
1976
|
+
*/
|
|
1977
|
+
async getRetainedArtifactContent(
|
|
1978
|
+
workspaceId: string,
|
|
1979
|
+
artifactId: string,
|
|
1980
|
+
options: RetainedArtifactContentOptions = {},
|
|
1981
|
+
): Promise<RetainedArtifactContent> {
|
|
1982
|
+
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
1983
|
+
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
1984
|
+
}
|
|
1985
|
+
const response = await this.fetchImpl(
|
|
1986
|
+
this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
|
|
1987
|
+
{
|
|
1988
|
+
method: "GET",
|
|
1989
|
+
headers: {
|
|
1990
|
+
...this.headers(),
|
|
1991
|
+
Accept: "application/octet-stream",
|
|
1992
|
+
...(options.range ? { Range: options.range } : {}),
|
|
1993
|
+
},
|
|
1994
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1995
|
+
},
|
|
1996
|
+
);
|
|
1997
|
+
try {
|
|
1998
|
+
assertApiContractResponse(response);
|
|
1999
|
+
} catch (error) {
|
|
2000
|
+
await cancelResponseBody(response, "retained artifact API contract mismatch");
|
|
2001
|
+
throw error;
|
|
2002
|
+
}
|
|
2003
|
+
if (!response.ok) {
|
|
2004
|
+
throw new OpenGeniApiError(response.status, await safeBoundedText(response));
|
|
2005
|
+
}
|
|
2006
|
+
if (response.status !== 200 && response.status !== 206) {
|
|
2007
|
+
await cancelResponseBody(response, "unexpected retained artifact response status");
|
|
2008
|
+
throw new OpenGeniApiError(response.status, "unexpected retained artifact response status");
|
|
2009
|
+
}
|
|
2010
|
+
if (response.headers.get("accept-ranges") !== "bytes") {
|
|
2011
|
+
await cancelResponseBody(response, "retained artifact response omitted byte-range support");
|
|
2012
|
+
throw new OpenGeniApiError(502, "retained artifact response omitted byte-range support");
|
|
2013
|
+
}
|
|
2014
|
+
let declaredLength: number | null;
|
|
2015
|
+
try {
|
|
2016
|
+
declaredLength = parseBoundedContentLength(response.headers.get("content-length"));
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
await cancelResponseBody(response, "invalid retained artifact content-length");
|
|
2019
|
+
throw error;
|
|
2020
|
+
}
|
|
2021
|
+
const bytes = await readBoundedResponseBytes(
|
|
2022
|
+
response,
|
|
2023
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
2024
|
+
declaredLength,
|
|
2025
|
+
);
|
|
2026
|
+
return {
|
|
2027
|
+
bytes,
|
|
2028
|
+
status: response.status,
|
|
2029
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
2030
|
+
contentLength: bytes.byteLength,
|
|
2031
|
+
contentRange: response.headers.get("content-range"),
|
|
2032
|
+
acceptRanges: "bytes",
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
1691
2036
|
/** Mint a short-lived signed download URL for a ready file. */
|
|
1692
2037
|
async createFileDownloadUrl(
|
|
1693
2038
|
workspaceId: string,
|
|
@@ -2048,15 +2393,14 @@ export class OpenGeniClient {
|
|
|
2048
2393
|
|
|
2049
2394
|
// --- GitHub ----------------------------------------------------------------------------------
|
|
2050
2395
|
|
|
2051
|
-
/** GitHub App configuration status
|
|
2396
|
+
/** GitHub App configuration status; install/link URLs are null while new binding is disabled. */
|
|
2052
2397
|
async getGitHubApp(workspaceId: string): Promise<GitHubAppInfo> {
|
|
2053
2398
|
return await this.requestJson<GitHubAppInfo>("GET", `/v1/workspaces/${workspaceId}/github/app`);
|
|
2054
2399
|
}
|
|
2055
2400
|
|
|
2056
2401
|
/**
|
|
2057
|
-
*
|
|
2058
|
-
*
|
|
2059
|
-
* `getGitHubApp().installUrl` or a github_connect_link tool.
|
|
2402
|
+
* Compatibility URL for previously issued state. New installation binding is
|
|
2403
|
+
* disabled, so the endpoint validates state and terminates with HTTP 410.
|
|
2060
2404
|
*/
|
|
2061
2405
|
githubConnectUrl(workspaceId: string, state: string): string {
|
|
2062
2406
|
return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });
|
|
@@ -2077,6 +2421,14 @@ export class OpenGeniClient {
|
|
|
2077
2421
|
);
|
|
2078
2422
|
}
|
|
2079
2423
|
|
|
2424
|
+
/** Remove one workspace binding without uninstalling the GitHub App itself. */
|
|
2425
|
+
async unlinkGitHubInstallation(workspaceId: string, installationId: number): Promise<void> {
|
|
2426
|
+
await this.requestVoid(
|
|
2427
|
+
"DELETE",
|
|
2428
|
+
`/v1/workspaces/${workspaceId}/github/installations/${installationId}`,
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2080
2432
|
/** Build a GitHub App manifest + the GitHub URL to submit it to. */
|
|
2081
2433
|
async createGitHubAppManifest(
|
|
2082
2434
|
workspaceId: string,
|
|
@@ -2219,6 +2571,14 @@ export class OpenGeniClient {
|
|
|
2219
2571
|
);
|
|
2220
2572
|
}
|
|
2221
2573
|
|
|
2574
|
+
/** Live independently-settled quota + reset-credit overview for every account. */
|
|
2575
|
+
async codexOverview(workspaceId: string): Promise<CodexOverviewResponse> {
|
|
2576
|
+
return await this.requestJson<CodexOverviewResponse>(
|
|
2577
|
+
"GET",
|
|
2578
|
+
`/v1/workspaces/${workspaceId}/codex/overview`,
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2222
2582
|
/** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */
|
|
2223
2583
|
async codexDisconnect(workspaceId: string): Promise<{ disconnected: boolean }> {
|
|
2224
2584
|
return await this.requestJson<{ disconnected: boolean }>(
|
|
@@ -2261,6 +2621,19 @@ export class OpenGeniClient {
|
|
|
2261
2621
|
);
|
|
2262
2622
|
}
|
|
2263
2623
|
|
|
2624
|
+
/** Toggle only NEW automatic allocations under independent allocator OCC. */
|
|
2625
|
+
async setCodexAccountAllocator(
|
|
2626
|
+
workspaceId: string,
|
|
2627
|
+
accountId: string,
|
|
2628
|
+
input: { enabled: boolean; expectedVersion: number },
|
|
2629
|
+
): Promise<CodexAllocatorUpdate> {
|
|
2630
|
+
return await this.requestJson<CodexAllocatorUpdate>(
|
|
2631
|
+
"PATCH",
|
|
2632
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/allocator`,
|
|
2633
|
+
input,
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2264
2637
|
/** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */
|
|
2265
2638
|
async disconnectCodexAccount(
|
|
2266
2639
|
workspaceId: string,
|
|
@@ -2303,6 +2676,7 @@ export class OpenGeniClient {
|
|
|
2303
2676
|
path: string,
|
|
2304
2677
|
body?: unknown,
|
|
2305
2678
|
query: Record<string, string> = {},
|
|
2679
|
+
options: OpenGeniRequestOptions = {},
|
|
2306
2680
|
): Promise<T> {
|
|
2307
2681
|
const response = await this.fetchImpl(this.url(path, query), {
|
|
2308
2682
|
method,
|
|
@@ -2312,6 +2686,7 @@ export class OpenGeniClient {
|
|
|
2312
2686
|
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
2313
2687
|
},
|
|
2314
2688
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
2689
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
2315
2690
|
});
|
|
2316
2691
|
assertApiContractResponse(response);
|
|
2317
2692
|
if (!response.ok) {
|
|
@@ -2352,3 +2727,69 @@ async function safeText(response: Response): Promise<string> {
|
|
|
2352
2727
|
return "";
|
|
2353
2728
|
}
|
|
2354
2729
|
}
|
|
2730
|
+
|
|
2731
|
+
async function safeBoundedText(response: Response): Promise<string> {
|
|
2732
|
+
try {
|
|
2733
|
+
return new TextDecoder().decode(await readBoundedResponseBytes(response, 64 * 1024, null));
|
|
2734
|
+
} catch {
|
|
2735
|
+
return "";
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
async function cancelResponseBody(response: Response, reason: string): Promise<void> {
|
|
2740
|
+
await response.body?.cancel(reason).catch(() => undefined);
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
function parseBoundedContentLength(value: string | null): number | null {
|
|
2744
|
+
if (value === null) return null;
|
|
2745
|
+
if (!/^\d+$/.test(value)) {
|
|
2746
|
+
throw new OpenGeniApiError(502, "invalid retained artifact content-length");
|
|
2747
|
+
}
|
|
2748
|
+
const length = Number(value);
|
|
2749
|
+
if (!Number.isSafeInteger(length) || length > RETAINED_OUTPUT_MAX_PAGE_BYTES) {
|
|
2750
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2751
|
+
}
|
|
2752
|
+
return length;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
async function readBoundedResponseBytes(
|
|
2756
|
+
response: Response,
|
|
2757
|
+
maxBytes: number,
|
|
2758
|
+
expectedBytes: number | null,
|
|
2759
|
+
): Promise<Uint8Array> {
|
|
2760
|
+
if (!response.body) {
|
|
2761
|
+
if (expectedBytes !== null && expectedBytes !== 0) {
|
|
2762
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2763
|
+
}
|
|
2764
|
+
return new Uint8Array();
|
|
2765
|
+
}
|
|
2766
|
+
const reader = response.body.getReader();
|
|
2767
|
+
const chunks: Uint8Array[] = [];
|
|
2768
|
+
let totalBytes = 0;
|
|
2769
|
+
try {
|
|
2770
|
+
while (true) {
|
|
2771
|
+
const { done, value } = await reader.read();
|
|
2772
|
+
if (done) break;
|
|
2773
|
+
totalBytes += value.byteLength;
|
|
2774
|
+
if (totalBytes > maxBytes) {
|
|
2775
|
+
await reader
|
|
2776
|
+
.cancel("retained artifact response exceeded the SDK byte limit")
|
|
2777
|
+
.catch(() => undefined);
|
|
2778
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2779
|
+
}
|
|
2780
|
+
chunks.push(value);
|
|
2781
|
+
}
|
|
2782
|
+
} finally {
|
|
2783
|
+
reader.releaseLock();
|
|
2784
|
+
}
|
|
2785
|
+
if (expectedBytes !== null && totalBytes !== expectedBytes) {
|
|
2786
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2787
|
+
}
|
|
2788
|
+
const bytes = new Uint8Array(totalBytes);
|
|
2789
|
+
let offset = 0;
|
|
2790
|
+
for (const chunk of chunks) {
|
|
2791
|
+
bytes.set(chunk, offset);
|
|
2792
|
+
offset += chunk.byteLength;
|
|
2793
|
+
}
|
|
2794
|
+
return bytes;
|
|
2795
|
+
}
|