@opengeni/sdk 0.11.0 → 0.15.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 +34 -12
- package/dist/index.d.ts +658 -83
- package/dist/index.js +1043 -304
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/client.ts +1367 -352
- package/src/errors.ts +20 -1
- package/src/index.ts +80 -6
- package/src/proxy.ts +1 -1
- package/src/sse.ts +11 -8
- package/src/stream.ts +9 -2
- package/src/types.ts +892 -102
- package/src/workspace-control-stream.ts +101 -0
package/src/client.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { OpenGeniApiError } from "./errors";
|
|
2
|
-
import {
|
|
1
|
+
import { OpenGeniApiContractMismatchError, OpenGeniApiError } from "./errors";
|
|
2
|
+
import {
|
|
3
|
+
streamSessionEvents,
|
|
4
|
+
type SessionEventStreamTransport,
|
|
5
|
+
type StreamSessionEventsOptions,
|
|
6
|
+
} from "./stream";
|
|
7
|
+
import {
|
|
8
|
+
streamWorkspaceControlEvents,
|
|
9
|
+
type WorkspaceControlStreamTransport,
|
|
10
|
+
} from "./workspace-control-stream";
|
|
3
11
|
import type {
|
|
4
12
|
AccessContext,
|
|
5
13
|
AddWorkspaceMemberRequest,
|
|
@@ -23,11 +31,13 @@ import type {
|
|
|
23
31
|
ClientSessionEventInput,
|
|
24
32
|
CompactSessionContextResult,
|
|
25
33
|
CompleteFileUploadResponse,
|
|
34
|
+
ConnectionMetadata,
|
|
26
35
|
CreateApiKeyRequest,
|
|
27
36
|
CreateApiKeyResponse,
|
|
28
37
|
CreateCapabilityCatalogItemRequest,
|
|
29
38
|
CreateCheckoutRequest,
|
|
30
39
|
CreateCheckoutResponse,
|
|
40
|
+
CreateConnectionRequest,
|
|
31
41
|
CreateDocumentBaseRequest,
|
|
32
42
|
CreateFileUploadRequest,
|
|
33
43
|
CreateFileUploadResponse,
|
|
@@ -36,7 +46,8 @@ import type {
|
|
|
36
46
|
CreateKnowledgeMemoryRequest,
|
|
37
47
|
CreateScheduledTaskRequest,
|
|
38
48
|
CreateSessionRequest,
|
|
39
|
-
|
|
49
|
+
CreateVariableSetRequest,
|
|
50
|
+
CreateRigRequest,
|
|
40
51
|
CreateWorkspaceRequest,
|
|
41
52
|
// Enrollment UX (design 11): the click-Grant approve-page lookup/deny + headless
|
|
42
53
|
// enroll-token mint.
|
|
@@ -62,7 +73,6 @@ import type {
|
|
|
62
73
|
ListPacksResponse,
|
|
63
74
|
// Bring-your-own-compute: the Machines dashboard + per-machine metrics (M10).
|
|
64
75
|
MachinesResponse,
|
|
65
|
-
MachineView,
|
|
66
76
|
MetricSample,
|
|
67
77
|
MachineMetricsSeriesResponse,
|
|
68
78
|
// Bring-your-own-compute: the user-authenticated active-sandbox swap (M7).
|
|
@@ -76,8 +86,23 @@ import type {
|
|
|
76
86
|
ScheduledTask,
|
|
77
87
|
ScheduledTaskRun,
|
|
78
88
|
Session,
|
|
89
|
+
SessionListResponse,
|
|
90
|
+
UpdateSessionPinRequest,
|
|
79
91
|
SessionEvent,
|
|
80
92
|
SessionGoal,
|
|
93
|
+
SessionLineageResponse,
|
|
94
|
+
SessionMcpCredentialUpdateInput,
|
|
95
|
+
SessionQueueSnapshot,
|
|
96
|
+
SessionQueueMutationResponse,
|
|
97
|
+
ComposerDraft,
|
|
98
|
+
DeleteSessionQueueItemRequest,
|
|
99
|
+
EditSessionQueueItemRequest,
|
|
100
|
+
MoveSessionQueueItemRequest,
|
|
101
|
+
SaveComposerDraftRequest,
|
|
102
|
+
SteerSessionQueueItemRequest,
|
|
103
|
+
SessionControlResponse,
|
|
104
|
+
WorkspaceInferenceControlResponse,
|
|
105
|
+
WorkspaceControlEvent,
|
|
81
106
|
SessionTurn,
|
|
82
107
|
// Stream surfacing (Phase 5): capability negotiation + viewer lifecycle + config.
|
|
83
108
|
SessionCapabilities,
|
|
@@ -108,6 +133,9 @@ import type {
|
|
|
108
133
|
GitLogResponse,
|
|
109
134
|
GitShowRequest,
|
|
110
135
|
GitShowResponse,
|
|
136
|
+
// Workbench v2 turn-end capture reads (M2, dossier §10.3).
|
|
137
|
+
GetWorkspaceCaptureResponse,
|
|
138
|
+
GetWorkspaceCaptureFileResponse,
|
|
111
139
|
TerminalExecRequest,
|
|
112
140
|
TerminalExecResponse,
|
|
113
141
|
PtyOpenRequest,
|
|
@@ -116,21 +144,35 @@ import type {
|
|
|
116
144
|
PtyResizeRequest,
|
|
117
145
|
PtyCloseRequest,
|
|
118
146
|
ToolRef,
|
|
147
|
+
UpdateConnectionRequest,
|
|
119
148
|
UpdateKnowledgeMemoryRequest,
|
|
120
149
|
UpdateScheduledTaskRequest,
|
|
121
150
|
UpdateSessionGoalRequest,
|
|
122
151
|
UpdateSessionRequest,
|
|
123
|
-
|
|
124
|
-
|
|
152
|
+
UpdateVariableSetRequest,
|
|
153
|
+
UpdateRigRequest,
|
|
125
154
|
UpdateWorkspaceMemberRequest,
|
|
126
155
|
UpdateWorkspaceRequest,
|
|
156
|
+
UpdateWorkspaceSettingsRequest,
|
|
157
|
+
SetWorkspaceDefaultRigRequest,
|
|
127
158
|
UploadFileInput,
|
|
128
|
-
|
|
129
|
-
|
|
159
|
+
VariableSet,
|
|
160
|
+
VariableSetVariableMetadata,
|
|
161
|
+
Rig,
|
|
162
|
+
RigVersion,
|
|
163
|
+
RigChange,
|
|
164
|
+
ProposeRigChangeRequest,
|
|
130
165
|
WorkspaceMember,
|
|
166
|
+
WorkspaceMemorySearchRequest,
|
|
167
|
+
WorkspaceMemorySearchResponse,
|
|
131
168
|
WorkspaceRegisteredPack,
|
|
132
169
|
Workspace,
|
|
170
|
+
ListConnectionsResponse,
|
|
171
|
+
ConnectionResponse,
|
|
172
|
+
OAuthStartRequest,
|
|
173
|
+
OAuthStartResponse,
|
|
133
174
|
} from "./types";
|
|
175
|
+
import { OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION } from "./types";
|
|
134
176
|
|
|
135
177
|
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
136
178
|
|
|
@@ -152,19 +194,16 @@ export type SendMessageInput = {
|
|
|
152
194
|
model?: string;
|
|
153
195
|
reasoningEffort?: ReasoningEffort;
|
|
154
196
|
clientEventId?: string;
|
|
197
|
+
controlEtag?: string;
|
|
198
|
+
expectedDraftRevision?: number;
|
|
199
|
+
mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[];
|
|
155
200
|
};
|
|
156
201
|
|
|
157
202
|
export type SteerMessageResult = {
|
|
158
203
|
/** The accepted `user.message` event. */
|
|
159
204
|
accepted: SessionEvent;
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
* still queued, but already claimed (running/requires_action or even
|
|
163
|
-
* finished) when the worker picked it up mid-call.
|
|
164
|
-
*/
|
|
165
|
-
turn: SessionTurn | null;
|
|
166
|
-
/** True when the running turn was interrupted to make way for the message. */
|
|
167
|
-
interrupted: boolean;
|
|
205
|
+
/** The exact turn created for this message in the same server transaction. */
|
|
206
|
+
turn: SessionTurn;
|
|
168
207
|
};
|
|
169
208
|
|
|
170
209
|
/**
|
|
@@ -180,34 +219,128 @@ export class OpenGeniClient {
|
|
|
180
219
|
constructor(options: OpenGeniClientOptions) {
|
|
181
220
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
182
221
|
this.options = options;
|
|
183
|
-
// Bind lazily so
|
|
222
|
+
// Bind lazily so variable sets that polyfill fetch after module load work.
|
|
184
223
|
this.fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
|
|
185
224
|
}
|
|
186
225
|
|
|
187
226
|
// --- Session lifecycle ---------------------------------------------------
|
|
188
227
|
|
|
189
228
|
async createSession(workspaceId: string, request: CreateSessionRequest): Promise<Session> {
|
|
190
|
-
return await this.requestJson<Session>(
|
|
229
|
+
return await this.requestJson<Session>(
|
|
230
|
+
"POST",
|
|
231
|
+
`/v1/workspaces/${workspaceId}/sessions`,
|
|
232
|
+
request,
|
|
233
|
+
);
|
|
191
234
|
}
|
|
192
235
|
|
|
193
236
|
async getSession(workspaceId: string, sessionId: string): Promise<Session> {
|
|
194
|
-
return await this.requestJson<Session>(
|
|
237
|
+
return await this.requestJson<Session>(
|
|
238
|
+
"GET",
|
|
239
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}`,
|
|
240
|
+
);
|
|
195
241
|
}
|
|
196
242
|
|
|
197
|
-
async updateSession(
|
|
198
|
-
|
|
243
|
+
async updateSession(
|
|
244
|
+
workspaceId: string,
|
|
245
|
+
sessionId: string,
|
|
246
|
+
request: UpdateSessionRequest,
|
|
247
|
+
): Promise<Session> {
|
|
248
|
+
return await this.requestJson<Session>(
|
|
249
|
+
"PATCH",
|
|
250
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}`,
|
|
251
|
+
request,
|
|
252
|
+
);
|
|
199
253
|
}
|
|
200
254
|
|
|
201
|
-
async listSessions(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
255
|
+
async listSessions(
|
|
256
|
+
workspaceId: string,
|
|
257
|
+
options: {
|
|
258
|
+
limit?: number;
|
|
259
|
+
parentSessionId?: string | null;
|
|
260
|
+
search?: string;
|
|
261
|
+
} = {},
|
|
262
|
+
): Promise<Session[]> {
|
|
263
|
+
return await this.requestJson<Session[]>(
|
|
264
|
+
"GET",
|
|
265
|
+
`/v1/workspaces/${workspaceId}/sessions`,
|
|
266
|
+
undefined,
|
|
267
|
+
{
|
|
268
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
269
|
+
...(options.search?.trim() ? { search: options.search.trim() } : {}),
|
|
270
|
+
...(Object.prototype.hasOwnProperty.call(options, "parentSessionId") &&
|
|
271
|
+
options.parentSessionId !== undefined
|
|
272
|
+
? {
|
|
273
|
+
parentSessionId:
|
|
274
|
+
options.parentSessionId === null ? "null" : String(options.parentSessionId),
|
|
275
|
+
}
|
|
276
|
+
: {}),
|
|
277
|
+
},
|
|
278
|
+
);
|
|
205
279
|
}
|
|
206
280
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
281
|
+
/** Pin-aware ordinary-session page with a stable keyset cursor. */
|
|
282
|
+
async listSessionPage(
|
|
283
|
+
workspaceId: string,
|
|
284
|
+
options: {
|
|
285
|
+
limit?: number;
|
|
286
|
+
parentSessionId?: string | null;
|
|
287
|
+
cursor?: string;
|
|
288
|
+
search?: string;
|
|
289
|
+
} = {},
|
|
290
|
+
): Promise<SessionListResponse> {
|
|
291
|
+
return await this.requestJson<SessionListResponse>(
|
|
292
|
+
"GET",
|
|
293
|
+
`/v1/workspaces/${workspaceId}/sessions`,
|
|
294
|
+
undefined,
|
|
295
|
+
{
|
|
296
|
+
view: "page",
|
|
297
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
298
|
+
...(options.cursor !== undefined ? { cursor: options.cursor } : {}),
|
|
299
|
+
...(options.search?.trim() ? { search: options.search.trim() } : {}),
|
|
300
|
+
...(Object.prototype.hasOwnProperty.call(options, "parentSessionId") &&
|
|
301
|
+
options.parentSessionId !== undefined
|
|
302
|
+
? {
|
|
303
|
+
parentSessionId:
|
|
304
|
+
options.parentSessionId === null ? "null" : String(options.parentSessionId),
|
|
305
|
+
}
|
|
306
|
+
: {}),
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Set this authenticated member's personal workspace pin for a session. */
|
|
312
|
+
async updateSessionPin(
|
|
313
|
+
workspaceId: string,
|
|
314
|
+
sessionId: string,
|
|
315
|
+
request: UpdateSessionPinRequest,
|
|
316
|
+
): Promise<Session> {
|
|
317
|
+
return await this.requestJson<Session>(
|
|
318
|
+
"PUT",
|
|
319
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/pin`,
|
|
320
|
+
request,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async getSessionLineage(workspaceId: string, sessionId: string): Promise<SessionLineageResponse> {
|
|
325
|
+
return await this.requestJson<SessionLineageResponse>(
|
|
326
|
+
"GET",
|
|
327
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/lineage`,
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async listTurns(
|
|
332
|
+
workspaceId: string,
|
|
333
|
+
sessionId: string,
|
|
334
|
+
options: { limit?: number } = {},
|
|
335
|
+
): Promise<SessionTurn[]> {
|
|
336
|
+
return await this.requestJson<SessionTurn[]>(
|
|
337
|
+
"GET",
|
|
338
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns`,
|
|
339
|
+
undefined,
|
|
340
|
+
{
|
|
341
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
342
|
+
},
|
|
343
|
+
);
|
|
211
344
|
}
|
|
212
345
|
|
|
213
346
|
// --- Bring-your-own-compute: Machines dashboard + metrics (M10) ------------
|
|
@@ -218,10 +351,18 @@ export class OpenGeniClient {
|
|
|
218
351
|
* sharedSessionCount. Pass `sessionId` for an in-session view, which adds the
|
|
219
352
|
* session's synthetic Modal group box + the active-sandbox pointer.
|
|
220
353
|
*/
|
|
221
|
-
async listMachines(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
354
|
+
async listMachines(
|
|
355
|
+
workspaceId: string,
|
|
356
|
+
options: { sessionId?: string } = {},
|
|
357
|
+
): Promise<MachinesResponse> {
|
|
358
|
+
return await this.requestJson<MachinesResponse>(
|
|
359
|
+
"GET",
|
|
360
|
+
`/v1/workspaces/${workspaceId}/machines`,
|
|
361
|
+
undefined,
|
|
362
|
+
{
|
|
363
|
+
...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),
|
|
364
|
+
},
|
|
365
|
+
);
|
|
225
366
|
}
|
|
226
367
|
|
|
227
368
|
/**
|
|
@@ -254,7 +395,11 @@ export class OpenGeniClient {
|
|
|
254
395
|
* the request.
|
|
255
396
|
*/
|
|
256
397
|
async lookupDeviceEnrollment(userCode: string): Promise<DeviceEnrollmentLookupResponse> {
|
|
257
|
-
return await this.requestJson<DeviceEnrollmentLookupResponse>(
|
|
398
|
+
return await this.requestJson<DeviceEnrollmentLookupResponse>(
|
|
399
|
+
"POST",
|
|
400
|
+
"/v1/enrollments/device/lookup",
|
|
401
|
+
{ userCode },
|
|
402
|
+
);
|
|
258
403
|
}
|
|
259
404
|
|
|
260
405
|
/**
|
|
@@ -324,14 +469,25 @@ export class OpenGeniClient {
|
|
|
324
469
|
|
|
325
470
|
// --- Scheduled tasks -------------------------------------------------------
|
|
326
471
|
|
|
327
|
-
async listScheduledTasks(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
472
|
+
async listScheduledTasks(
|
|
473
|
+
workspaceId: string,
|
|
474
|
+
options: { limit?: number } = {},
|
|
475
|
+
): Promise<ScheduledTask[]> {
|
|
476
|
+
return await this.requestJson<ScheduledTask[]>(
|
|
477
|
+
"GET",
|
|
478
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks`,
|
|
479
|
+
undefined,
|
|
480
|
+
{
|
|
481
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
482
|
+
},
|
|
483
|
+
);
|
|
331
484
|
}
|
|
332
485
|
|
|
333
486
|
async getScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {
|
|
334
|
-
return await this.requestJson<ScheduledTask>(
|
|
487
|
+
return await this.requestJson<ScheduledTask>(
|
|
488
|
+
"GET",
|
|
489
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`,
|
|
490
|
+
);
|
|
335
491
|
}
|
|
336
492
|
|
|
337
493
|
// --- Events: replay, send, stream ----------------------------------------
|
|
@@ -347,20 +503,37 @@ export class OpenGeniClient {
|
|
|
347
503
|
sessionId: string,
|
|
348
504
|
options: { after?: number; before?: number; limit?: number; compact?: boolean } = {},
|
|
349
505
|
): Promise<SessionEvent[]> {
|
|
350
|
-
return await this.requestJson<SessionEvent[]>(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
506
|
+
return await this.requestJson<SessionEvent[]>(
|
|
507
|
+
"GET",
|
|
508
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`,
|
|
509
|
+
undefined,
|
|
510
|
+
{
|
|
511
|
+
...(options.after !== undefined ? { after: String(options.after) } : {}),
|
|
512
|
+
...(options.before !== undefined ? { before: String(options.before) } : {}),
|
|
513
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
514
|
+
...(options.compact ? { compact: "1" } : {}),
|
|
515
|
+
},
|
|
516
|
+
);
|
|
356
517
|
}
|
|
357
518
|
|
|
358
519
|
/** POST a user/control event to the session. Returns the accepted event. */
|
|
359
|
-
async sendEvent(
|
|
360
|
-
|
|
520
|
+
async sendEvent(
|
|
521
|
+
workspaceId: string,
|
|
522
|
+
sessionId: string,
|
|
523
|
+
event: ClientSessionEventInput,
|
|
524
|
+
): Promise<SessionEvent> {
|
|
525
|
+
return await this.requestJson<SessionEvent>(
|
|
526
|
+
"POST",
|
|
527
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`,
|
|
528
|
+
event,
|
|
529
|
+
);
|
|
361
530
|
}
|
|
362
531
|
|
|
363
|
-
async sendMessage(
|
|
532
|
+
async sendMessage(
|
|
533
|
+
workspaceId: string,
|
|
534
|
+
sessionId: string,
|
|
535
|
+
message: string | SendMessageInput,
|
|
536
|
+
): Promise<SessionEvent> {
|
|
364
537
|
const input = typeof message === "string" ? { text: message } : message;
|
|
365
538
|
const { clientEventId, ...payload } = input;
|
|
366
539
|
return await this.sendEvent(workspaceId, sessionId, {
|
|
@@ -370,22 +543,28 @@ export class OpenGeniClient {
|
|
|
370
543
|
});
|
|
371
544
|
}
|
|
372
545
|
|
|
373
|
-
async
|
|
546
|
+
async pauseSession(
|
|
374
547
|
workspaceId: string,
|
|
375
548
|
sessionId: string,
|
|
376
|
-
options: { reason?: string; clientEventId?: string } = {},
|
|
377
|
-
): Promise<
|
|
378
|
-
return await this.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
549
|
+
options: { reason?: string; clientEventId?: string; expectedControlEtag?: string } = {},
|
|
550
|
+
): Promise<SessionControlResponse> {
|
|
551
|
+
return await this.controlSession(workspaceId, sessionId, {
|
|
552
|
+
action: "pause",
|
|
553
|
+
clientEventId: options.clientEventId ?? crypto.randomUUID(),
|
|
554
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
555
|
+
...(options.expectedControlEtag ? { expectedControlEtag: options.expectedControlEtag } : {}),
|
|
382
556
|
});
|
|
383
557
|
}
|
|
384
558
|
|
|
385
559
|
async sendApprovalDecision(
|
|
386
560
|
workspaceId: string,
|
|
387
561
|
sessionId: string,
|
|
388
|
-
decision: {
|
|
562
|
+
decision: {
|
|
563
|
+
approvalId: string;
|
|
564
|
+
decision: "approve" | "reject";
|
|
565
|
+
message?: string;
|
|
566
|
+
clientEventId?: string;
|
|
567
|
+
},
|
|
389
568
|
): Promise<SessionEvent> {
|
|
390
569
|
const { clientEventId, ...payload } = decision;
|
|
391
570
|
return await this.sendEvent(workspaceId, sessionId, {
|
|
@@ -411,8 +590,13 @@ export class OpenGeniClient {
|
|
|
411
590
|
/** The transport `streamEvents` runs on; useful for custom streaming layers. */
|
|
412
591
|
eventStreamTransport(workspaceId: string, sessionId: string): SessionEventStreamTransport {
|
|
413
592
|
return {
|
|
414
|
-
openStream: async (after, signal) =>
|
|
415
|
-
|
|
593
|
+
openStream: async (after, signal) =>
|
|
594
|
+
await this.openEventStream(workspaceId, sessionId, {
|
|
595
|
+
after,
|
|
596
|
+
...(signal ? { signal } : {}),
|
|
597
|
+
}),
|
|
598
|
+
listEvents: async (after, limit) =>
|
|
599
|
+
await this.listEvents(workspaceId, sessionId, { after, limit }),
|
|
416
600
|
};
|
|
417
601
|
}
|
|
418
602
|
|
|
@@ -430,6 +614,7 @@ export class OpenGeniClient {
|
|
|
430
614
|
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
431
615
|
...(options.signal ? { signal: options.signal } : {}),
|
|
432
616
|
});
|
|
617
|
+
assertApiContractResponse(response);
|
|
433
618
|
if (!response.ok) {
|
|
434
619
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
435
620
|
}
|
|
@@ -441,122 +626,233 @@ export class OpenGeniClient {
|
|
|
441
626
|
|
|
442
627
|
// --- Turn queue ------------------------------------------------------------
|
|
443
628
|
|
|
444
|
-
|
|
445
|
-
|
|
629
|
+
async getQueue(workspaceId: string, sessionId: string): Promise<SessionQueueSnapshot> {
|
|
630
|
+
return await this.requestJson<SessionQueueSnapshot>(
|
|
631
|
+
"GET",
|
|
632
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue`,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async moveQueueItem(
|
|
446
637
|
workspaceId: string,
|
|
447
638
|
sessionId: string,
|
|
448
639
|
turnId: string,
|
|
449
|
-
|
|
450
|
-
): Promise<
|
|
451
|
-
return await this.requestJson<
|
|
452
|
-
"
|
|
453
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/
|
|
454
|
-
|
|
640
|
+
request: MoveSessionQueueItemRequest,
|
|
641
|
+
): Promise<SessionQueueMutationResponse> {
|
|
642
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
643
|
+
"POST",
|
|
644
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/move`,
|
|
645
|
+
request,
|
|
455
646
|
);
|
|
456
647
|
}
|
|
457
648
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
649
|
+
async editQueueItem(
|
|
650
|
+
workspaceId: string,
|
|
651
|
+
sessionId: string,
|
|
652
|
+
turnId: string,
|
|
653
|
+
request: EditSessionQueueItemRequest,
|
|
654
|
+
): Promise<SessionQueueMutationResponse> {
|
|
655
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
464
656
|
"POST",
|
|
465
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/
|
|
466
|
-
|
|
657
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/edit`,
|
|
658
|
+
request,
|
|
467
659
|
);
|
|
468
660
|
}
|
|
469
661
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
662
|
+
async steerQueueItem(
|
|
663
|
+
workspaceId: string,
|
|
664
|
+
sessionId: string,
|
|
665
|
+
turnId: string,
|
|
666
|
+
request: SteerSessionQueueItemRequest,
|
|
667
|
+
): Promise<SessionQueueMutationResponse> {
|
|
668
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
669
|
+
"POST",
|
|
670
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/steer`,
|
|
671
|
+
request,
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
async deleteQueueItem(
|
|
676
|
+
workspaceId: string,
|
|
677
|
+
sessionId: string,
|
|
678
|
+
turnId: string,
|
|
679
|
+
request: DeleteSessionQueueItemRequest,
|
|
680
|
+
): Promise<SessionQueueMutationResponse> {
|
|
681
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
682
|
+
"POST",
|
|
683
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/delete`,
|
|
684
|
+
request,
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async getComposerDraft(workspaceId: string, sessionId: string): Promise<ComposerDraft> {
|
|
689
|
+
return await this.requestJson<ComposerDraft>(
|
|
690
|
+
"GET",
|
|
691
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/composer-draft`,
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async saveComposerDraft(
|
|
696
|
+
workspaceId: string,
|
|
697
|
+
sessionId: string,
|
|
698
|
+
request: SaveComposerDraftRequest,
|
|
699
|
+
): Promise<ComposerDraft> {
|
|
700
|
+
return await this.requestJson<ComposerDraft>(
|
|
701
|
+
"PUT",
|
|
702
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/composer-draft`,
|
|
703
|
+
request,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async controlSession(
|
|
708
|
+
workspaceId: string,
|
|
709
|
+
sessionId: string,
|
|
710
|
+
request: {
|
|
711
|
+
action: "pause" | "resume";
|
|
712
|
+
reason?: string;
|
|
713
|
+
clientEventId: string;
|
|
714
|
+
expectedControlEtag?: string;
|
|
715
|
+
},
|
|
716
|
+
): Promise<SessionControlResponse> {
|
|
717
|
+
return await this.requestJson<SessionControlResponse>(
|
|
718
|
+
"POST",
|
|
719
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/control`,
|
|
720
|
+
request,
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async resumeSession(
|
|
725
|
+
workspaceId: string,
|
|
726
|
+
sessionId: string,
|
|
727
|
+
options: { reason?: string; clientEventId?: string; expectedControlEtag?: string } = {},
|
|
728
|
+
): Promise<SessionControlResponse> {
|
|
729
|
+
return await this.controlSession(workspaceId, sessionId, {
|
|
730
|
+
action: "resume",
|
|
731
|
+
clientEventId: options.clientEventId ?? crypto.randomUUID(),
|
|
732
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
733
|
+
...(options.expectedControlEtag ? { expectedControlEtag: options.expectedControlEtag } : {}),
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
async setWorkspaceInferenceState(
|
|
738
|
+
workspaceId: string,
|
|
739
|
+
request: {
|
|
740
|
+
action: "pause" | "resume";
|
|
741
|
+
reason?: string;
|
|
742
|
+
clientEventId: string;
|
|
743
|
+
expectedRevision?: number;
|
|
744
|
+
},
|
|
745
|
+
): Promise<WorkspaceInferenceControlResponse> {
|
|
746
|
+
return await this.requestJson<WorkspaceInferenceControlResponse>(
|
|
747
|
+
"POST",
|
|
748
|
+
`/v1/workspaces/${workspaceId}/inference-control`,
|
|
749
|
+
request,
|
|
475
750
|
);
|
|
476
751
|
}
|
|
477
752
|
|
|
753
|
+
async listWorkspaceControlEvents(
|
|
754
|
+
workspaceId: string,
|
|
755
|
+
options: { after?: number; limit?: number } = {},
|
|
756
|
+
): Promise<WorkspaceControlEvent[]> {
|
|
757
|
+
return await this.requestJson<WorkspaceControlEvent[]>(
|
|
758
|
+
"GET",
|
|
759
|
+
`/v1/workspaces/${workspaceId}/control-events`,
|
|
760
|
+
undefined,
|
|
761
|
+
{
|
|
762
|
+
...(options.after !== undefined ? { after: String(options.after) } : {}),
|
|
763
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
764
|
+
},
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
streamWorkspaceControlEvents(
|
|
769
|
+
workspaceId: string,
|
|
770
|
+
options: StreamSessionEventsOptions = {},
|
|
771
|
+
): AsyncGenerator<WorkspaceControlEvent, void, void> {
|
|
772
|
+
return streamWorkspaceControlEvents(this.workspaceControlStreamTransport(workspaceId), options);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
workspaceControlStreamTransport(workspaceId: string): WorkspaceControlStreamTransport {
|
|
776
|
+
return {
|
|
777
|
+
openStream: async (after, signal) =>
|
|
778
|
+
await this.openWorkspaceControlEventStream(workspaceId, {
|
|
779
|
+
after,
|
|
780
|
+
...(signal ? { signal } : {}),
|
|
781
|
+
}),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async openWorkspaceControlEventStream(
|
|
786
|
+
workspaceId: string,
|
|
787
|
+
options: { after?: number; signal?: AbortSignal } = {},
|
|
788
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
789
|
+
const response = await this.fetchImpl(
|
|
790
|
+
this.url(`/v1/workspaces/${workspaceId}/control-events/stream`, {
|
|
791
|
+
after: String(options.after ?? 0),
|
|
792
|
+
}),
|
|
793
|
+
{
|
|
794
|
+
method: "GET",
|
|
795
|
+
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
796
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
assertApiContractResponse(response);
|
|
800
|
+
if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
|
|
801
|
+
if (!response.body) {
|
|
802
|
+
throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
|
|
803
|
+
}
|
|
804
|
+
return response.body;
|
|
805
|
+
}
|
|
806
|
+
|
|
478
807
|
/**
|
|
479
|
-
* Steer:
|
|
480
|
-
*
|
|
481
|
-
* running turn so the session picks the steer turn up next. On a session
|
|
482
|
-
* that is not running this degrades gracefully to a plain queued message.
|
|
483
|
-
*
|
|
484
|
-
* The steer turn is located by `triggerEventId` across ALL turns (retried
|
|
485
|
-
* briefly in case the server is still materializing it) — not just the
|
|
486
|
-
* queued ones, because the worker can claim the steer turn before it is
|
|
487
|
-
* ever observed queued, and a claimed steer turn means the message is
|
|
488
|
-
* already being delivered: interrupting then would cancel the very message
|
|
489
|
-
* being steered. If the turn cannot be found while other turns are queued,
|
|
490
|
-
* the interrupt is also skipped — stopping the running turn would otherwise
|
|
491
|
-
* promote someone else's queued work over this message — and the call
|
|
492
|
-
* degrades to a plain queued send (`interrupted: false`).
|
|
808
|
+
* Steer: atomically put this prompt at the head and supersede the current
|
|
809
|
+
* inference. The client performs one request and renders server order.
|
|
493
810
|
*/
|
|
494
811
|
async steerMessage(
|
|
495
812
|
workspaceId: string,
|
|
496
813
|
sessionId: string,
|
|
497
814
|
message: string | SendMessageInput,
|
|
498
815
|
): Promise<SteerMessageResult> {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
const turns = await this.listTurns(workspaceId, sessionId);
|
|
507
|
-
queued = turns
|
|
508
|
-
.filter((turn) => turn.status === "queued")
|
|
509
|
-
.sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));
|
|
510
|
-
// Match against every turn, whatever its status: a steer turn that is
|
|
511
|
-
// already running/requires_action (or even finished) was claimed before
|
|
512
|
-
// this listing — that is delivery, not grounds for an interrupt.
|
|
513
|
-
steerTurn = turns.find((turn) => turn.triggerEventId === accepted.id) ?? null;
|
|
514
|
-
if (steerTurn) {
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
const steerTurnQueued = steerTurn?.status === "queued";
|
|
519
|
-
if (steerTurn && steerTurnQueued && queued.length > 1) {
|
|
520
|
-
const front = steerTurn;
|
|
521
|
-
await this.reorderQueuedTurns(workspaceId, sessionId, [
|
|
522
|
-
front.id,
|
|
523
|
-
...queued.filter((turn) => turn.id !== front.id).map((turn) => turn.id),
|
|
524
|
-
]);
|
|
525
|
-
}
|
|
526
|
-
// Interrupting is only safe when the next claim is provably this message:
|
|
527
|
-
// either the steer turn sits queued (now at the front), or no turn
|
|
528
|
-
// materialized yet AND nothing else is queued. A steer turn observed in
|
|
529
|
-
// any non-queued state was already claimed — skip the interrupt.
|
|
530
|
-
const canDeliverNext = steerTurnQueued || (steerTurn === null && queued.length === 0);
|
|
531
|
-
const session = await this.getSession(workspaceId, sessionId);
|
|
532
|
-
// If the previously running turn already finished and the session claimed
|
|
533
|
-
// the steer turn itself, interrupting now would cancel the very message
|
|
534
|
-
// being steered. `activeTurnId` is the claim check; the residual window
|
|
535
|
-
// between this read and the interrupt landing is accepted (an interrupt
|
|
536
|
-
// can never be atomic with a status read over HTTP).
|
|
537
|
-
const steerTurnAlreadyActive = steerTurn !== null && session.activeTurnId === steerTurn.id;
|
|
538
|
-
const interrupted = canDeliverNext
|
|
539
|
-
&& !steerTurnAlreadyActive
|
|
540
|
-
&& (session.status === "running" || session.status === "requires_action");
|
|
541
|
-
if (interrupted) {
|
|
542
|
-
await this.interrupt(workspaceId, sessionId, { reason: "steer" });
|
|
543
|
-
}
|
|
544
|
-
return { accepted, turn: steerTurn, interrupted };
|
|
816
|
+
const input = typeof message === "string" ? { text: message } : message;
|
|
817
|
+
return await this.requestJson<SteerMessageResult>(
|
|
818
|
+
"POST",
|
|
819
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/steer`,
|
|
820
|
+
input,
|
|
821
|
+
);
|
|
545
822
|
}
|
|
546
823
|
|
|
547
824
|
// --- Goals -------------------------------------------------------------------
|
|
548
825
|
|
|
549
826
|
/** The session's goal. 404s when the session never had one. */
|
|
550
827
|
async getGoal(workspaceId: string, sessionId: string): Promise<SessionGoal> {
|
|
551
|
-
return await this.requestJson<SessionGoal>(
|
|
828
|
+
return await this.requestJson<SessionGoal>(
|
|
829
|
+
"GET",
|
|
830
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async updateGoal(
|
|
835
|
+
workspaceId: string,
|
|
836
|
+
sessionId: string,
|
|
837
|
+
request: UpdateSessionGoalRequest,
|
|
838
|
+
): Promise<SessionGoal> {
|
|
839
|
+
return await this.requestJson<SessionGoal>(
|
|
840
|
+
"PATCH",
|
|
841
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`,
|
|
842
|
+
request,
|
|
843
|
+
);
|
|
552
844
|
}
|
|
553
845
|
|
|
554
|
-
async
|
|
555
|
-
|
|
846
|
+
async deleteGoal(workspaceId: string, sessionId: string): Promise<void> {
|
|
847
|
+
await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`);
|
|
556
848
|
}
|
|
557
849
|
|
|
558
850
|
/** Pause the goal loop: the session stops self-continuing until resumed. */
|
|
559
|
-
async pauseGoal(
|
|
851
|
+
async pauseGoal(
|
|
852
|
+
workspaceId: string,
|
|
853
|
+
sessionId: string,
|
|
854
|
+
options: { rationale?: string } = {},
|
|
855
|
+
): Promise<SessionGoal> {
|
|
560
856
|
return await this.updateGoal(workspaceId, sessionId, {
|
|
561
857
|
status: "paused",
|
|
562
858
|
...(options.rationale !== undefined ? { rationale: options.rationale } : {}),
|
|
@@ -578,17 +874,23 @@ export class OpenGeniClient {
|
|
|
578
874
|
* context — the destructive intent is explicit on the wire.
|
|
579
875
|
*/
|
|
580
876
|
async clearSessionContext(workspaceId: string, sessionId: string): Promise<void> {
|
|
581
|
-
await this.requestVoid(
|
|
877
|
+
await this.requestVoid(
|
|
878
|
+
"POST",
|
|
879
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/clear`,
|
|
880
|
+
{ confirm: true },
|
|
881
|
+
);
|
|
582
882
|
}
|
|
583
883
|
|
|
584
|
-
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
884
|
+
/** Request one durable portable compaction at the next safe model boundary. */
|
|
885
|
+
async compactSessionContext(
|
|
886
|
+
workspaceId: string,
|
|
887
|
+
sessionId: string,
|
|
888
|
+
): Promise<CompactSessionContextResult> {
|
|
889
|
+
return await this.requestJson<CompactSessionContextResult>(
|
|
890
|
+
"POST",
|
|
891
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/compact`,
|
|
892
|
+
{},
|
|
893
|
+
);
|
|
592
894
|
}
|
|
593
895
|
|
|
594
896
|
// --- Channel-A structured services (P4.4) ------------------------------------
|
|
@@ -597,79 +899,232 @@ export class OpenGeniClient {
|
|
|
597
899
|
// notifications + the PTY output stream arrive on the existing event SSE.
|
|
598
900
|
|
|
599
901
|
/** FileSystem: list a directory tree (feeds the Pierre file tree). */
|
|
600
|
-
async fsList(
|
|
601
|
-
|
|
902
|
+
async fsList(
|
|
903
|
+
workspaceId: string,
|
|
904
|
+
sessionId: string,
|
|
905
|
+
request: FsListRequest = {},
|
|
906
|
+
): Promise<FsListResponse> {
|
|
907
|
+
return await this.requestJson<FsListResponse>(
|
|
908
|
+
"POST",
|
|
909
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`,
|
|
910
|
+
request,
|
|
911
|
+
);
|
|
602
912
|
}
|
|
603
913
|
|
|
604
914
|
/** FileSystem: read a file (text or base64; binary-safe, size-capped). */
|
|
605
|
-
async fsRead(
|
|
606
|
-
|
|
915
|
+
async fsRead(
|
|
916
|
+
workspaceId: string,
|
|
917
|
+
sessionId: string,
|
|
918
|
+
request: FsReadRequest,
|
|
919
|
+
): Promise<FsReadResponse> {
|
|
920
|
+
return await this.requestJson<FsReadResponse>(
|
|
921
|
+
"POST",
|
|
922
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`,
|
|
923
|
+
request,
|
|
924
|
+
);
|
|
607
925
|
}
|
|
608
926
|
|
|
609
927
|
/** FileSystem: write a file (last-writer-wins; emits fs.changed). */
|
|
610
|
-
async fsWrite(
|
|
611
|
-
|
|
928
|
+
async fsWrite(
|
|
929
|
+
workspaceId: string,
|
|
930
|
+
sessionId: string,
|
|
931
|
+
request: FsWriteRequest,
|
|
932
|
+
): Promise<FsWriteResponse> {
|
|
933
|
+
return await this.requestJson<FsWriteResponse>(
|
|
934
|
+
"POST",
|
|
935
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/write`,
|
|
936
|
+
request,
|
|
937
|
+
);
|
|
612
938
|
}
|
|
613
939
|
|
|
614
940
|
/** FileSystem: delete a path (emits fs.changed). */
|
|
615
|
-
async fsDelete(
|
|
616
|
-
|
|
941
|
+
async fsDelete(
|
|
942
|
+
workspaceId: string,
|
|
943
|
+
sessionId: string,
|
|
944
|
+
request: FsDeleteRequest,
|
|
945
|
+
): Promise<FsDeleteResponse> {
|
|
946
|
+
return await this.requestJson<FsDeleteResponse>(
|
|
947
|
+
"POST",
|
|
948
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/delete`,
|
|
949
|
+
request,
|
|
950
|
+
);
|
|
617
951
|
}
|
|
618
952
|
|
|
619
953
|
/** FileSystem: move/rename a path (emits fs.changed; 409 if destination exists and overwrite is false). */
|
|
620
|
-
async fsMove(
|
|
621
|
-
|
|
954
|
+
async fsMove(
|
|
955
|
+
workspaceId: string,
|
|
956
|
+
sessionId: string,
|
|
957
|
+
request: FsMoveRequest,
|
|
958
|
+
): Promise<FsMoveResponse> {
|
|
959
|
+
return await this.requestJson<FsMoveResponse>(
|
|
960
|
+
"POST",
|
|
961
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/move`,
|
|
962
|
+
request,
|
|
963
|
+
);
|
|
622
964
|
}
|
|
623
965
|
|
|
624
966
|
/** FileSystem: create a directory (emits fs.changed; recursive defaults to true). */
|
|
625
|
-
async fsMkdir(
|
|
626
|
-
|
|
967
|
+
async fsMkdir(
|
|
968
|
+
workspaceId: string,
|
|
969
|
+
sessionId: string,
|
|
970
|
+
request: FsMkdirRequest,
|
|
971
|
+
): Promise<FsMkdirResponse> {
|
|
972
|
+
return await this.requestJson<FsMkdirResponse>(
|
|
973
|
+
"POST",
|
|
974
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/mkdir`,
|
|
975
|
+
request,
|
|
976
|
+
);
|
|
627
977
|
}
|
|
628
978
|
|
|
629
979
|
/** Git: working-tree/index status (the Pierre file-status feed). */
|
|
630
|
-
async gitStatus(
|
|
631
|
-
|
|
980
|
+
async gitStatus(
|
|
981
|
+
workspaceId: string,
|
|
982
|
+
sessionId: string,
|
|
983
|
+
request: GitStatusRequest = {},
|
|
984
|
+
): Promise<GitStatusResponse> {
|
|
985
|
+
return await this.requestJson<GitStatusResponse>(
|
|
986
|
+
"POST",
|
|
987
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`,
|
|
988
|
+
request,
|
|
989
|
+
);
|
|
632
990
|
}
|
|
633
991
|
|
|
634
992
|
/** Git: structured diff hunks (the Pierre diff feed). */
|
|
635
|
-
async gitDiff(
|
|
636
|
-
|
|
993
|
+
async gitDiff(
|
|
994
|
+
workspaceId: string,
|
|
995
|
+
sessionId: string,
|
|
996
|
+
request: GitDiffRequest = {},
|
|
997
|
+
): Promise<GitDiffResponse> {
|
|
998
|
+
return await this.requestJson<GitDiffResponse>(
|
|
999
|
+
"POST",
|
|
1000
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`,
|
|
1001
|
+
request,
|
|
1002
|
+
);
|
|
637
1003
|
}
|
|
638
1004
|
|
|
639
1005
|
/** Git: commit log. */
|
|
640
|
-
async gitLog(
|
|
641
|
-
|
|
1006
|
+
async gitLog(
|
|
1007
|
+
workspaceId: string,
|
|
1008
|
+
sessionId: string,
|
|
1009
|
+
request: GitLogRequest = {},
|
|
1010
|
+
): Promise<GitLogResponse> {
|
|
1011
|
+
return await this.requestJson<GitLogResponse>(
|
|
1012
|
+
"POST",
|
|
1013
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/log`,
|
|
1014
|
+
request,
|
|
1015
|
+
);
|
|
642
1016
|
}
|
|
643
1017
|
|
|
644
1018
|
/** Git: show a commit (diff vs first parent) or fetch a raw blob at a ref. */
|
|
645
|
-
async gitShow(
|
|
646
|
-
|
|
1019
|
+
async gitShow(
|
|
1020
|
+
workspaceId: string,
|
|
1021
|
+
sessionId: string,
|
|
1022
|
+
request: GitShowRequest,
|
|
1023
|
+
): Promise<GitShowResponse> {
|
|
1024
|
+
return await this.requestJson<GitShowResponse>(
|
|
1025
|
+
"POST",
|
|
1026
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/show`,
|
|
1027
|
+
request,
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/** Workspace capture: the latest turn-end snapshot of the session's workspace
|
|
1032
|
+
* (tree + per-repo diff + file after-image refs), served from durable storage
|
|
1033
|
+
* WITHOUT warming a machine — the workbench cold-paint source. Returns
|
|
1034
|
+
* `{available:false}` when no capture exists yet (fall back to the live path). */
|
|
1035
|
+
async getWorkspaceCapture(
|
|
1036
|
+
workspaceId: string,
|
|
1037
|
+
sessionId: string,
|
|
1038
|
+
): Promise<GetWorkspaceCaptureResponse> {
|
|
1039
|
+
return await this.requestJson<GetWorkspaceCaptureResponse>(
|
|
1040
|
+
"GET",
|
|
1041
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture`,
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/** Workspace capture: a single file's after-image from the capture (revision
|
|
1046
|
+
* pins a specific one; omitted → latest). Content is inline for small files,
|
|
1047
|
+
* else a short-TTL signed URL; a tooLarge file returns metadata only. */
|
|
1048
|
+
async getWorkspaceCaptureFile(
|
|
1049
|
+
workspaceId: string,
|
|
1050
|
+
sessionId: string,
|
|
1051
|
+
path: string,
|
|
1052
|
+
revision?: number,
|
|
1053
|
+
): Promise<GetWorkspaceCaptureFileResponse> {
|
|
1054
|
+
const query: Record<string, string> = { path };
|
|
1055
|
+
if (revision !== undefined) query.revision = String(revision);
|
|
1056
|
+
return await this.requestJson<GetWorkspaceCaptureFileResponse>(
|
|
1057
|
+
"GET",
|
|
1058
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture/file`,
|
|
1059
|
+
undefined,
|
|
1060
|
+
query,
|
|
1061
|
+
);
|
|
647
1062
|
}
|
|
648
1063
|
|
|
649
1064
|
/** Terminal: run a bounded command, returning buffered stdout/stderr inline. */
|
|
650
|
-
async terminalExec(
|
|
651
|
-
|
|
1065
|
+
async terminalExec(
|
|
1066
|
+
workspaceId: string,
|
|
1067
|
+
sessionId: string,
|
|
1068
|
+
request: TerminalExecRequest,
|
|
1069
|
+
): Promise<TerminalExecResponse> {
|
|
1070
|
+
return await this.requestJson<TerminalExecResponse>(
|
|
1071
|
+
"POST",
|
|
1072
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/exec`,
|
|
1073
|
+
request,
|
|
1074
|
+
);
|
|
652
1075
|
}
|
|
653
1076
|
|
|
654
1077
|
/** Terminal: open an interactive PTY. Output streams on the event SSE as
|
|
655
1078
|
* terminal.pty.output.delta; drive it with terminalPtyWrite. */
|
|
656
|
-
async terminalPtyOpen(
|
|
657
|
-
|
|
1079
|
+
async terminalPtyOpen(
|
|
1080
|
+
workspaceId: string,
|
|
1081
|
+
sessionId: string,
|
|
1082
|
+
request: PtyOpenRequest = {},
|
|
1083
|
+
): Promise<PtyOpenResponse> {
|
|
1084
|
+
return await this.requestJson<PtyOpenResponse>(
|
|
1085
|
+
"POST",
|
|
1086
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty`,
|
|
1087
|
+
request,
|
|
1088
|
+
);
|
|
658
1089
|
}
|
|
659
1090
|
|
|
660
1091
|
/** Terminal: send stdin to an open PTY (output rides A1). */
|
|
661
|
-
async terminalPtyWrite(
|
|
662
|
-
|
|
1092
|
+
async terminalPtyWrite(
|
|
1093
|
+
workspaceId: string,
|
|
1094
|
+
sessionId: string,
|
|
1095
|
+
request: PtyWriteRequest,
|
|
1096
|
+
): Promise<void> {
|
|
1097
|
+
await this.requestVoid(
|
|
1098
|
+
"POST",
|
|
1099
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/write`,
|
|
1100
|
+
request,
|
|
1101
|
+
);
|
|
663
1102
|
}
|
|
664
1103
|
|
|
665
1104
|
/** Terminal: resize an open PTY. */
|
|
666
|
-
async terminalPtyResize(
|
|
667
|
-
|
|
1105
|
+
async terminalPtyResize(
|
|
1106
|
+
workspaceId: string,
|
|
1107
|
+
sessionId: string,
|
|
1108
|
+
request: PtyResizeRequest,
|
|
1109
|
+
): Promise<void> {
|
|
1110
|
+
await this.requestVoid(
|
|
1111
|
+
"POST",
|
|
1112
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/resize`,
|
|
1113
|
+
request,
|
|
1114
|
+
);
|
|
668
1115
|
}
|
|
669
1116
|
|
|
670
1117
|
/** Terminal: close an open PTY (idempotent). */
|
|
671
|
-
async terminalPtyClose(
|
|
672
|
-
|
|
1118
|
+
async terminalPtyClose(
|
|
1119
|
+
workspaceId: string,
|
|
1120
|
+
sessionId: string,
|
|
1121
|
+
request: PtyCloseRequest,
|
|
1122
|
+
): Promise<void> {
|
|
1123
|
+
await this.requestVoid(
|
|
1124
|
+
"POST",
|
|
1125
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/close`,
|
|
1126
|
+
request,
|
|
1127
|
+
);
|
|
673
1128
|
}
|
|
674
1129
|
|
|
675
1130
|
// --- Stream surfacing: capability negotiation + viewer lifecycle (Phase 5) ---
|
|
@@ -684,19 +1139,29 @@ export class OpenGeniClient {
|
|
|
684
1139
|
* liveness the client polls on while `cold`/`warming`. The desktop URL/token
|
|
685
1140
|
* are minted in-process only when the box is warm AND the principal has
|
|
686
1141
|
* acknowledged the un-redacted plane. */
|
|
687
|
-
async getStreamCapabilities(
|
|
1142
|
+
async getStreamCapabilities(
|
|
1143
|
+
workspaceId: string,
|
|
1144
|
+
sessionId: string,
|
|
1145
|
+
): Promise<SessionCapabilities> {
|
|
688
1146
|
return await this.requestJson<SessionCapabilities>(
|
|
689
|
-
"GET",
|
|
1147
|
+
"GET",
|
|
1148
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`,
|
|
1149
|
+
);
|
|
690
1150
|
}
|
|
691
1151
|
|
|
692
1152
|
/** Record the calling principal's acknowledgment of the un-redacted desktop
|
|
693
1153
|
* pixel plane (and, when the box is shared, the shared-exposure disclosure).
|
|
694
1154
|
* The desktop viewer-attach path returns 409 until this is recorded. */
|
|
695
1155
|
async acknowledgeStream(
|
|
696
|
-
workspaceId: string,
|
|
1156
|
+
workspaceId: string,
|
|
1157
|
+
sessionId: string,
|
|
1158
|
+
request: AcknowledgeStreamRequest = {},
|
|
697
1159
|
): Promise<AcknowledgeStreamResponse> {
|
|
698
1160
|
return await this.requestJson<AcknowledgeStreamResponse>(
|
|
699
|
-
"POST",
|
|
1161
|
+
"POST",
|
|
1162
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities/acknowledge`,
|
|
1163
|
+
request,
|
|
1164
|
+
);
|
|
700
1165
|
}
|
|
701
1166
|
|
|
702
1167
|
/** Attach a viewer holder (refcounted liveness — keeps the box warm while
|
|
@@ -708,25 +1173,39 @@ export class OpenGeniClient {
|
|
|
708
1173
|
* (`desktop` omitted/false) warms the box + mints the pty-ws terminal cell with
|
|
709
1174
|
* NO consent gate. An omitted `viewerId` mints a fresh one. */
|
|
710
1175
|
async attachViewer(
|
|
711
|
-
workspaceId: string,
|
|
1176
|
+
workspaceId: string,
|
|
1177
|
+
sessionId: string,
|
|
1178
|
+
request: AttachViewerRequest = {},
|
|
712
1179
|
): Promise<AttachViewerResponse> {
|
|
713
1180
|
return await this.requestJson<AttachViewerResponse>(
|
|
714
|
-
"POST",
|
|
1181
|
+
"POST",
|
|
1182
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers`,
|
|
1183
|
+
request,
|
|
1184
|
+
);
|
|
715
1185
|
}
|
|
716
1186
|
|
|
717
1187
|
/** Heartbeat a viewer holder (Channel-A app-level liveness). A closed laptop
|
|
718
1188
|
* stops sending these → the reaper drops the holder within ~90s. Echoes
|
|
719
1189
|
* `leaseEpoch` so a superseded epoch is rejected (`alive:false` → re-attach). */
|
|
720
1190
|
async heartbeatViewer(
|
|
721
|
-
workspaceId: string,
|
|
1191
|
+
workspaceId: string,
|
|
1192
|
+
sessionId: string,
|
|
1193
|
+
viewerId: string,
|
|
1194
|
+
request: ViewerHeartbeatRequest,
|
|
722
1195
|
): Promise<ViewerHeartbeatResponse> {
|
|
723
1196
|
return await this.requestJson<ViewerHeartbeatResponse>(
|
|
724
|
-
"POST",
|
|
1197
|
+
"POST",
|
|
1198
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}/heartbeat`,
|
|
1199
|
+
request,
|
|
1200
|
+
);
|
|
725
1201
|
}
|
|
726
1202
|
|
|
727
1203
|
/** Detach a viewer (delete this holder; idempotent delete-my-row). */
|
|
728
1204
|
async detachViewer(workspaceId: string, sessionId: string, viewerId: string): Promise<void> {
|
|
729
|
-
await this.requestVoid(
|
|
1205
|
+
await this.requestVoid(
|
|
1206
|
+
"DELETE",
|
|
1207
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}`,
|
|
1208
|
+
);
|
|
730
1209
|
}
|
|
731
1210
|
|
|
732
1211
|
// --- Access + workspaces -----------------------------------------------------
|
|
@@ -739,7 +1218,14 @@ export class OpenGeniClient {
|
|
|
739
1218
|
* knowledge of the host setup; safe to call before any auth is established.
|
|
740
1219
|
*/
|
|
741
1220
|
async getClientConfig(): Promise<ClientConfig> {
|
|
742
|
-
|
|
1221
|
+
const config = await this.requestJson<ClientConfig>("GET", "/v1/config/client");
|
|
1222
|
+
if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
|
|
1223
|
+
throw new OpenGeniApiContractMismatchError(
|
|
1224
|
+
OPENGENI_API_CONTRACT_REVISION,
|
|
1225
|
+
String(config.apiContractRevision || "(missing)"),
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
return config;
|
|
743
1229
|
}
|
|
744
1230
|
|
|
745
1231
|
/** The caller's access context: subject, account + workspace grants, defaults. */
|
|
@@ -755,159 +1241,396 @@ export class OpenGeniClient {
|
|
|
755
1241
|
return await this.requestJson<Workspace>("POST", "/v1/workspaces", request);
|
|
756
1242
|
}
|
|
757
1243
|
|
|
758
|
-
async getWorkspace(workspaceId: string): Promise<Workspace> {
|
|
759
|
-
return await this.requestJson<Workspace>("GET", `/v1/workspaces/${workspaceId}`);
|
|
1244
|
+
async getWorkspace(workspaceId: string): Promise<Workspace> {
|
|
1245
|
+
return await this.requestJson<Workspace>("GET", `/v1/workspaces/${workspaceId}`);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
async updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace> {
|
|
1249
|
+
return await this.requestJson<Workspace>("PATCH", `/v1/workspaces/${workspaceId}`, request);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Delete a workspace and everything in it. Refused (409) for the account's
|
|
1254
|
+
* only workspace and while it still has a running session. Irreversible.
|
|
1255
|
+
*/
|
|
1256
|
+
async deleteWorkspace(workspaceId: string): Promise<void> {
|
|
1257
|
+
await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}`);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// --- Members ("People with access") -------------------------------------------
|
|
1261
|
+
|
|
1262
|
+
/** The workspace's members (user + api_key subjects). */
|
|
1263
|
+
async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMember[]> {
|
|
1264
|
+
const response = await this.requestJson<ListWorkspaceMembersResponse>(
|
|
1265
|
+
"GET",
|
|
1266
|
+
`/v1/workspaces/${workspaceId}/members`,
|
|
1267
|
+
);
|
|
1268
|
+
return response.members;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* Add an already-registered user by email. 404s when no user with that email
|
|
1273
|
+
* exists (email invites for unknown users are deferred).
|
|
1274
|
+
*/
|
|
1275
|
+
async addWorkspaceMember(
|
|
1276
|
+
workspaceId: string,
|
|
1277
|
+
request: AddWorkspaceMemberRequest,
|
|
1278
|
+
): Promise<WorkspaceMember> {
|
|
1279
|
+
return await this.requestJson<WorkspaceMember>(
|
|
1280
|
+
"POST",
|
|
1281
|
+
`/v1/workspaces/${workspaceId}/members`,
|
|
1282
|
+
request,
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
async updateWorkspaceMember(
|
|
1287
|
+
workspaceId: string,
|
|
1288
|
+
subjectId: string,
|
|
1289
|
+
request: UpdateWorkspaceMemberRequest,
|
|
1290
|
+
): Promise<WorkspaceMember> {
|
|
1291
|
+
return await this.requestJson<WorkspaceMember>(
|
|
1292
|
+
"PATCH",
|
|
1293
|
+
`/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`,
|
|
1294
|
+
request,
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Remove a member. Refused (409) for your own membership and for the last
|
|
1300
|
+
* member who can still manage the workspace.
|
|
1301
|
+
*/
|
|
1302
|
+
async removeWorkspaceMember(workspaceId: string, subjectId: string): Promise<void> {
|
|
1303
|
+
await this.requestVoid(
|
|
1304
|
+
"DELETE",
|
|
1305
|
+
`/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`,
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// --- Scheduled tasks (write + runs) -------------------------------------------
|
|
1310
|
+
|
|
1311
|
+
async createScheduledTask(
|
|
1312
|
+
workspaceId: string,
|
|
1313
|
+
request: CreateScheduledTaskRequest,
|
|
1314
|
+
): Promise<ScheduledTask> {
|
|
1315
|
+
return await this.requestJson<ScheduledTask>(
|
|
1316
|
+
"POST",
|
|
1317
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks`,
|
|
1318
|
+
request,
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
async updateScheduledTask(
|
|
1323
|
+
workspaceId: string,
|
|
1324
|
+
taskId: string,
|
|
1325
|
+
request: UpdateScheduledTaskRequest,
|
|
1326
|
+
): Promise<ScheduledTask> {
|
|
1327
|
+
return await this.requestJson<ScheduledTask>(
|
|
1328
|
+
"PATCH",
|
|
1329
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`,
|
|
1330
|
+
request,
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
async pauseScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {
|
|
1335
|
+
return await this.requestJson<ScheduledTask>(
|
|
1336
|
+
"POST",
|
|
1337
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/pause`,
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
async resumeScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {
|
|
1342
|
+
return await this.requestJson<ScheduledTask>(
|
|
1343
|
+
"POST",
|
|
1344
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/resume`,
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Fire the task immediately (manual trigger), independent of its schedule.
|
|
1350
|
+
* Pass a stable `triggerId` to make a retried trigger idempotent — the same
|
|
1351
|
+
* token charges once and starts one run. Omit it and each call is distinct.
|
|
1352
|
+
*/
|
|
1353
|
+
async triggerScheduledTask(
|
|
1354
|
+
workspaceId: string,
|
|
1355
|
+
taskId: string,
|
|
1356
|
+
options: { triggerId?: string } = {},
|
|
1357
|
+
): Promise<ScheduledTask> {
|
|
1358
|
+
return await this.requestJson<ScheduledTask>(
|
|
1359
|
+
"POST",
|
|
1360
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/trigger`,
|
|
1361
|
+
options.triggerId ? { triggerId: options.triggerId } : undefined,
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
async deleteScheduledTask(workspaceId: string, taskId: string): Promise<void> {
|
|
1366
|
+
await this.requestJson<unknown>(
|
|
1367
|
+
"DELETE",
|
|
1368
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`,
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
async listScheduledTaskRuns(
|
|
1373
|
+
workspaceId: string,
|
|
1374
|
+
taskId: string,
|
|
1375
|
+
options: { limit?: number } = {},
|
|
1376
|
+
): Promise<ScheduledTaskRun[]> {
|
|
1377
|
+
return await this.requestJson<ScheduledTaskRun[]>(
|
|
1378
|
+
"GET",
|
|
1379
|
+
`/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/runs`,
|
|
1380
|
+
undefined,
|
|
1381
|
+
{ ...(options.limit !== undefined ? { limit: String(options.limit) } : {}) },
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// --- VariableSets --------------------------------------------------------------
|
|
1386
|
+
// Variable values are write-only: reads return name/version metadata only.
|
|
1387
|
+
|
|
1388
|
+
async listVariableSets(workspaceId: string): Promise<VariableSet[]> {
|
|
1389
|
+
return await this.requestJson<VariableSet[]>(
|
|
1390
|
+
"GET",
|
|
1391
|
+
`/v1/workspaces/${workspaceId}/variable-sets`,
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
async createVariableSet(
|
|
1396
|
+
workspaceId: string,
|
|
1397
|
+
request: CreateVariableSetRequest,
|
|
1398
|
+
): Promise<VariableSet> {
|
|
1399
|
+
return await this.requestJson<VariableSet>(
|
|
1400
|
+
"POST",
|
|
1401
|
+
`/v1/workspaces/${workspaceId}/variable-sets`,
|
|
1402
|
+
request,
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
async getVariableSet(workspaceId: string, variableSetId: string): Promise<VariableSet> {
|
|
1407
|
+
return await this.requestJson<VariableSet>(
|
|
1408
|
+
"GET",
|
|
1409
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`,
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
async updateVariableSet(
|
|
1414
|
+
workspaceId: string,
|
|
1415
|
+
variableSetId: string,
|
|
1416
|
+
request: UpdateVariableSetRequest,
|
|
1417
|
+
): Promise<VariableSet> {
|
|
1418
|
+
return await this.requestJson<VariableSet>(
|
|
1419
|
+
"PATCH",
|
|
1420
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`,
|
|
1421
|
+
request,
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
async deleteVariableSet(workspaceId: string, variableSetId: string): Promise<void> {
|
|
1426
|
+
await this.requestJson<unknown>(
|
|
1427
|
+
"DELETE",
|
|
1428
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}`,
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/** Create or rotate a variable. The value never comes back on any read. */
|
|
1433
|
+
async setVariableSetVariable(
|
|
1434
|
+
workspaceId: string,
|
|
1435
|
+
variableSetId: string,
|
|
1436
|
+
name: string,
|
|
1437
|
+
value: string,
|
|
1438
|
+
): Promise<VariableSetVariableMetadata> {
|
|
1439
|
+
return await this.requestJson<VariableSetVariableMetadata>(
|
|
1440
|
+
"PUT",
|
|
1441
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}/variables/${encodeURIComponent(name)}`,
|
|
1442
|
+
{ value },
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
async deleteVariableSetVariable(
|
|
1447
|
+
workspaceId: string,
|
|
1448
|
+
variableSetId: string,
|
|
1449
|
+
name: string,
|
|
1450
|
+
): Promise<void> {
|
|
1451
|
+
await this.requestJson<unknown>(
|
|
1452
|
+
"DELETE",
|
|
1453
|
+
`/v1/workspaces/${workspaceId}/variable-sets/${variableSetId}/variables/${encodeURIComponent(name)}`,
|
|
1454
|
+
);
|
|
760
1455
|
}
|
|
761
1456
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1457
|
+
// --- Rigs ------------------------------------------------------------------
|
|
1458
|
+
// Workspace-scoped, versioned sandbox machine definitions. rigs:use gates read
|
|
1459
|
+
// + proposeRigChange; rigs:manage gates create / update / delete / activate.
|
|
765
1460
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
* only workspace and while it still has a running session. Irreversible.
|
|
769
|
-
*/
|
|
770
|
-
async deleteWorkspace(workspaceId: string): Promise<void> {
|
|
771
|
-
await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}`);
|
|
1461
|
+
async listRigs(workspaceId: string): Promise<Rig[]> {
|
|
1462
|
+
return await this.requestJson<Rig[]>("GET", `/v1/workspaces/${workspaceId}/rigs`);
|
|
772
1463
|
}
|
|
773
1464
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
/** The workspace's members (user + api_key subjects). */
|
|
777
|
-
async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMember[]> {
|
|
778
|
-
const response = await this.requestJson<ListWorkspaceMembersResponse>("GET", `/v1/workspaces/${workspaceId}/members`);
|
|
779
|
-
return response.members;
|
|
1465
|
+
async createRig(workspaceId: string, request: CreateRigRequest): Promise<Rig> {
|
|
1466
|
+
return await this.requestJson<Rig>("POST", `/v1/workspaces/${workspaceId}/rigs`, request);
|
|
780
1467
|
}
|
|
781
1468
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
* exists (email invites for unknown users are deferred).
|
|
785
|
-
*/
|
|
786
|
-
async addWorkspaceMember(workspaceId: string, request: AddWorkspaceMemberRequest): Promise<WorkspaceMember> {
|
|
787
|
-
return await this.requestJson<WorkspaceMember>("POST", `/v1/workspaces/${workspaceId}/members`, request);
|
|
1469
|
+
async getRig(workspaceId: string, rigId: string): Promise<Rig> {
|
|
1470
|
+
return await this.requestJson<Rig>("GET", `/v1/workspaces/${workspaceId}/rigs/${rigId}`);
|
|
788
1471
|
}
|
|
789
1472
|
|
|
790
|
-
async
|
|
791
|
-
return await this.requestJson<
|
|
1473
|
+
async updateRig(workspaceId: string, rigId: string, request: UpdateRigRequest): Promise<Rig> {
|
|
1474
|
+
return await this.requestJson<Rig>(
|
|
792
1475
|
"PATCH",
|
|
793
|
-
`/v1/workspaces/${workspaceId}/
|
|
1476
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}`,
|
|
794
1477
|
request,
|
|
795
1478
|
);
|
|
796
1479
|
}
|
|
797
1480
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
* member who can still manage the workspace.
|
|
801
|
-
*/
|
|
802
|
-
async removeWorkspaceMember(workspaceId: string, subjectId: string): Promise<void> {
|
|
803
|
-
await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`);
|
|
1481
|
+
async deleteRig(workspaceId: string, rigId: string): Promise<void> {
|
|
1482
|
+
await this.requestJson<unknown>("DELETE", `/v1/workspaces/${workspaceId}/rigs/${rigId}`);
|
|
804
1483
|
}
|
|
805
1484
|
|
|
806
|
-
|
|
1485
|
+
async listRigVersions(workspaceId: string, rigId: string): Promise<RigVersion[]> {
|
|
1486
|
+
return await this.requestJson<RigVersion[]>(
|
|
1487
|
+
"GET",
|
|
1488
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/versions`,
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
807
1491
|
|
|
808
|
-
|
|
809
|
-
|
|
1492
|
+
/** Roll the active version to an existing one (rollback / promote-activate). */
|
|
1493
|
+
async activateRigVersion(
|
|
1494
|
+
workspaceId: string,
|
|
1495
|
+
rigId: string,
|
|
1496
|
+
versionId: string,
|
|
1497
|
+
): Promise<RigVersion> {
|
|
1498
|
+
return await this.requestJson<RigVersion>(
|
|
1499
|
+
"POST",
|
|
1500
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/versions/${versionId}/activate`,
|
|
1501
|
+
);
|
|
810
1502
|
}
|
|
811
1503
|
|
|
812
|
-
async
|
|
813
|
-
return await this.requestJson<
|
|
1504
|
+
async listRigChanges(workspaceId: string, rigId: string): Promise<RigChange[]> {
|
|
1505
|
+
return await this.requestJson<RigChange[]>(
|
|
1506
|
+
"GET",
|
|
1507
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/changes`,
|
|
1508
|
+
);
|
|
814
1509
|
}
|
|
815
1510
|
|
|
816
|
-
|
|
817
|
-
|
|
1511
|
+
/** Propose a change against the rig's active version (rigs:use). */
|
|
1512
|
+
async proposeRigChange(
|
|
1513
|
+
workspaceId: string,
|
|
1514
|
+
rigId: string,
|
|
1515
|
+
request: ProposeRigChangeRequest,
|
|
1516
|
+
): Promise<RigChange> {
|
|
1517
|
+
return await this.requestJson<RigChange>(
|
|
1518
|
+
"POST",
|
|
1519
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/changes`,
|
|
1520
|
+
request,
|
|
1521
|
+
);
|
|
818
1522
|
}
|
|
819
1523
|
|
|
820
|
-
async
|
|
821
|
-
return await this.requestJson<
|
|
1524
|
+
async getRigChange(workspaceId: string, rigId: string, changeId: string): Promise<RigChange> {
|
|
1525
|
+
return await this.requestJson<RigChange>(
|
|
1526
|
+
"GET",
|
|
1527
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/changes/${changeId}`,
|
|
1528
|
+
);
|
|
822
1529
|
}
|
|
823
1530
|
|
|
824
1531
|
/**
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
1532
|
+
* Re-run verification for a change (rigs:use). Verification is asynchronous:
|
|
1533
|
+
* this returns the change immediately with status `verifying`; poll
|
|
1534
|
+
* `getRigChange`/`listRigChanges` for the terminal outcome + logs.
|
|
828
1535
|
*/
|
|
829
|
-
async
|
|
830
|
-
return await this.requestJson<
|
|
1536
|
+
async verifyRigChange(workspaceId: string, rigId: string, changeId: string): Promise<RigChange> {
|
|
1537
|
+
return await this.requestJson<RigChange>(
|
|
831
1538
|
"POST",
|
|
832
|
-
`/v1/workspaces/${workspaceId}/
|
|
833
|
-
options.triggerId ? { triggerId: options.triggerId } : undefined,
|
|
1539
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/changes/${changeId}/verify`,
|
|
834
1540
|
);
|
|
835
1541
|
}
|
|
836
1542
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1543
|
+
/**
|
|
1544
|
+
* Promote a verified `definition_edit` change into a new active rig version
|
|
1545
|
+
* (rigs:manage). Only valid once the change's verification passed; returns the
|
|
1546
|
+
* newly minted version.
|
|
1547
|
+
*/
|
|
1548
|
+
async promoteRigChange(
|
|
842
1549
|
workspaceId: string,
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
): Promise<
|
|
846
|
-
return await this.requestJson<
|
|
847
|
-
"
|
|
848
|
-
`/v1/workspaces/${workspaceId}/
|
|
849
|
-
undefined,
|
|
850
|
-
{ ...(options.limit !== undefined ? { limit: String(options.limit) } : {}) },
|
|
1550
|
+
rigId: string,
|
|
1551
|
+
changeId: string,
|
|
1552
|
+
): Promise<RigVersion> {
|
|
1553
|
+
return await this.requestJson<RigVersion>(
|
|
1554
|
+
"POST",
|
|
1555
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/changes/${changeId}/promote`,
|
|
851
1556
|
);
|
|
852
1557
|
}
|
|
853
1558
|
|
|
854
|
-
|
|
855
|
-
|
|
1559
|
+
/**
|
|
1560
|
+
* Re-run the active version's checks in a clean throwaway sandbox (rigs:use).
|
|
1561
|
+
* Asynchronous — returns the version id being verified; the outcome lands on
|
|
1562
|
+
* the version's audit trail.
|
|
1563
|
+
*/
|
|
1564
|
+
async verifyRig(workspaceId: string, rigId: string): Promise<{ ok: boolean; versionId: string }> {
|
|
1565
|
+
return await this.requestJson<{ ok: boolean; versionId: string }>(
|
|
1566
|
+
"POST",
|
|
1567
|
+
`/v1/workspaces/${workspaceId}/rigs/${rigId}/verify`,
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
856
1570
|
|
|
857
|
-
|
|
858
|
-
|
|
1571
|
+
/** @deprecated use listVariableSets */
|
|
1572
|
+
async listEnvironments(workspaceId: string): Promise<VariableSet[]> {
|
|
1573
|
+
return await this.listVariableSets(workspaceId);
|
|
859
1574
|
}
|
|
860
1575
|
|
|
861
|
-
|
|
862
|
-
|
|
1576
|
+
/** @deprecated use createVariableSet */
|
|
1577
|
+
async createEnvironment(
|
|
1578
|
+
workspaceId: string,
|
|
1579
|
+
request: CreateVariableSetRequest,
|
|
1580
|
+
): Promise<VariableSet> {
|
|
1581
|
+
return await this.createVariableSet(workspaceId, request);
|
|
863
1582
|
}
|
|
864
1583
|
|
|
865
|
-
|
|
866
|
-
|
|
1584
|
+
/** @deprecated use getVariableSet */
|
|
1585
|
+
async getEnvironment(workspaceId: string, environmentId: string): Promise<VariableSet> {
|
|
1586
|
+
return await this.getVariableSet(workspaceId, environmentId);
|
|
867
1587
|
}
|
|
868
1588
|
|
|
1589
|
+
/** @deprecated use updateVariableSet */
|
|
869
1590
|
async updateEnvironment(
|
|
870
1591
|
workspaceId: string,
|
|
871
1592
|
environmentId: string,
|
|
872
|
-
request:
|
|
873
|
-
): Promise<
|
|
874
|
-
return await this.
|
|
875
|
-
"PATCH",
|
|
876
|
-
`/v1/workspaces/${workspaceId}/environments/${environmentId}`,
|
|
877
|
-
request,
|
|
878
|
-
);
|
|
1593
|
+
request: UpdateVariableSetRequest,
|
|
1594
|
+
): Promise<VariableSet> {
|
|
1595
|
+
return await this.updateVariableSet(workspaceId, environmentId, request);
|
|
879
1596
|
}
|
|
880
1597
|
|
|
1598
|
+
/** @deprecated use deleteVariableSet */
|
|
881
1599
|
async deleteEnvironment(workspaceId: string, environmentId: string): Promise<void> {
|
|
882
|
-
await this.
|
|
1600
|
+
await this.deleteVariableSet(workspaceId, environmentId);
|
|
883
1601
|
}
|
|
884
1602
|
|
|
885
|
-
/**
|
|
1603
|
+
/** @deprecated use setVariableSetVariable */
|
|
886
1604
|
async setEnvironmentVariable(
|
|
887
1605
|
workspaceId: string,
|
|
888
1606
|
environmentId: string,
|
|
889
1607
|
name: string,
|
|
890
1608
|
value: string,
|
|
891
|
-
): Promise<
|
|
892
|
-
return await this.
|
|
893
|
-
"PUT",
|
|
894
|
-
`/v1/workspaces/${workspaceId}/environments/${environmentId}/variables/${encodeURIComponent(name)}`,
|
|
895
|
-
{ value },
|
|
896
|
-
);
|
|
1609
|
+
): Promise<VariableSetVariableMetadata> {
|
|
1610
|
+
return await this.setVariableSetVariable(workspaceId, environmentId, name, value);
|
|
897
1611
|
}
|
|
898
1612
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1613
|
+
/** @deprecated use deleteVariableSetVariable */
|
|
1614
|
+
async deleteEnvironmentVariable(
|
|
1615
|
+
workspaceId: string,
|
|
1616
|
+
environmentId: string,
|
|
1617
|
+
name: string,
|
|
1618
|
+
): Promise<void> {
|
|
1619
|
+
await this.deleteVariableSetVariable(workspaceId, environmentId, name);
|
|
904
1620
|
}
|
|
905
1621
|
|
|
906
1622
|
// --- Files -----------------------------------------------------------------------
|
|
907
1623
|
|
|
908
1624
|
/** Step 1 of the upload flow: returns the pre-signed PUT target. */
|
|
909
|
-
async beginFileUpload(
|
|
910
|
-
|
|
1625
|
+
async beginFileUpload(
|
|
1626
|
+
workspaceId: string,
|
|
1627
|
+
request: CreateFileUploadRequest,
|
|
1628
|
+
): Promise<CreateFileUploadResponse> {
|
|
1629
|
+
return await this.requestJson<CreateFileUploadResponse>(
|
|
1630
|
+
"POST",
|
|
1631
|
+
`/v1/workspaces/${workspaceId}/files/uploads`,
|
|
1632
|
+
request,
|
|
1633
|
+
);
|
|
911
1634
|
}
|
|
912
1635
|
|
|
913
1636
|
/** Step 3 of the upload flow: server verifies the object and marks it ready. */
|
|
@@ -927,12 +1650,14 @@ export class OpenGeniClient {
|
|
|
927
1650
|
async uploadFile(workspaceId: string, input: UploadFileInput): Promise<FileAsset> {
|
|
928
1651
|
// Copy Uint8Array views into a Blob so byte offsets/shared buffers can't
|
|
929
1652
|
// leak surrounding bytes into the PUT body.
|
|
930
|
-
const body: Blob | ArrayBuffer | string =
|
|
931
|
-
? new Blob([input.data.slice()])
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1653
|
+
const body: Blob | ArrayBuffer | string =
|
|
1654
|
+
input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data;
|
|
1655
|
+
const sizeBytes =
|
|
1656
|
+
typeof body === "string"
|
|
1657
|
+
? new TextEncoder().encode(body).byteLength
|
|
1658
|
+
: body instanceof Blob
|
|
1659
|
+
? body.size
|
|
1660
|
+
: body.byteLength;
|
|
936
1661
|
const upload = await this.beginFileUpload(workspaceId, {
|
|
937
1662
|
filename: input.filename,
|
|
938
1663
|
contentType: input.contentType,
|
|
@@ -957,39 +1682,76 @@ export class OpenGeniClient {
|
|
|
957
1682
|
}
|
|
958
1683
|
|
|
959
1684
|
async getFile(workspaceId: string, fileId: string): Promise<FileAsset> {
|
|
960
|
-
return await this.requestJson<FileAsset>(
|
|
1685
|
+
return await this.requestJson<FileAsset>(
|
|
1686
|
+
"GET",
|
|
1687
|
+
`/v1/workspaces/${workspaceId}/files/${fileId}`,
|
|
1688
|
+
);
|
|
961
1689
|
}
|
|
962
1690
|
|
|
963
1691
|
/** Mint a short-lived signed download URL for a ready file. */
|
|
964
|
-
async createFileDownloadUrl(
|
|
965
|
-
|
|
1692
|
+
async createFileDownloadUrl(
|
|
1693
|
+
workspaceId: string,
|
|
1694
|
+
fileId: string,
|
|
1695
|
+
): Promise<FileDownloadUrlResponse> {
|
|
1696
|
+
return await this.requestJson<FileDownloadUrlResponse>(
|
|
1697
|
+
"POST",
|
|
1698
|
+
`/v1/workspaces/${workspaceId}/files/${fileId}/download-url`,
|
|
1699
|
+
);
|
|
966
1700
|
}
|
|
967
1701
|
|
|
968
1702
|
// --- Documents ----------------------------------------------------------------------
|
|
969
1703
|
|
|
970
|
-
async createDocumentBase(
|
|
971
|
-
|
|
1704
|
+
async createDocumentBase(
|
|
1705
|
+
workspaceId: string,
|
|
1706
|
+
request: CreateDocumentBaseRequest,
|
|
1707
|
+
): Promise<DocumentBase> {
|
|
1708
|
+
return await this.requestJson<DocumentBase>(
|
|
1709
|
+
"POST",
|
|
1710
|
+
`/v1/workspaces/${workspaceId}/document-bases`,
|
|
1711
|
+
request,
|
|
1712
|
+
);
|
|
972
1713
|
}
|
|
973
1714
|
|
|
974
1715
|
async listDocumentBases(workspaceId: string): Promise<DocumentBase[]> {
|
|
975
|
-
return await this.requestJson<DocumentBase[]>(
|
|
1716
|
+
return await this.requestJson<DocumentBase[]>(
|
|
1717
|
+
"GET",
|
|
1718
|
+
`/v1/workspaces/${workspaceId}/document-bases`,
|
|
1719
|
+
);
|
|
976
1720
|
}
|
|
977
1721
|
|
|
978
1722
|
async getDocumentBase(workspaceId: string, baseId: string): Promise<DocumentBase> {
|
|
979
|
-
return await this.requestJson<DocumentBase>(
|
|
1723
|
+
return await this.requestJson<DocumentBase>(
|
|
1724
|
+
"GET",
|
|
1725
|
+
`/v1/workspaces/${workspaceId}/document-bases/${baseId}`,
|
|
1726
|
+
);
|
|
980
1727
|
}
|
|
981
1728
|
|
|
982
1729
|
/** Index an uploaded file into the base. The file must be `ready`. */
|
|
983
|
-
async addDocument(
|
|
984
|
-
|
|
1730
|
+
async addDocument(
|
|
1731
|
+
workspaceId: string,
|
|
1732
|
+
baseId: string,
|
|
1733
|
+
request: AddDocumentRequest,
|
|
1734
|
+
): Promise<Document> {
|
|
1735
|
+
return await this.requestJson<Document>(
|
|
1736
|
+
"POST",
|
|
1737
|
+
`/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`,
|
|
1738
|
+
request,
|
|
1739
|
+
);
|
|
985
1740
|
}
|
|
986
1741
|
|
|
987
1742
|
async listDocuments(workspaceId: string, baseId: string): Promise<Document[]> {
|
|
988
|
-
return await this.requestJson<Document[]>(
|
|
1743
|
+
return await this.requestJson<Document[]>(
|
|
1744
|
+
"GET",
|
|
1745
|
+
`/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`,
|
|
1746
|
+
);
|
|
989
1747
|
}
|
|
990
1748
|
|
|
991
1749
|
/** Retry indexing for a failed document. */
|
|
992
|
-
async reindexDocument(
|
|
1750
|
+
async reindexDocument(
|
|
1751
|
+
workspaceId: string,
|
|
1752
|
+
baseId: string,
|
|
1753
|
+
documentId: string,
|
|
1754
|
+
): Promise<Document> {
|
|
993
1755
|
return await this.requestJson<Document>(
|
|
994
1756
|
"POST",
|
|
995
1757
|
`/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}/reindex`,
|
|
@@ -1023,10 +1785,17 @@ export class OpenGeniClient {
|
|
|
1023
1785
|
workspaceId: string,
|
|
1024
1786
|
request: DocumentSearchRequest,
|
|
1025
1787
|
): Promise<DocumentSearchResponse> {
|
|
1026
|
-
return await this.requestJson<DocumentSearchResponse>(
|
|
1788
|
+
return await this.requestJson<DocumentSearchResponse>(
|
|
1789
|
+
"POST",
|
|
1790
|
+
`/v1/workspaces/${workspaceId}/knowledge/search`,
|
|
1791
|
+
request,
|
|
1792
|
+
);
|
|
1027
1793
|
}
|
|
1028
1794
|
|
|
1029
|
-
async listKnowledgeMemories(
|
|
1795
|
+
async listKnowledgeMemories(
|
|
1796
|
+
workspaceId: string,
|
|
1797
|
+
request: KnowledgeMemorySearchRequest = {},
|
|
1798
|
+
): Promise<KnowledgeMemory[]> {
|
|
1030
1799
|
const params = new URLSearchParams();
|
|
1031
1800
|
if (request.query) params.set("query", request.query);
|
|
1032
1801
|
if (request.status) params.set("status", request.status);
|
|
@@ -1034,19 +1803,75 @@ export class OpenGeniClient {
|
|
|
1034
1803
|
if (request.scope) params.set("scope", request.scope);
|
|
1035
1804
|
if (request.limit) params.set("limit", String(request.limit));
|
|
1036
1805
|
const query = params.toString();
|
|
1037
|
-
return await this.requestJson<KnowledgeMemory[]>(
|
|
1806
|
+
return await this.requestJson<KnowledgeMemory[]>(
|
|
1807
|
+
"GET",
|
|
1808
|
+
`/v1/workspaces/${workspaceId}/knowledge/memories${query ? `?${query}` : ""}`,
|
|
1809
|
+
);
|
|
1038
1810
|
}
|
|
1039
1811
|
|
|
1040
1812
|
async getKnowledgeMemory(workspaceId: string, memoryId: string): Promise<KnowledgeMemory> {
|
|
1041
|
-
return await this.requestJson<KnowledgeMemory>(
|
|
1813
|
+
return await this.requestJson<KnowledgeMemory>(
|
|
1814
|
+
"GET",
|
|
1815
|
+
`/v1/workspaces/${workspaceId}/knowledge/memories/${memoryId}`,
|
|
1816
|
+
);
|
|
1042
1817
|
}
|
|
1043
1818
|
|
|
1044
|
-
async createKnowledgeMemory(
|
|
1045
|
-
|
|
1819
|
+
async createKnowledgeMemory(
|
|
1820
|
+
workspaceId: string,
|
|
1821
|
+
request: CreateKnowledgeMemoryRequest,
|
|
1822
|
+
): Promise<KnowledgeMemory> {
|
|
1823
|
+
return await this.requestJson<KnowledgeMemory>(
|
|
1824
|
+
"POST",
|
|
1825
|
+
`/v1/workspaces/${workspaceId}/knowledge/memories`,
|
|
1826
|
+
request,
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
async updateKnowledgeMemory(
|
|
1831
|
+
workspaceId: string,
|
|
1832
|
+
memoryId: string,
|
|
1833
|
+
request: UpdateKnowledgeMemoryRequest,
|
|
1834
|
+
): Promise<KnowledgeMemory> {
|
|
1835
|
+
return await this.requestJson<KnowledgeMemory>(
|
|
1836
|
+
"PATCH",
|
|
1837
|
+
`/v1/workspaces/${workspaceId}/knowledge/memories/${memoryId}`,
|
|
1838
|
+
request,
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/** Hybrid (semantic + keyword) search over the workspace's agent-visible memory. */
|
|
1843
|
+
async searchWorkspaceMemories(
|
|
1844
|
+
workspaceId: string,
|
|
1845
|
+
request: WorkspaceMemorySearchRequest,
|
|
1846
|
+
): Promise<WorkspaceMemorySearchResponse> {
|
|
1847
|
+
return await this.requestJson<WorkspaceMemorySearchResponse>(
|
|
1848
|
+
"POST",
|
|
1849
|
+
`/v1/workspaces/${workspaceId}/knowledge/memories/search`,
|
|
1850
|
+
request,
|
|
1851
|
+
);
|
|
1046
1852
|
}
|
|
1047
1853
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1854
|
+
/** Deep-merge a settings patch into the workspace (preserves unknown keys). */
|
|
1855
|
+
async updateWorkspaceSettings(
|
|
1856
|
+
workspaceId: string,
|
|
1857
|
+
request: UpdateWorkspaceSettingsRequest,
|
|
1858
|
+
): Promise<Workspace> {
|
|
1859
|
+
return await this.requestJson<Workspace>(
|
|
1860
|
+
"PATCH",
|
|
1861
|
+
`/v1/workspaces/${workspaceId}/settings`,
|
|
1862
|
+
request,
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
async setWorkspaceDefaultRig(
|
|
1867
|
+
workspaceId: string,
|
|
1868
|
+
request: SetWorkspaceDefaultRigRequest,
|
|
1869
|
+
): Promise<Workspace> {
|
|
1870
|
+
return await this.requestJson<Workspace>(
|
|
1871
|
+
"PUT",
|
|
1872
|
+
`/v1/workspaces/${workspaceId}/default-rig`,
|
|
1873
|
+
request,
|
|
1874
|
+
);
|
|
1050
1875
|
}
|
|
1051
1876
|
|
|
1052
1877
|
// --- Capability packs ------------------------------------------------------------------
|
|
@@ -1057,15 +1882,29 @@ export class OpenGeniClient {
|
|
|
1057
1882
|
}
|
|
1058
1883
|
|
|
1059
1884
|
/** Register (or replace) a workspace-scoped pack from a manifest. */
|
|
1060
|
-
async registerPack(
|
|
1061
|
-
|
|
1885
|
+
async registerPack(
|
|
1886
|
+
workspaceId: string,
|
|
1887
|
+
manifest: RegisterCapabilityPackRequest,
|
|
1888
|
+
): Promise<WorkspaceRegisteredPack> {
|
|
1889
|
+
return await this.requestJson<WorkspaceRegisteredPack>(
|
|
1890
|
+
"POST",
|
|
1891
|
+
`/v1/workspaces/${workspaceId}/packs`,
|
|
1892
|
+
manifest,
|
|
1893
|
+
);
|
|
1062
1894
|
}
|
|
1063
1895
|
|
|
1064
1896
|
async getPack(workspaceId: string, packId: string): Promise<GetPackResponse> {
|
|
1065
|
-
return await this.requestJson<GetPackResponse>(
|
|
1897
|
+
return await this.requestJson<GetPackResponse>(
|
|
1898
|
+
"GET",
|
|
1899
|
+
`/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`,
|
|
1900
|
+
);
|
|
1066
1901
|
}
|
|
1067
1902
|
|
|
1068
|
-
async enablePack(
|
|
1903
|
+
async enablePack(
|
|
1904
|
+
workspaceId: string,
|
|
1905
|
+
packId: string,
|
|
1906
|
+
request: EnablePackRequest = {},
|
|
1907
|
+
): Promise<PackInstallation> {
|
|
1069
1908
|
return await this.requestJson<PackInstallation>(
|
|
1070
1909
|
"POST",
|
|
1071
1910
|
`/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/enable`,
|
|
@@ -1075,22 +1914,38 @@ export class OpenGeniClient {
|
|
|
1075
1914
|
|
|
1076
1915
|
/** Unregister a workspace-scoped pack (built-in packs cannot be deleted). */
|
|
1077
1916
|
async deletePack(workspaceId: string, packId: string): Promise<void> {
|
|
1078
|
-
await this.requestVoid(
|
|
1917
|
+
await this.requestVoid(
|
|
1918
|
+
"DELETE",
|
|
1919
|
+
`/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`,
|
|
1920
|
+
);
|
|
1079
1921
|
}
|
|
1080
1922
|
|
|
1081
1923
|
async listPackInstallations(workspaceId: string): Promise<PackInstallation[]> {
|
|
1082
|
-
return await this.requestJson<PackInstallation[]>(
|
|
1924
|
+
return await this.requestJson<PackInstallation[]>(
|
|
1925
|
+
"GET",
|
|
1926
|
+
`/v1/workspaces/${workspaceId}/packs/installations`,
|
|
1927
|
+
);
|
|
1083
1928
|
}
|
|
1084
1929
|
|
|
1085
1930
|
// --- Capabilities -------------------------------------------------------------------------
|
|
1086
1931
|
|
|
1087
1932
|
async listCapabilities(workspaceId: string): Promise<CapabilityCatalogResponse> {
|
|
1088
|
-
return await this.requestJson<CapabilityCatalogResponse>(
|
|
1933
|
+
return await this.requestJson<CapabilityCatalogResponse>(
|
|
1934
|
+
"GET",
|
|
1935
|
+
`/v1/workspaces/${workspaceId}/capabilities`,
|
|
1936
|
+
);
|
|
1089
1937
|
}
|
|
1090
1938
|
|
|
1091
1939
|
/** Add a manual capability catalog item (e.g. a remote MCP server). */
|
|
1092
|
-
async createCapability(
|
|
1093
|
-
|
|
1940
|
+
async createCapability(
|
|
1941
|
+
workspaceId: string,
|
|
1942
|
+
request: CreateCapabilityCatalogItemRequest,
|
|
1943
|
+
): Promise<CapabilityCatalogItem> {
|
|
1944
|
+
return await this.requestJson<CapabilityCatalogItem>(
|
|
1945
|
+
"POST",
|
|
1946
|
+
`/v1/workspaces/${workspaceId}/capabilities`,
|
|
1947
|
+
request,
|
|
1948
|
+
);
|
|
1094
1949
|
}
|
|
1095
1950
|
|
|
1096
1951
|
async enableCapability(
|
|
@@ -1105,7 +1960,10 @@ export class OpenGeniClient {
|
|
|
1105
1960
|
);
|
|
1106
1961
|
}
|
|
1107
1962
|
|
|
1108
|
-
async disableCapability(
|
|
1963
|
+
async disableCapability(
|
|
1964
|
+
workspaceId: string,
|
|
1965
|
+
capabilityId: string,
|
|
1966
|
+
): Promise<CapabilityInstallation> {
|
|
1109
1967
|
return await this.requestJson<CapabilityInstallation>(
|
|
1110
1968
|
"POST",
|
|
1111
1969
|
`/v1/workspaces/${workspaceId}/capabilities/${encodeURIComponent(capabilityId)}/disable`,
|
|
@@ -1128,6 +1986,66 @@ export class OpenGeniClient {
|
|
|
1128
1986
|
);
|
|
1129
1987
|
}
|
|
1130
1988
|
|
|
1989
|
+
// --- Connections -------------------------------------------------------------------------------
|
|
1990
|
+
|
|
1991
|
+
async listConnections(workspaceId: string): Promise<ConnectionMetadata[]> {
|
|
1992
|
+
const response = await this.requestJson<ListConnectionsResponse>(
|
|
1993
|
+
"GET",
|
|
1994
|
+
`/v1/workspaces/${workspaceId}/connections`,
|
|
1995
|
+
);
|
|
1996
|
+
return response.connections;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
async createConnection(
|
|
2000
|
+
workspaceId: string,
|
|
2001
|
+
request: CreateConnectionRequest,
|
|
2002
|
+
): Promise<ConnectionMetadata> {
|
|
2003
|
+
const response = await this.requestJson<ConnectionResponse>(
|
|
2004
|
+
"POST",
|
|
2005
|
+
`/v1/workspaces/${workspaceId}/connections`,
|
|
2006
|
+
request,
|
|
2007
|
+
);
|
|
2008
|
+
return response.connection;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
async updateConnection(
|
|
2012
|
+
workspaceId: string,
|
|
2013
|
+
connectionId: string,
|
|
2014
|
+
request: UpdateConnectionRequest,
|
|
2015
|
+
): Promise<ConnectionMetadata> {
|
|
2016
|
+
const response = await this.requestJson<ConnectionResponse>(
|
|
2017
|
+
"PATCH",
|
|
2018
|
+
`/v1/workspaces/${workspaceId}/connections/${connectionId}`,
|
|
2019
|
+
request,
|
|
2020
|
+
);
|
|
2021
|
+
return response.connection;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
async deleteConnection(workspaceId: string, connectionId: string): Promise<ConnectionMetadata> {
|
|
2025
|
+
const response = await this.requestJson<ConnectionResponse>(
|
|
2026
|
+
"DELETE",
|
|
2027
|
+
`/v1/workspaces/${workspaceId}/connections/${connectionId}`,
|
|
2028
|
+
);
|
|
2029
|
+
return response.connection;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
/** Start an OAuth connection flow; redirect the user to the returned `authorizationUrl`. */
|
|
2033
|
+
async startConnectionOAuth(
|
|
2034
|
+
workspaceId: string,
|
|
2035
|
+
request: OAuthStartRequest,
|
|
2036
|
+
): Promise<OAuthStartResponse> {
|
|
2037
|
+
return await this.requestJson<OAuthStartResponse>(
|
|
2038
|
+
"POST",
|
|
2039
|
+
`/v1/workspaces/${workspaceId}/connections/oauth/start`,
|
|
2040
|
+
request,
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
/** Public, immutably-cached URL for a catalog item's logo, or null when the item has none. */
|
|
2045
|
+
catalogAssetUrl(logoAssetPath: string | null): string | null {
|
|
2046
|
+
return logoAssetPath ? `${this.baseUrl}/v1/${logoAssetPath}` : null;
|
|
2047
|
+
}
|
|
2048
|
+
|
|
1131
2049
|
// --- GitHub ----------------------------------------------------------------------------------
|
|
1132
2050
|
|
|
1133
2051
|
/** GitHub App configuration status + a signed install URL when configured. */
|
|
@@ -1145,12 +2063,18 @@ export class OpenGeniClient {
|
|
|
1145
2063
|
}
|
|
1146
2064
|
|
|
1147
2065
|
async listGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {
|
|
1148
|
-
return await this.requestJson<GitHubRepositoriesResponse>(
|
|
2066
|
+
return await this.requestJson<GitHubRepositoriesResponse>(
|
|
2067
|
+
"GET",
|
|
2068
|
+
`/v1/workspaces/${workspaceId}/github/repositories`,
|
|
2069
|
+
);
|
|
1149
2070
|
}
|
|
1150
2071
|
|
|
1151
2072
|
/** Re-sync the installation's repository list from GitHub. */
|
|
1152
2073
|
async syncGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {
|
|
1153
|
-
return await this.requestJson<GitHubRepositoriesResponse>(
|
|
2074
|
+
return await this.requestJson<GitHubRepositoriesResponse>(
|
|
2075
|
+
"POST",
|
|
2076
|
+
`/v1/workspaces/${workspaceId}/github/repositories/sync`,
|
|
2077
|
+
);
|
|
1154
2078
|
}
|
|
1155
2079
|
|
|
1156
2080
|
/** Build a GitHub App manifest + the GitHub URL to submit it to. */
|
|
@@ -1168,18 +2092,31 @@ export class OpenGeniClient {
|
|
|
1168
2092
|
// --- API keys ----------------------------------------------------------------------------------
|
|
1169
2093
|
|
|
1170
2094
|
async listApiKeys(workspaceId: string): Promise<ApiKey[]> {
|
|
1171
|
-
const response = await this.requestJson<ListApiKeysResponse>(
|
|
2095
|
+
const response = await this.requestJson<ListApiKeysResponse>(
|
|
2096
|
+
"GET",
|
|
2097
|
+
`/v1/workspaces/${workspaceId}/api-keys`,
|
|
2098
|
+
);
|
|
1172
2099
|
return response.apiKeys;
|
|
1173
2100
|
}
|
|
1174
2101
|
|
|
1175
2102
|
/** The returned `token` is shown once; only its prefix is stored. */
|
|
1176
|
-
async createApiKey(
|
|
1177
|
-
|
|
2103
|
+
async createApiKey(
|
|
2104
|
+
workspaceId: string,
|
|
2105
|
+
request: CreateApiKeyRequest,
|
|
2106
|
+
): Promise<CreateApiKeyResponse> {
|
|
2107
|
+
return await this.requestJson<CreateApiKeyResponse>(
|
|
2108
|
+
"POST",
|
|
2109
|
+
`/v1/workspaces/${workspaceId}/api-keys`,
|
|
2110
|
+
request,
|
|
2111
|
+
);
|
|
1178
2112
|
}
|
|
1179
2113
|
|
|
1180
2114
|
/** Revoke an API key. Returns the revoked key. */
|
|
1181
2115
|
async deleteApiKey(workspaceId: string, apiKeyId: string): Promise<ApiKey> {
|
|
1182
|
-
return await this.requestJson<ApiKey>(
|
|
2116
|
+
return await this.requestJson<ApiKey>(
|
|
2117
|
+
"DELETE",
|
|
2118
|
+
`/v1/workspaces/${workspaceId}/api-keys/${apiKeyId}`,
|
|
2119
|
+
);
|
|
1183
2120
|
}
|
|
1184
2121
|
|
|
1185
2122
|
// --- Billing (account-scoped) --------------------------------------------------------------------
|
|
@@ -1190,17 +2127,26 @@ export class OpenGeniClient {
|
|
|
1190
2127
|
});
|
|
1191
2128
|
}
|
|
1192
2129
|
|
|
1193
|
-
async getBillingUsage(
|
|
2130
|
+
async getBillingUsage(
|
|
2131
|
+
options: { accountId?: string; workspaceId?: string } = {},
|
|
2132
|
+
): Promise<BillingUsageResponse> {
|
|
1194
2133
|
return await this.requestJson<BillingUsageResponse>("GET", "/v1/billing/usage", undefined, {
|
|
1195
2134
|
...(options.accountId !== undefined ? { accountId: options.accountId } : {}),
|
|
1196
2135
|
...(options.workspaceId !== undefined ? { workspaceId: options.workspaceId } : {}),
|
|
1197
2136
|
});
|
|
1198
2137
|
}
|
|
1199
2138
|
|
|
1200
|
-
async getBillingEntitlements(
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
2139
|
+
async getBillingEntitlements(
|
|
2140
|
+
options: { accountId?: string } = {},
|
|
2141
|
+
): Promise<BillingEntitlementsResponse> {
|
|
2142
|
+
return await this.requestJson<BillingEntitlementsResponse>(
|
|
2143
|
+
"GET",
|
|
2144
|
+
"/v1/billing/entitlements",
|
|
2145
|
+
undefined,
|
|
2146
|
+
{
|
|
2147
|
+
...(options.accountId !== undefined ? { accountId: options.accountId } : {}),
|
|
2148
|
+
},
|
|
2149
|
+
);
|
|
1204
2150
|
}
|
|
1205
2151
|
|
|
1206
2152
|
/** Start a Stripe checkout for prepaid credits. */
|
|
@@ -1211,10 +2157,12 @@ export class OpenGeniClient {
|
|
|
1211
2157
|
// --- Internals -------------------------------------------------------------
|
|
1212
2158
|
|
|
1213
2159
|
private headers(): Record<string, string> {
|
|
1214
|
-
const extra =
|
|
2160
|
+
const extra =
|
|
2161
|
+
typeof this.options.headers === "function" ? this.options.headers() : this.options.headers;
|
|
1215
2162
|
return {
|
|
1216
2163
|
...(this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}),
|
|
1217
2164
|
...extra,
|
|
2165
|
+
[OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION,
|
|
1218
2166
|
};
|
|
1219
2167
|
}
|
|
1220
2168
|
|
|
@@ -1227,17 +2175,27 @@ export class OpenGeniClient {
|
|
|
1227
2175
|
|
|
1228
2176
|
/** Connection state + the codex models the workspace may select (empty until connected). */
|
|
1229
2177
|
async codexStatus(workspaceId: string): Promise<CodexConnectionStatus> {
|
|
1230
|
-
return await this.requestJson<CodexConnectionStatus>(
|
|
2178
|
+
return await this.requestJson<CodexConnectionStatus>(
|
|
2179
|
+
"GET",
|
|
2180
|
+
`/v1/workspaces/${workspaceId}/codex/status`,
|
|
2181
|
+
);
|
|
1231
2182
|
}
|
|
1232
2183
|
|
|
1233
2184
|
/** Begin device-code login: show `userCode` at `verificationUri`, then poll with `state`. */
|
|
1234
2185
|
async codexConnectStart(workspaceId: string): Promise<CodexConnectStart> {
|
|
1235
|
-
return await this.requestJson<CodexConnectStart>(
|
|
2186
|
+
return await this.requestJson<CodexConnectStart>(
|
|
2187
|
+
"POST",
|
|
2188
|
+
`/v1/workspaces/${workspaceId}/codex/connect/start`,
|
|
2189
|
+
);
|
|
1236
2190
|
}
|
|
1237
2191
|
|
|
1238
2192
|
/** Poll device-code authorization with the `state` from {@link codexConnectStart}. */
|
|
1239
2193
|
async codexConnectPoll(workspaceId: string, state: string): Promise<CodexConnectPoll> {
|
|
1240
|
-
return await this.requestJson<CodexConnectPoll>(
|
|
2194
|
+
return await this.requestJson<CodexConnectPoll>(
|
|
2195
|
+
"POST",
|
|
2196
|
+
`/v1/workspaces/${workspaceId}/codex/connect/poll`,
|
|
2197
|
+
{ state },
|
|
2198
|
+
);
|
|
1241
2199
|
}
|
|
1242
2200
|
|
|
1243
2201
|
/** Remaining usage / limits for the connected (ACTIVE) subscription. Back-compat. */
|
|
@@ -1247,53 +2205,105 @@ export class OpenGeniClient {
|
|
|
1247
2205
|
|
|
1248
2206
|
/** Live per-account usage read (refreshes THIS account's bearer; writes the cache). */
|
|
1249
2207
|
async codexAccountUsage(workspaceId: string, accountId: string): Promise<CodexUsage> {
|
|
1250
|
-
return await this.requestJson<CodexUsage>(
|
|
2208
|
+
return await this.requestJson<CodexUsage>(
|
|
2209
|
+
"GET",
|
|
2210
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/usage`,
|
|
2211
|
+
);
|
|
1251
2212
|
}
|
|
1252
2213
|
|
|
1253
2214
|
/** Batched live refresh across every connected account, keyed by credential id. */
|
|
1254
2215
|
async refreshCodexUsage(workspaceId: string): Promise<{ usage: CodexUsageMap }> {
|
|
1255
|
-
return await this.requestJson<{ usage: CodexUsageMap }>(
|
|
2216
|
+
return await this.requestJson<{ usage: CodexUsageMap }>(
|
|
2217
|
+
"POST",
|
|
2218
|
+
`/v1/workspaces/${workspaceId}/codex/usage/refresh`,
|
|
2219
|
+
);
|
|
1256
2220
|
}
|
|
1257
2221
|
|
|
1258
2222
|
/** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */
|
|
1259
2223
|
async codexDisconnect(workspaceId: string): Promise<{ disconnected: boolean }> {
|
|
1260
|
-
return await this.requestJson<{ disconnected: boolean }>(
|
|
2224
|
+
return await this.requestJson<{ disconnected: boolean }>(
|
|
2225
|
+
"DELETE",
|
|
2226
|
+
`/v1/workspaces/${workspaceId}/codex`,
|
|
2227
|
+
);
|
|
1261
2228
|
}
|
|
1262
2229
|
|
|
1263
2230
|
/** List every connected Codex account + the workspace active pointer + settings. */
|
|
1264
2231
|
async listCodexAccounts(workspaceId: string): Promise<CodexAccountsResponse> {
|
|
1265
|
-
return await this.requestJson<CodexAccountsResponse>(
|
|
2232
|
+
return await this.requestJson<CodexAccountsResponse>(
|
|
2233
|
+
"GET",
|
|
2234
|
+
`/v1/workspaces/${workspaceId}/codex/accounts`,
|
|
2235
|
+
);
|
|
1266
2236
|
}
|
|
1267
2237
|
|
|
1268
2238
|
/** Switch the workspace ACTIVE Codex account (the one unpinned sessions use). */
|
|
1269
|
-
async activateCodexAccount(
|
|
1270
|
-
|
|
2239
|
+
async activateCodexAccount(
|
|
2240
|
+
workspaceId: string,
|
|
2241
|
+
accountId: string,
|
|
2242
|
+
): Promise<{ activated: boolean; accountId: string }> {
|
|
2243
|
+
return await this.requestJson<{ activated: boolean; accountId: string }>(
|
|
2244
|
+
"POST",
|
|
2245
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/activate`,
|
|
2246
|
+
);
|
|
1271
2247
|
}
|
|
1272
2248
|
|
|
1273
2249
|
/** P3: enable/disable Codex auto-rotation and/or pick the strategy. Returns the effective settings. */
|
|
1274
2250
|
async setCodexRotationSettings(
|
|
1275
2251
|
workspaceId: string,
|
|
1276
|
-
patch: {
|
|
2252
|
+
patch: {
|
|
2253
|
+
rotationEnabled?: boolean;
|
|
2254
|
+
rotationStrategy?: CodexRotationSettings["rotationStrategy"];
|
|
2255
|
+
},
|
|
1277
2256
|
): Promise<CodexRotationSettings> {
|
|
1278
|
-
return await this.requestJson<CodexRotationSettings>(
|
|
2257
|
+
return await this.requestJson<CodexRotationSettings>(
|
|
2258
|
+
"PATCH",
|
|
2259
|
+
`/v1/workspaces/${workspaceId}/codex/settings`,
|
|
2260
|
+
patch,
|
|
2261
|
+
);
|
|
1279
2262
|
}
|
|
1280
2263
|
|
|
1281
2264
|
/** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */
|
|
1282
|
-
async disconnectCodexAccount(
|
|
1283
|
-
|
|
2265
|
+
async disconnectCodexAccount(
|
|
2266
|
+
workspaceId: string,
|
|
2267
|
+
accountId: string,
|
|
2268
|
+
): Promise<{ disconnected: boolean; newActiveId: string | null }> {
|
|
2269
|
+
return await this.requestJson<{ disconnected: boolean; newActiveId: string | null }>(
|
|
2270
|
+
"DELETE",
|
|
2271
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`,
|
|
2272
|
+
);
|
|
1284
2273
|
}
|
|
1285
2274
|
|
|
1286
2275
|
/** Rename a Codex account (label only in P1). */
|
|
1287
|
-
async renameCodexAccount(
|
|
1288
|
-
|
|
2276
|
+
async renameCodexAccount(
|
|
2277
|
+
workspaceId: string,
|
|
2278
|
+
accountId: string,
|
|
2279
|
+
label: string | null,
|
|
2280
|
+
): Promise<CodexAccount> {
|
|
2281
|
+
return await this.requestJson<CodexAccount>(
|
|
2282
|
+
"PATCH",
|
|
2283
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`,
|
|
2284
|
+
{ label },
|
|
2285
|
+
);
|
|
1289
2286
|
}
|
|
1290
2287
|
|
|
1291
2288
|
/** Pin (or unpin via "auto") a session's Codex account. Applies on the next turn. */
|
|
1292
|
-
async pinSessionCodexAccount(
|
|
1293
|
-
|
|
2289
|
+
async pinSessionCodexAccount(
|
|
2290
|
+
workspaceId: string,
|
|
2291
|
+
sessionId: string,
|
|
2292
|
+
target: string,
|
|
2293
|
+
): Promise<{ pinned: string }> {
|
|
2294
|
+
return await this.requestJson<{ pinned: string }>(
|
|
2295
|
+
"POST",
|
|
2296
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/codex-account`,
|
|
2297
|
+
{ target },
|
|
2298
|
+
);
|
|
1294
2299
|
}
|
|
1295
2300
|
|
|
1296
|
-
private async requestJson<T>(
|
|
2301
|
+
private async requestJson<T>(
|
|
2302
|
+
method: string,
|
|
2303
|
+
path: string,
|
|
2304
|
+
body?: unknown,
|
|
2305
|
+
query: Record<string, string> = {},
|
|
2306
|
+
): Promise<T> {
|
|
1297
2307
|
const response = await this.fetchImpl(this.url(path, query), {
|
|
1298
2308
|
method,
|
|
1299
2309
|
headers: {
|
|
@@ -1303,6 +2313,7 @@ export class OpenGeniClient {
|
|
|
1303
2313
|
},
|
|
1304
2314
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
1305
2315
|
});
|
|
2316
|
+
assertApiContractResponse(response);
|
|
1306
2317
|
if (!response.ok) {
|
|
1307
2318
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
1308
2319
|
}
|
|
@@ -1320,12 +2331,20 @@ export class OpenGeniClient {
|
|
|
1320
2331
|
},
|
|
1321
2332
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
1322
2333
|
});
|
|
2334
|
+
assertApiContractResponse(response);
|
|
1323
2335
|
if (!response.ok) {
|
|
1324
2336
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
1325
2337
|
}
|
|
1326
2338
|
}
|
|
1327
2339
|
}
|
|
1328
2340
|
|
|
2341
|
+
function assertApiContractResponse(response: Response): void {
|
|
2342
|
+
const actual = response.headers.get(OPENGENI_API_CONTRACT_HEADER);
|
|
2343
|
+
if (actual && actual !== OPENGENI_API_CONTRACT_REVISION) {
|
|
2344
|
+
throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
1329
2348
|
async function safeText(response: Response): Promise<string> {
|
|
1330
2349
|
try {
|
|
1331
2350
|
return await response.text();
|
|
@@ -1333,7 +2352,3 @@ async function safeText(response: Response): Promise<string> {
|
|
|
1333
2352
|
return "";
|
|
1334
2353
|
}
|
|
1335
2354
|
}
|
|
1336
|
-
|
|
1337
|
-
function delay(ms: number): Promise<void> {
|
|
1338
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1339
|
-
}
|