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