@opengeni/sdk 0.46.0 → 0.52.1
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 +94 -2
- package/dist/artifact-client.d.ts +20 -2
- package/dist/artifacts.d.ts +4 -0
- package/dist/artifacts.js +11 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/chunk-FHI4DFIG.js +128 -0
- package/dist/chunk-FHI4DFIG.js.map +1 -0
- package/dist/chunk-FRJFNDIR.js +218 -0
- package/dist/chunk-FRJFNDIR.js.map +1 -0
- package/dist/{chunk-QKV5OCLJ.js → chunk-ILQZR5N6.js} +1380 -595
- package/dist/chunk-ILQZR5N6.js.map +1 -0
- package/dist/chunk-MZWTWOX6.js +661 -0
- package/dist/chunk-MZWTWOX6.js.map +1 -0
- package/dist/chunk-VWE2QIVO.js +1162 -0
- package/dist/chunk-VWE2QIVO.js.map +1 -0
- package/dist/chunk-VYCTL62C.js +230 -0
- package/dist/chunk-VYCTL62C.js.map +1 -0
- package/dist/client.d.ts +176 -20
- package/dist/codex-realtime-controller.d.ts +6 -6
- package/dist/codex-realtime-lifecycle.d.ts +1 -1
- package/dist/codex-realtime-v3.d.ts +3 -3
- package/dist/codex-realtime.d.ts +1 -1
- package/dist/company-profile.d.ts +100 -0
- package/dist/core.d.ts +5 -5
- package/dist/core.js +9 -4
- package/dist/desktop.d.ts +1 -1
- package/dist/editable-artifact-resources.d.ts +96 -0
- package/dist/editable-artifacts/browser-session.d.ts +35 -0
- package/dist/editable-artifacts/controller.d.ts +60 -0
- package/dist/editable-artifacts/errors.d.ts +23 -0
- package/dist/editable-artifacts/http-live-transport.d.ts +46 -0
- package/dist/editable-artifacts/index.d.ts +19 -0
- package/dist/editable-artifacts/pool.d.ts +15 -0
- package/dist/editable-artifacts/session.d.ts +96 -0
- package/dist/editable-artifacts/storage.d.ts +72 -0
- package/dist/editable-artifacts/types.d.ts +416 -0
- package/dist/editable-artifacts/worker/browser-client.d.ts +80 -0
- package/dist/editable-artifacts/worker/browser-entry.d.ts +6 -0
- package/dist/editable-artifacts/worker/kernel-adapter.d.ts +45 -0
- package/dist/editable-artifacts/worker/rpc-protocol.d.ts +92 -0
- package/dist/editable-artifacts/worker/runtime.d.ts +66 -0
- package/dist/editable-artifacts/worker/wire-codec.d.ts +52 -0
- package/dist/editable-artifacts-worker.d.ts +8 -0
- package/dist/editable-artifacts-worker.js +1708 -0
- package/dist/editable-artifacts-worker.js.map +1 -0
- package/dist/editable-artifacts.d.ts +6 -0
- package/dist/editable-artifacts.js +5954 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/gateway-realtime-transport.d.ts +1 -1
- package/dist/index.d.ts +41 -35
- package/dist/index.js +104 -68
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +885 -0
- package/dist/interaction.js +55 -0
- package/dist/interaction.js.map +1 -0
- package/dist/memory-slack-client.d.ts +13 -0
- package/dist/memory-slack-delivery.d.ts +99 -0
- package/dist/memory-slack.d.ts +4 -0
- package/dist/memory-slack.js +44 -0
- package/dist/memory-slack.js.map +1 -0
- package/dist/proxy.d.ts +3 -3
- package/dist/realtime.d.ts +12 -12
- package/dist/retained-artifacts.d.ts +12 -0
- package/dist/stream.d.ts +16 -1
- package/dist/terminal.d.ts +1 -1
- package/dist/transcription.d.ts +2 -2
- package/dist/types.d.ts +1203 -23
- package/dist/workspace-control-stream.d.ts +2 -2
- package/dist/workspace-state.d.ts +1 -1
- package/package.json +29 -3
- package/src/artifact-client.ts +129 -0
- package/src/artifacts.ts +17 -0
- package/src/client.ts +1801 -206
- package/src/company-profile.ts +92 -0
- package/src/editable-artifact-resources.ts +119 -0
- package/src/editable-artifacts/browser-session.ts +176 -0
- package/src/editable-artifacts/controller.ts +2632 -0
- package/src/editable-artifacts/errors.ts +52 -0
- package/src/editable-artifacts/http-live-transport.ts +1449 -0
- package/src/editable-artifacts/index.ts +175 -0
- package/src/editable-artifacts/pool.ts +81 -0
- package/src/editable-artifacts/session.ts +778 -0
- package/src/editable-artifacts/storage.ts +1718 -0
- package/src/editable-artifacts/types.ts +499 -0
- package/src/editable-artifacts/worker/browser-client.ts +1167 -0
- package/src/editable-artifacts/worker/browser-entry.ts +57 -0
- package/src/editable-artifacts/worker/kernel-adapter.ts +1039 -0
- package/src/editable-artifacts/worker/rpc-protocol.ts +622 -0
- package/src/editable-artifacts/worker/runtime.ts +1204 -0
- package/src/editable-artifacts/worker/wire-codec.ts +852 -0
- package/src/editable-artifacts-worker.ts +8 -0
- package/src/editable-artifacts.ts +6 -0
- package/src/index.ts +194 -0
- package/src/interaction.ts +1830 -0
- package/src/memory-slack-client.ts +71 -0
- package/src/memory-slack-delivery.ts +128 -0
- package/src/memory-slack.ts +19 -0
- package/src/retained-artifacts.ts +326 -0
- package/src/stream.ts +102 -4
- package/src/transcription.ts +2 -2
- package/src/types.ts +1430 -33
- package/src/workspace-control-stream.ts +15 -5
- package/dist/chunk-QKV5OCLJ.js.map +0 -1
|
@@ -0,0 +1,1830 @@
|
|
|
1
|
+
import type { OpenGeniRequestOptions } from "./client";
|
|
2
|
+
import type { RetainedArtifactReference } from "./types";
|
|
3
|
+
|
|
4
|
+
/** Public Browser/Computer protocol version. Driver and provider details stay private. */
|
|
5
|
+
export const INTERACTION_PROTOCOL_VERSION = 1 as const;
|
|
6
|
+
export const BROWSER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.browser.v1" as const;
|
|
7
|
+
export const COMPUTER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.computer.v1" as const;
|
|
8
|
+
export const BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX = "opengeni.auth." as const;
|
|
9
|
+
export const BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES = 64 * 1024;
|
|
10
|
+
export const BROWSER_FRAME_MAX_BYTES = 24 * 1024 * 1024;
|
|
11
|
+
export const BROWSER_FRAME_MAX_DIMENSION = 32_768;
|
|
12
|
+
export const BROWSER_FRAME_MAX_PIXELS = 100_000_000;
|
|
13
|
+
|
|
14
|
+
export type InteractionJsonValue =
|
|
15
|
+
| null
|
|
16
|
+
| string
|
|
17
|
+
| number
|
|
18
|
+
| boolean
|
|
19
|
+
| InteractionJsonValue[]
|
|
20
|
+
| { [key: string]: InteractionJsonValue };
|
|
21
|
+
|
|
22
|
+
export type InteractionPlacement =
|
|
23
|
+
| { kind: "sandbox_group"; sandboxGroupId: string }
|
|
24
|
+
| { kind: "connected_machine"; sandboxId: string }
|
|
25
|
+
| { kind: "attached_device"; deviceId: string }
|
|
26
|
+
| { kind: "external_provider"; providerId: string; placementId: string };
|
|
27
|
+
|
|
28
|
+
export type AttachedBrowserDeviceCapabilities = {
|
|
29
|
+
tabInventory: boolean;
|
|
30
|
+
debuggerAttachment: boolean;
|
|
31
|
+
semanticObservation: boolean;
|
|
32
|
+
screenshots: boolean;
|
|
33
|
+
liveFrames: boolean;
|
|
34
|
+
humanInput: boolean;
|
|
35
|
+
diagnostics: boolean;
|
|
36
|
+
rawCdp: boolean;
|
|
37
|
+
linkedComputer: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** One live browser-profile endpoint on an enrolled machine. Saved, reusable
|
|
41
|
+
* login state is represented separately by BrowserIdentity. */
|
|
42
|
+
export type AttachedBrowserDevice = {
|
|
43
|
+
id: string;
|
|
44
|
+
accountId: string;
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
enrollmentId: string;
|
|
47
|
+
name: string;
|
|
48
|
+
profileLabel: string | null;
|
|
49
|
+
browserName: string;
|
|
50
|
+
browserVersion: string;
|
|
51
|
+
extensionVersion: string;
|
|
52
|
+
platform: "linux" | "macos" | "windows";
|
|
53
|
+
architecture: "x64" | "arm64";
|
|
54
|
+
state: "connected" | "disconnected";
|
|
55
|
+
connectionGeneration: string;
|
|
56
|
+
inventoryRevision: number;
|
|
57
|
+
tabCount: number;
|
|
58
|
+
capabilities: AttachedBrowserDeviceCapabilities;
|
|
59
|
+
lastSeenAt: string;
|
|
60
|
+
disconnectedAt: string | null;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type AttachedBrowserDeviceListResponse = {
|
|
66
|
+
revision: number;
|
|
67
|
+
devices: AttachedBrowserDevice[];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type InteractionControllerBinding = {
|
|
71
|
+
controllerId: string;
|
|
72
|
+
controllerGeneration: string;
|
|
73
|
+
placementInstanceId: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type InteractionAssociation = {
|
|
77
|
+
sessionId: string;
|
|
78
|
+
turnId: string | null;
|
|
79
|
+
attemptId: string | null;
|
|
80
|
+
relationship: "created" | "using" | "observing" | "related";
|
|
81
|
+
actorSubjectId: string;
|
|
82
|
+
lastUsedAt: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type InteractionLifecycle =
|
|
86
|
+
| "starting"
|
|
87
|
+
| "active"
|
|
88
|
+
| "suspending"
|
|
89
|
+
| "suspended"
|
|
90
|
+
| "restoring"
|
|
91
|
+
| "repair_required"
|
|
92
|
+
| "lost"
|
|
93
|
+
| "ending"
|
|
94
|
+
| "ended"
|
|
95
|
+
| "failed";
|
|
96
|
+
|
|
97
|
+
export type BrowserSessionCapabilities = {
|
|
98
|
+
semanticObservation: boolean;
|
|
99
|
+
screenshots: boolean;
|
|
100
|
+
liveFrames: boolean;
|
|
101
|
+
humanInput: boolean;
|
|
102
|
+
tabs: boolean;
|
|
103
|
+
downloads: boolean;
|
|
104
|
+
uploads: boolean;
|
|
105
|
+
clipboard: boolean;
|
|
106
|
+
diagnostics: boolean;
|
|
107
|
+
rawCdp: boolean;
|
|
108
|
+
linkedComputer: boolean;
|
|
109
|
+
privateCheckpoint: boolean;
|
|
110
|
+
identityPublication: boolean;
|
|
111
|
+
parallelTargets: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type BrowserSession = {
|
|
115
|
+
id: string;
|
|
116
|
+
accountId: string;
|
|
117
|
+
workspaceId: string;
|
|
118
|
+
name: string;
|
|
119
|
+
lifecycle: InteractionLifecycle;
|
|
120
|
+
placement: InteractionPlacement;
|
|
121
|
+
controller: InteractionControllerBinding | null;
|
|
122
|
+
driverId: string;
|
|
123
|
+
engine: "chromium" | "chrome" | "firefox" | "webkit" | "lightpanda" | "external";
|
|
124
|
+
engineVersion: string | null;
|
|
125
|
+
headless: boolean;
|
|
126
|
+
identityId: string | null;
|
|
127
|
+
baseRevisionId: string | null;
|
|
128
|
+
networkRouteId: string | null;
|
|
129
|
+
linkedComputerSessionId: string | null;
|
|
130
|
+
capabilities: BrowserSessionCapabilities;
|
|
131
|
+
associations: InteractionAssociation[];
|
|
132
|
+
createdBySubjectId: string;
|
|
133
|
+
createdAt: string;
|
|
134
|
+
lastUsedAt: string;
|
|
135
|
+
failureCode: string | null;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type BrowserIdentityStatus = "active" | "archived";
|
|
139
|
+
export type BrowserRevisionComponentKind =
|
|
140
|
+
| "chromium_profile"
|
|
141
|
+
| "normalized_web_state"
|
|
142
|
+
| "provider_snapshot";
|
|
143
|
+
export type BrowserRevisionPortability = "portable" | "provider_bound" | "placement_bound";
|
|
144
|
+
|
|
145
|
+
export type BrowserRevisionMaterialization = {
|
|
146
|
+
portability: BrowserRevisionPortability;
|
|
147
|
+
reason: string | null;
|
|
148
|
+
platform: "linux" | "macos" | "windows" | null;
|
|
149
|
+
architecture: "x64" | "arm64" | null;
|
|
150
|
+
engine: "chromium" | "chrome" | "firefox" | "webkit" | "lightpanda" | "external";
|
|
151
|
+
engineVersion: string | null;
|
|
152
|
+
driverId: string;
|
|
153
|
+
driverSchemaVersion: number;
|
|
154
|
+
profileCrypto: "chromium_basic" | "chromium_mock_keychain" | "platform_bound";
|
|
155
|
+
providerId: string | null;
|
|
156
|
+
placement: InteractionPlacement | null;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export type BrowserRevisionComponent = {
|
|
160
|
+
id: string;
|
|
161
|
+
kind: BrowserRevisionComponentKind;
|
|
162
|
+
format: string;
|
|
163
|
+
artifactDigest: string;
|
|
164
|
+
sizeBytes: number;
|
|
165
|
+
materialization: BrowserRevisionMaterialization;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export type BrowserRevision = {
|
|
169
|
+
id: string;
|
|
170
|
+
accountId: string;
|
|
171
|
+
workspaceId: string;
|
|
172
|
+
identityId: string;
|
|
173
|
+
parentRevisionId: string | null;
|
|
174
|
+
ordinal: number;
|
|
175
|
+
sourceBrowserSessionId: string;
|
|
176
|
+
manifestDigest: string;
|
|
177
|
+
components: BrowserRevisionComponent[];
|
|
178
|
+
createdBySubjectId: string;
|
|
179
|
+
createdAt: string;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export type BrowserIdentity = {
|
|
183
|
+
id: string;
|
|
184
|
+
accountId: string;
|
|
185
|
+
workspaceId: string;
|
|
186
|
+
name: string;
|
|
187
|
+
status: BrowserIdentityStatus;
|
|
188
|
+
defaultRevisionId: string | null;
|
|
189
|
+
headGeneration: number;
|
|
190
|
+
revisionCount: number;
|
|
191
|
+
createdBySubjectId: string;
|
|
192
|
+
createdAt: string;
|
|
193
|
+
updatedAt: string;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type BrowserIdentityListResponse = {
|
|
197
|
+
revision: number;
|
|
198
|
+
identities: BrowserIdentity[];
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export type BrowserRevisionListResponse = {
|
|
202
|
+
identity: BrowserIdentity;
|
|
203
|
+
revisions: BrowserRevision[];
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export type CreateBrowserIdentityRequest = {
|
|
207
|
+
operationId: string;
|
|
208
|
+
name: string;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export type BrowserIdentityMutationResponse = {
|
|
212
|
+
identity: BrowserIdentity;
|
|
213
|
+
operationId: string;
|
|
214
|
+
replayed: boolean;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export type PublishBrowserRevisionRequest = {
|
|
218
|
+
operationId: string;
|
|
219
|
+
identityId: string;
|
|
220
|
+
expectedHeadGeneration: number;
|
|
221
|
+
advanceDefault?: boolean | undefined;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export type PublishBrowserRevisionResponse = {
|
|
225
|
+
identity: BrowserIdentity;
|
|
226
|
+
revision: BrowserRevision;
|
|
227
|
+
outcome: "saved_as_default" | "saved_not_default";
|
|
228
|
+
replayed: boolean;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export type BrowserTarget = {
|
|
232
|
+
id: string;
|
|
233
|
+
browserSessionId: string;
|
|
234
|
+
controllerGeneration: string;
|
|
235
|
+
targetGeneration: string;
|
|
236
|
+
documentGeneration: string | null;
|
|
237
|
+
kind: "page" | "popup" | "background_page" | "worker";
|
|
238
|
+
title: string;
|
|
239
|
+
url: string;
|
|
240
|
+
selected: boolean;
|
|
241
|
+
attached: boolean;
|
|
242
|
+
createdAt: string;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export type InteractionRect = {
|
|
246
|
+
x: number;
|
|
247
|
+
y: number;
|
|
248
|
+
width: number;
|
|
249
|
+
height: number;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export type InteractionRedactedValue = {
|
|
253
|
+
redacted: true;
|
|
254
|
+
reason: "password" | "payment" | "private" | "policy";
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export type InteractionSemanticNode = {
|
|
258
|
+
ref: string;
|
|
259
|
+
role: string;
|
|
260
|
+
identifier?: string | undefined;
|
|
261
|
+
name?: string | undefined;
|
|
262
|
+
description?: string | undefined;
|
|
263
|
+
value?: string | InteractionRedactedValue | undefined;
|
|
264
|
+
states: string[];
|
|
265
|
+
bounds?: InteractionRect | undefined;
|
|
266
|
+
actions: string[];
|
|
267
|
+
children?: InteractionSemanticNode[] | undefined;
|
|
268
|
+
native?: { platform: "dom" | "mac_ax" | "at_spi" | "uia"; data: unknown } | undefined;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export type InteractionSemanticSnapshot = {
|
|
272
|
+
kind: "snapshot";
|
|
273
|
+
roots: InteractionSemanticNode[];
|
|
274
|
+
nodeCount: number;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export type InteractionSemanticDiff = {
|
|
278
|
+
kind: "diff";
|
|
279
|
+
baseObservationId: string;
|
|
280
|
+
removedRefs: string[];
|
|
281
|
+
changed: InteractionSemanticNode[];
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export type InteractionDiagnosticSummary = {
|
|
285
|
+
consoleErrorCount: number;
|
|
286
|
+
failedRequestCount: number;
|
|
287
|
+
downloadCount: number;
|
|
288
|
+
pageErrorCount: number;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
export type BrowserDialog = {
|
|
292
|
+
type: "alert" | "confirm" | "prompt" | "beforeunload";
|
|
293
|
+
message: string;
|
|
294
|
+
defaultPrompt: string;
|
|
295
|
+
openedAt: string;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export type BrowserObservation = {
|
|
299
|
+
protocolVersion: typeof INTERACTION_PROTOCOL_VERSION;
|
|
300
|
+
observationId: string;
|
|
301
|
+
browserSessionId: string;
|
|
302
|
+
target: BrowserTarget;
|
|
303
|
+
frameId: string | null;
|
|
304
|
+
semantic: InteractionSemanticSnapshot | InteractionSemanticDiff | null;
|
|
305
|
+
screenshot: RetainedArtifactReference | null;
|
|
306
|
+
focusedRef: string | null;
|
|
307
|
+
changedRegions: InteractionRect[];
|
|
308
|
+
diagnostics: InteractionDiagnosticSummary;
|
|
309
|
+
dialog: BrowserDialog | null;
|
|
310
|
+
observedAt: string;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
export type BrowserDiagnosticKind = "console" | "page_error" | "failed_request" | "download";
|
|
314
|
+
|
|
315
|
+
export type BrowserDiagnosticEntry = {
|
|
316
|
+
sequence: number;
|
|
317
|
+
kind: BrowserDiagnosticKind;
|
|
318
|
+
level: "debug" | "info" | "warning" | "error" | null;
|
|
319
|
+
message: string;
|
|
320
|
+
url: string | null;
|
|
321
|
+
method: string | null;
|
|
322
|
+
status: number | null;
|
|
323
|
+
filename: string | null;
|
|
324
|
+
occurredAt: string;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export type BrowserDiagnosticBatch = {
|
|
328
|
+
browserSessionId: string;
|
|
329
|
+
controllerGeneration: string;
|
|
330
|
+
targetId: string;
|
|
331
|
+
targetGeneration: string;
|
|
332
|
+
entries: BrowserDiagnosticEntry[];
|
|
333
|
+
cursor: number;
|
|
334
|
+
truncated: boolean;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export type BrowserLocator =
|
|
338
|
+
| { kind: "ref"; ref: string }
|
|
339
|
+
| {
|
|
340
|
+
kind: "role";
|
|
341
|
+
role: string;
|
|
342
|
+
name?: string | undefined;
|
|
343
|
+
exact?: boolean | undefined;
|
|
344
|
+
}
|
|
345
|
+
| { kind: "label"; text: string }
|
|
346
|
+
| { kind: "text"; text: string }
|
|
347
|
+
| { kind: "placeholder"; text: string }
|
|
348
|
+
| { kind: "test_id"; value: string }
|
|
349
|
+
| { kind: "css"; selector: string };
|
|
350
|
+
|
|
351
|
+
export type BrowserAction =
|
|
352
|
+
| { type: "navigate"; url: string }
|
|
353
|
+
| {
|
|
354
|
+
type: "click";
|
|
355
|
+
locator: BrowserLocator;
|
|
356
|
+
button?: "left" | "right" | "middle" | undefined;
|
|
357
|
+
}
|
|
358
|
+
| { type: "double_click"; locator: BrowserLocator }
|
|
359
|
+
| { type: "hover"; locator: BrowserLocator }
|
|
360
|
+
| { type: "fill"; locator: BrowserLocator; value: string }
|
|
361
|
+
| { type: "type"; locator?: BrowserLocator | undefined; text: string }
|
|
362
|
+
| { type: "press"; locator?: BrowserLocator | undefined; key: string }
|
|
363
|
+
| { type: "select"; locator: BrowserLocator; values: string[] }
|
|
364
|
+
| { type: "check"; locator: BrowserLocator; checked: boolean }
|
|
365
|
+
| {
|
|
366
|
+
type: "scroll";
|
|
367
|
+
locator?: BrowserLocator | undefined;
|
|
368
|
+
deltaX: number;
|
|
369
|
+
deltaY: number;
|
|
370
|
+
}
|
|
371
|
+
| { type: "drag"; from: BrowserLocator; to: BrowserLocator }
|
|
372
|
+
| {
|
|
373
|
+
type: "pointer";
|
|
374
|
+
action: "click" | "double_click" | "move" | "scroll" | "drag";
|
|
375
|
+
x: number;
|
|
376
|
+
y: number;
|
|
377
|
+
endX?: number | undefined;
|
|
378
|
+
endY?: number | undefined;
|
|
379
|
+
deltaX?: number | undefined;
|
|
380
|
+
deltaY?: number | undefined;
|
|
381
|
+
button?: "left" | "right" | "middle" | undefined;
|
|
382
|
+
}
|
|
383
|
+
| {
|
|
384
|
+
type: "handle_dialog";
|
|
385
|
+
response: "accept" | "dismiss";
|
|
386
|
+
promptText?: string | undefined;
|
|
387
|
+
}
|
|
388
|
+
| { type: "upload"; locator: BrowserLocator; workspaceFileIds: string[] }
|
|
389
|
+
| {
|
|
390
|
+
type: "wait";
|
|
391
|
+
condition: "load" | "network_idle" | "visible" | "hidden";
|
|
392
|
+
locator?: BrowserLocator | undefined;
|
|
393
|
+
timeoutMs?: number | undefined;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export type BrowserActionBatch = { type: "batch"; actions: BrowserAction[] };
|
|
397
|
+
|
|
398
|
+
export type InteractionError = {
|
|
399
|
+
code:
|
|
400
|
+
| "resource_not_found"
|
|
401
|
+
| "resource_unavailable"
|
|
402
|
+
| "controller_stale"
|
|
403
|
+
| "target_not_found"
|
|
404
|
+
| "target_stale"
|
|
405
|
+
| "observation_stale"
|
|
406
|
+
| "document_stale"
|
|
407
|
+
| "frame_stale"
|
|
408
|
+
| "locator_not_found"
|
|
409
|
+
| "locator_ambiguous"
|
|
410
|
+
| "unsupported"
|
|
411
|
+
| "permission_denied"
|
|
412
|
+
| "machine_locked"
|
|
413
|
+
| "attempt_stale"
|
|
414
|
+
| "operation_conflict"
|
|
415
|
+
| "outcome_unknown"
|
|
416
|
+
| "invalid_action"
|
|
417
|
+
| "timeout"
|
|
418
|
+
| "controller_lost"
|
|
419
|
+
| "driver_failed";
|
|
420
|
+
message: string;
|
|
421
|
+
retryable: boolean;
|
|
422
|
+
details?: Record<string, InteractionJsonValue> | undefined;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
export type InteractionOperationState =
|
|
426
|
+
| "prepared"
|
|
427
|
+
| "dispatched"
|
|
428
|
+
| "completed"
|
|
429
|
+
| "failed"
|
|
430
|
+
| "outcome_unknown";
|
|
431
|
+
|
|
432
|
+
export type InteractionLifecycleOperationReceipt = {
|
|
433
|
+
operationId: string;
|
|
434
|
+
resourceKind: "browser_session" | "computer_session";
|
|
435
|
+
resourceId: string;
|
|
436
|
+
kind: "create" | "resume" | "suspend" | "end" | "publish";
|
|
437
|
+
state: InteractionOperationState;
|
|
438
|
+
replayed: boolean;
|
|
439
|
+
error: InteractionError | null;
|
|
440
|
+
createdAt: string;
|
|
441
|
+
dispatchedAt: string | null;
|
|
442
|
+
settledAt: string | null;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
export type CreateBrowserSessionRequest = {
|
|
446
|
+
operationId: string;
|
|
447
|
+
sessionId: string;
|
|
448
|
+
name?: string | undefined;
|
|
449
|
+
initialUrl?: string | undefined;
|
|
450
|
+
headless?: boolean | undefined;
|
|
451
|
+
placement?: InteractionPlacement | undefined;
|
|
452
|
+
identityId?: string | undefined;
|
|
453
|
+
baseRevisionId?: string | undefined;
|
|
454
|
+
linkedComputerSessionId?: string | undefined;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
export type BrowserSessionListResponse = {
|
|
458
|
+
revision: number;
|
|
459
|
+
sessions: BrowserSession[];
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
export type BrowserSessionMutationResponse = {
|
|
463
|
+
session: BrowserSession;
|
|
464
|
+
operation: InteractionLifecycleOperationReceipt;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
export type BrowserSessionLifecycleRequest = { operationId: string };
|
|
468
|
+
export type BrowserOpenTargetRequest = { url?: string | undefined };
|
|
469
|
+
|
|
470
|
+
export type BrowserTargetListResponse = {
|
|
471
|
+
browserSessionId: string;
|
|
472
|
+
controllerGeneration: string;
|
|
473
|
+
targets: BrowserTarget[];
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
export type InteractionFrameStreamAttachment<RelayKind extends 3 | 4 = 3 | 4> =
|
|
477
|
+
| { kind: "direct_websocket"; url: string; protocols: string[] }
|
|
478
|
+
| {
|
|
479
|
+
kind: "relay";
|
|
480
|
+
url: string;
|
|
481
|
+
token: string;
|
|
482
|
+
channel: {
|
|
483
|
+
channelId: string;
|
|
484
|
+
workspaceId: string;
|
|
485
|
+
agentId: string;
|
|
486
|
+
kind: RelayKind;
|
|
487
|
+
port: number;
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
export type BrowserSessionAttachment = {
|
|
492
|
+
browserSessionId: string;
|
|
493
|
+
controllerGeneration: string;
|
|
494
|
+
targetId: string;
|
|
495
|
+
stream: InteractionFrameStreamAttachment<3>;
|
|
496
|
+
expiresAt: string;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
export type BrowserSessionAttachmentRequest = {
|
|
500
|
+
targetId: string;
|
|
501
|
+
expiresInSeconds?: number | undefined;
|
|
502
|
+
stream?: BrowserFrameStreamOptions | undefined;
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
export type BrowserActionRequest = {
|
|
506
|
+
operationId: string;
|
|
507
|
+
targetId: string;
|
|
508
|
+
expectedTargetGeneration: string;
|
|
509
|
+
expectedDocumentGeneration: string | null;
|
|
510
|
+
expectedFrameId: string | null;
|
|
511
|
+
action: BrowserAction | BrowserActionBatch;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
export type BrowserActionReceipt = {
|
|
515
|
+
protocolVersion: typeof INTERACTION_PROTOCOL_VERSION;
|
|
516
|
+
operationId: string;
|
|
517
|
+
browserSessionId: string;
|
|
518
|
+
controllerGeneration: string;
|
|
519
|
+
targetId: string;
|
|
520
|
+
state: InteractionOperationState;
|
|
521
|
+
dispatchedAt: string | null;
|
|
522
|
+
settledAt: string | null;
|
|
523
|
+
observation: BrowserObservation | null;
|
|
524
|
+
error: InteractionError | null;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
export type BrowserSessionHeartbeatResponse = {
|
|
528
|
+
browserSessionId: string;
|
|
529
|
+
controllerGeneration: string;
|
|
530
|
+
alive: true;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
export type ComputerSessionCapabilities = {
|
|
534
|
+
semanticObservation: boolean;
|
|
535
|
+
appDiscovery: boolean;
|
|
536
|
+
appLaunch: boolean;
|
|
537
|
+
windowCapture: boolean;
|
|
538
|
+
screenCapture: boolean;
|
|
539
|
+
semanticActions: boolean;
|
|
540
|
+
pointerInput: boolean;
|
|
541
|
+
keyboardInput: boolean;
|
|
542
|
+
backgroundActions: boolean;
|
|
543
|
+
parallelApps: boolean;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
export type ComputerSession = {
|
|
547
|
+
id: string;
|
|
548
|
+
accountId: string;
|
|
549
|
+
workspaceId: string;
|
|
550
|
+
name: string;
|
|
551
|
+
lifecycle: InteractionLifecycle;
|
|
552
|
+
placement: InteractionPlacement;
|
|
553
|
+
controller: InteractionControllerBinding | null;
|
|
554
|
+
platform: "linux" | "macos" | "windows" | null;
|
|
555
|
+
adapter: string | null;
|
|
556
|
+
seatId: string | null;
|
|
557
|
+
displayId: string | null;
|
|
558
|
+
capabilities: ComputerSessionCapabilities | null;
|
|
559
|
+
associations: InteractionAssociation[];
|
|
560
|
+
createdBySubjectId: string;
|
|
561
|
+
createdAt: string;
|
|
562
|
+
lastUsedAt: string;
|
|
563
|
+
failureCode: string | null;
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
export type ComputerTarget = {
|
|
567
|
+
id: string;
|
|
568
|
+
computerSessionId: string;
|
|
569
|
+
controllerGeneration: string;
|
|
570
|
+
targetGeneration: string;
|
|
571
|
+
kind: "app" | "window" | "screen";
|
|
572
|
+
applicationId: string | null;
|
|
573
|
+
processId: number | null;
|
|
574
|
+
title: string;
|
|
575
|
+
bounds: InteractionRect | null;
|
|
576
|
+
focused: boolean;
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
export type ComputerObservation = {
|
|
580
|
+
protocolVersion: typeof INTERACTION_PROTOCOL_VERSION;
|
|
581
|
+
observationId: string;
|
|
582
|
+
computerSessionId: string;
|
|
583
|
+
target: ComputerTarget;
|
|
584
|
+
frameId: string | null;
|
|
585
|
+
semantic: InteractionSemanticSnapshot | InteractionSemanticDiff | null;
|
|
586
|
+
screenshot: RetainedArtifactReference | null;
|
|
587
|
+
focusedRef: string | null;
|
|
588
|
+
changedRegions: InteractionRect[];
|
|
589
|
+
observedAt: string;
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
export type ComputerLocator =
|
|
593
|
+
| { kind: "ref"; ref: string }
|
|
594
|
+
| {
|
|
595
|
+
kind: "role";
|
|
596
|
+
role: string;
|
|
597
|
+
name?: string | undefined;
|
|
598
|
+
exact?: boolean | undefined;
|
|
599
|
+
}
|
|
600
|
+
| { kind: "label"; text: string; exact?: boolean | undefined }
|
|
601
|
+
| { kind: "text"; text: string; exact?: boolean | undefined }
|
|
602
|
+
| { kind: "identifier"; value: string };
|
|
603
|
+
|
|
604
|
+
export type ComputerAction =
|
|
605
|
+
| {
|
|
606
|
+
type: "semantic";
|
|
607
|
+
locator: ComputerLocator;
|
|
608
|
+
action:
|
|
609
|
+
| "invoke"
|
|
610
|
+
| "focus"
|
|
611
|
+
| "set_value"
|
|
612
|
+
| "increment"
|
|
613
|
+
| "decrement"
|
|
614
|
+
| "select"
|
|
615
|
+
| "deselect"
|
|
616
|
+
| "expand"
|
|
617
|
+
| "collapse"
|
|
618
|
+
| "show_menu"
|
|
619
|
+
| "scroll_into_view";
|
|
620
|
+
value?: string | number | boolean | undefined;
|
|
621
|
+
}
|
|
622
|
+
| {
|
|
623
|
+
type: "pointer";
|
|
624
|
+
frameId: string;
|
|
625
|
+
action: "click" | "double_click" | "move" | "scroll" | "drag";
|
|
626
|
+
x: number;
|
|
627
|
+
y: number;
|
|
628
|
+
endX?: number | undefined;
|
|
629
|
+
endY?: number | undefined;
|
|
630
|
+
deltaX?: number | undefined;
|
|
631
|
+
deltaY?: number | undefined;
|
|
632
|
+
button?: "left" | "right" | "middle" | undefined;
|
|
633
|
+
}
|
|
634
|
+
| { type: "keyboard"; action: "type" | "press"; value: string }
|
|
635
|
+
| { type: "focus"; targetId: string }
|
|
636
|
+
| { type: "launch"; applicationId: string };
|
|
637
|
+
|
|
638
|
+
export type CreateComputerSessionRequest = {
|
|
639
|
+
operationId: string;
|
|
640
|
+
sessionId: string;
|
|
641
|
+
name?: string | undefined;
|
|
642
|
+
placement?: InteractionPlacement | undefined;
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
export type ComputerSessionListResponse = {
|
|
646
|
+
revision: number;
|
|
647
|
+
sessions: ComputerSession[];
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
export type ComputerSessionMutationResponse = {
|
|
651
|
+
session: ComputerSession;
|
|
652
|
+
operation: InteractionLifecycleOperationReceipt;
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
export type ComputerSessionLifecycleRequest = { operationId: string };
|
|
656
|
+
|
|
657
|
+
export type ComputerTargetListResponse = {
|
|
658
|
+
computerSessionId: string;
|
|
659
|
+
controllerGeneration: string;
|
|
660
|
+
targets: ComputerTarget[];
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
export type InteractionFrameStreamOptions = {
|
|
664
|
+
format?: "jpeg" | "png" | undefined;
|
|
665
|
+
quality?: number | undefined;
|
|
666
|
+
maxWidth?: number | undefined;
|
|
667
|
+
maxHeight?: number | undefined;
|
|
668
|
+
everyNthFrame?: number | undefined;
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
export type ComputerFrameStreamOptions = InteractionFrameStreamOptions;
|
|
672
|
+
|
|
673
|
+
export type ComputerSessionAttachment = {
|
|
674
|
+
computerSessionId: string;
|
|
675
|
+
controllerGeneration: string;
|
|
676
|
+
targetId: string;
|
|
677
|
+
stream: InteractionFrameStreamAttachment<4>;
|
|
678
|
+
expiresAt: string;
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
export type ComputerSessionAttachmentRequest = {
|
|
682
|
+
targetId: string;
|
|
683
|
+
expiresInSeconds?: number | undefined;
|
|
684
|
+
stream?: ComputerFrameStreamOptions | undefined;
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
export type ComputerActionRequest = {
|
|
688
|
+
operationId: string;
|
|
689
|
+
targetId: string;
|
|
690
|
+
expectedTargetGeneration: string;
|
|
691
|
+
expectedObservationId: string | null;
|
|
692
|
+
expectedFrameId: string | null;
|
|
693
|
+
action: ComputerAction;
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
export type ComputerActionReceipt = {
|
|
697
|
+
protocolVersion: typeof INTERACTION_PROTOCOL_VERSION;
|
|
698
|
+
operationId: string;
|
|
699
|
+
computerSessionId: string;
|
|
700
|
+
controllerGeneration: string;
|
|
701
|
+
targetId: string;
|
|
702
|
+
state: InteractionOperationState;
|
|
703
|
+
dispatchedAt: string | null;
|
|
704
|
+
settledAt: string | null;
|
|
705
|
+
observation: ComputerObservation | null;
|
|
706
|
+
error: InteractionError | null;
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
export type ComputerSessionHeartbeatResponse = {
|
|
710
|
+
computerSessionId: string;
|
|
711
|
+
controllerGeneration: string;
|
|
712
|
+
alive: true;
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
export type BrowserDiagnosticsOptions = {
|
|
716
|
+
kinds?: BrowserDiagnosticKind[] | undefined;
|
|
717
|
+
after?: number | undefined;
|
|
718
|
+
limit?: number | undefined;
|
|
719
|
+
signal?: AbortSignal | undefined;
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
/** The exact HTTP surface needed by the framework-free interaction client. */
|
|
723
|
+
export interface InteractionTransport {
|
|
724
|
+
listAttachedBrowsers(
|
|
725
|
+
workspaceId: string,
|
|
726
|
+
options?: AttachedBrowserDeviceListOptions,
|
|
727
|
+
): Promise<AttachedBrowserDeviceListResponse>;
|
|
728
|
+
getAttachedBrowser(
|
|
729
|
+
workspaceId: string,
|
|
730
|
+
deviceId: string,
|
|
731
|
+
options?: OpenGeniRequestOptions,
|
|
732
|
+
): Promise<AttachedBrowserDevice>;
|
|
733
|
+
listBrowserIdentities(
|
|
734
|
+
workspaceId: string,
|
|
735
|
+
options?: BrowserIdentityListOptions,
|
|
736
|
+
): Promise<BrowserIdentityListResponse>;
|
|
737
|
+
getBrowserIdentity(
|
|
738
|
+
workspaceId: string,
|
|
739
|
+
identityId: string,
|
|
740
|
+
options?: OpenGeniRequestOptions,
|
|
741
|
+
): Promise<BrowserIdentity>;
|
|
742
|
+
createBrowserIdentity(
|
|
743
|
+
workspaceId: string,
|
|
744
|
+
request: CreateBrowserIdentityRequest,
|
|
745
|
+
options?: OpenGeniRequestOptions,
|
|
746
|
+
): Promise<BrowserIdentityMutationResponse>;
|
|
747
|
+
listBrowserRevisions(
|
|
748
|
+
workspaceId: string,
|
|
749
|
+
identityId: string,
|
|
750
|
+
options?: OpenGeniRequestOptions,
|
|
751
|
+
): Promise<BrowserRevisionListResponse>;
|
|
752
|
+
listBrowserSessions(
|
|
753
|
+
workspaceId: string,
|
|
754
|
+
options?: OpenGeniRequestOptions,
|
|
755
|
+
): Promise<BrowserSessionListResponse>;
|
|
756
|
+
getBrowserSession(
|
|
757
|
+
workspaceId: string,
|
|
758
|
+
browserSessionId: string,
|
|
759
|
+
options?: OpenGeniRequestOptions,
|
|
760
|
+
): Promise<BrowserSession>;
|
|
761
|
+
createBrowserSession(
|
|
762
|
+
workspaceId: string,
|
|
763
|
+
request: CreateBrowserSessionRequest,
|
|
764
|
+
options?: OpenGeniRequestOptions,
|
|
765
|
+
): Promise<BrowserSessionMutationResponse>;
|
|
766
|
+
listBrowserTargets(
|
|
767
|
+
workspaceId: string,
|
|
768
|
+
browserSessionId: string,
|
|
769
|
+
options?: OpenGeniRequestOptions,
|
|
770
|
+
): Promise<BrowserTargetListResponse>;
|
|
771
|
+
openBrowserTarget(
|
|
772
|
+
workspaceId: string,
|
|
773
|
+
browserSessionId: string,
|
|
774
|
+
request?: BrowserOpenTargetRequest,
|
|
775
|
+
options?: OpenGeniRequestOptions,
|
|
776
|
+
): Promise<BrowserTarget>;
|
|
777
|
+
selectBrowserTarget(
|
|
778
|
+
workspaceId: string,
|
|
779
|
+
browserSessionId: string,
|
|
780
|
+
targetId: string,
|
|
781
|
+
options?: OpenGeniRequestOptions,
|
|
782
|
+
): Promise<BrowserTarget>;
|
|
783
|
+
closeBrowserTarget(
|
|
784
|
+
workspaceId: string,
|
|
785
|
+
browserSessionId: string,
|
|
786
|
+
targetId: string,
|
|
787
|
+
options?: OpenGeniRequestOptions,
|
|
788
|
+
): Promise<BrowserTargetListResponse>;
|
|
789
|
+
observeBrowserTarget(
|
|
790
|
+
workspaceId: string,
|
|
791
|
+
browserSessionId: string,
|
|
792
|
+
targetId: string,
|
|
793
|
+
options?: OpenGeniRequestOptions,
|
|
794
|
+
): Promise<BrowserObservation>;
|
|
795
|
+
actInBrowser(
|
|
796
|
+
workspaceId: string,
|
|
797
|
+
browserSessionId: string,
|
|
798
|
+
request: BrowserActionRequest,
|
|
799
|
+
options?: OpenGeniRequestOptions,
|
|
800
|
+
): Promise<BrowserActionReceipt>;
|
|
801
|
+
getBrowserActionReceipt(
|
|
802
|
+
workspaceId: string,
|
|
803
|
+
browserSessionId: string,
|
|
804
|
+
operationId: string,
|
|
805
|
+
options?: OpenGeniRequestOptions,
|
|
806
|
+
): Promise<BrowserActionReceipt>;
|
|
807
|
+
listBrowserDiagnostics(
|
|
808
|
+
workspaceId: string,
|
|
809
|
+
browserSessionId: string,
|
|
810
|
+
targetId: string,
|
|
811
|
+
options?: BrowserDiagnosticsOptions,
|
|
812
|
+
): Promise<BrowserDiagnosticBatch>;
|
|
813
|
+
attachBrowserSession(
|
|
814
|
+
workspaceId: string,
|
|
815
|
+
browserSessionId: string,
|
|
816
|
+
request: BrowserSessionAttachmentRequest,
|
|
817
|
+
options?: OpenGeniRequestOptions,
|
|
818
|
+
): Promise<BrowserSessionAttachment>;
|
|
819
|
+
heartbeatBrowserSession(
|
|
820
|
+
workspaceId: string,
|
|
821
|
+
browserSessionId: string,
|
|
822
|
+
options?: OpenGeniRequestOptions,
|
|
823
|
+
): Promise<BrowserSessionHeartbeatResponse>;
|
|
824
|
+
publishBrowserRevision(
|
|
825
|
+
workspaceId: string,
|
|
826
|
+
browserSessionId: string,
|
|
827
|
+
request: PublishBrowserRevisionRequest,
|
|
828
|
+
options?: OpenGeniRequestOptions,
|
|
829
|
+
): Promise<PublishBrowserRevisionResponse>;
|
|
830
|
+
suspendBrowserSession(
|
|
831
|
+
workspaceId: string,
|
|
832
|
+
browserSessionId: string,
|
|
833
|
+
request: BrowserSessionLifecycleRequest,
|
|
834
|
+
options?: OpenGeniRequestOptions,
|
|
835
|
+
): Promise<BrowserSessionMutationResponse>;
|
|
836
|
+
resumeBrowserSession(
|
|
837
|
+
workspaceId: string,
|
|
838
|
+
browserSessionId: string,
|
|
839
|
+
request: BrowserSessionLifecycleRequest,
|
|
840
|
+
options?: OpenGeniRequestOptions,
|
|
841
|
+
): Promise<BrowserSessionMutationResponse>;
|
|
842
|
+
endBrowserSession(
|
|
843
|
+
workspaceId: string,
|
|
844
|
+
browserSessionId: string,
|
|
845
|
+
request: BrowserSessionLifecycleRequest,
|
|
846
|
+
options?: OpenGeniRequestOptions,
|
|
847
|
+
): Promise<BrowserSessionMutationResponse>;
|
|
848
|
+
listComputerSessions(
|
|
849
|
+
workspaceId: string,
|
|
850
|
+
options?: OpenGeniRequestOptions,
|
|
851
|
+
): Promise<ComputerSessionListResponse>;
|
|
852
|
+
getComputerSession(
|
|
853
|
+
workspaceId: string,
|
|
854
|
+
computerSessionId: string,
|
|
855
|
+
options?: OpenGeniRequestOptions,
|
|
856
|
+
): Promise<ComputerSession>;
|
|
857
|
+
createComputerSession(
|
|
858
|
+
workspaceId: string,
|
|
859
|
+
request: CreateComputerSessionRequest,
|
|
860
|
+
options?: OpenGeniRequestOptions,
|
|
861
|
+
): Promise<ComputerSessionMutationResponse>;
|
|
862
|
+
listComputerTargets(
|
|
863
|
+
workspaceId: string,
|
|
864
|
+
computerSessionId: string,
|
|
865
|
+
options?: OpenGeniRequestOptions,
|
|
866
|
+
): Promise<ComputerTargetListResponse>;
|
|
867
|
+
observeComputerTarget(
|
|
868
|
+
workspaceId: string,
|
|
869
|
+
computerSessionId: string,
|
|
870
|
+
targetId: string,
|
|
871
|
+
options?: OpenGeniRequestOptions,
|
|
872
|
+
): Promise<ComputerObservation>;
|
|
873
|
+
actInComputer(
|
|
874
|
+
workspaceId: string,
|
|
875
|
+
computerSessionId: string,
|
|
876
|
+
request: ComputerActionRequest,
|
|
877
|
+
options?: OpenGeniRequestOptions,
|
|
878
|
+
): Promise<ComputerActionReceipt>;
|
|
879
|
+
getComputerActionReceipt(
|
|
880
|
+
workspaceId: string,
|
|
881
|
+
computerSessionId: string,
|
|
882
|
+
operationId: string,
|
|
883
|
+
options?: OpenGeniRequestOptions,
|
|
884
|
+
): Promise<ComputerActionReceipt>;
|
|
885
|
+
attachComputerSession(
|
|
886
|
+
workspaceId: string,
|
|
887
|
+
computerSessionId: string,
|
|
888
|
+
request: ComputerSessionAttachmentRequest,
|
|
889
|
+
options?: OpenGeniRequestOptions,
|
|
890
|
+
): Promise<ComputerSessionAttachment>;
|
|
891
|
+
heartbeatComputerSession(
|
|
892
|
+
workspaceId: string,
|
|
893
|
+
computerSessionId: string,
|
|
894
|
+
options?: OpenGeniRequestOptions,
|
|
895
|
+
): Promise<ComputerSessionHeartbeatResponse>;
|
|
896
|
+
endComputerSession(
|
|
897
|
+
workspaceId: string,
|
|
898
|
+
computerSessionId: string,
|
|
899
|
+
request: ComputerSessionLifecycleRequest,
|
|
900
|
+
options?: OpenGeniRequestOptions,
|
|
901
|
+
): Promise<ComputerSessionMutationResponse>;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/** @deprecated Use InteractionTransport. */
|
|
905
|
+
export type BrowserInteractionTransport = InteractionTransport;
|
|
906
|
+
|
|
907
|
+
export type BrowserIdentityListOptions = OpenGeniRequestOptions & {
|
|
908
|
+
includeArchived?: boolean | undefined;
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
export type AttachedBrowserDeviceListOptions = OpenGeniRequestOptions & {
|
|
912
|
+
includeDisconnected?: boolean | undefined;
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
export type CurrentOrOpenBrowserOptions = {
|
|
916
|
+
workspaceId: string;
|
|
917
|
+
associationSessionId: string;
|
|
918
|
+
operationId?: string | undefined;
|
|
919
|
+
name?: string | undefined;
|
|
920
|
+
initialUrl?: string | undefined;
|
|
921
|
+
headless?: boolean | undefined;
|
|
922
|
+
placement?: InteractionPlacement | undefined;
|
|
923
|
+
identityId?: string | undefined;
|
|
924
|
+
baseRevisionId?: string | undefined;
|
|
925
|
+
linkedComputerSessionId?: string | undefined;
|
|
926
|
+
signal?: AbortSignal | undefined;
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
export type CurrentOrOpenComputerOptions = {
|
|
930
|
+
workspaceId: string;
|
|
931
|
+
associationSessionId: string;
|
|
932
|
+
operationId?: string | undefined;
|
|
933
|
+
name?: string | undefined;
|
|
934
|
+
placement?: InteractionPlacement | undefined;
|
|
935
|
+
signal?: AbortSignal | undefined;
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
/** Framework-free resource facade over the public interaction HTTP API. */
|
|
939
|
+
export class OpenGeniInteractionClient {
|
|
940
|
+
readonly attachedBrowsers: AttachedBrowserDeviceCollection;
|
|
941
|
+
readonly browsers: BrowserSessionCollection;
|
|
942
|
+
readonly computers: ComputerSessionCollection;
|
|
943
|
+
readonly identities: BrowserIdentityCollection;
|
|
944
|
+
|
|
945
|
+
constructor(transport: BrowserInteractionTransport) {
|
|
946
|
+
this.attachedBrowsers = new AttachedBrowserDeviceCollection(transport);
|
|
947
|
+
this.browsers = new BrowserSessionCollection(transport);
|
|
948
|
+
this.computers = new ComputerSessionCollection(transport);
|
|
949
|
+
this.identities = new BrowserIdentityCollection(transport);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export class AttachedBrowserDeviceCollection {
|
|
954
|
+
constructor(private readonly transport: BrowserInteractionTransport) {}
|
|
955
|
+
|
|
956
|
+
async list(
|
|
957
|
+
workspaceId: string,
|
|
958
|
+
options: AttachedBrowserDeviceListOptions = {},
|
|
959
|
+
): Promise<AttachedBrowserDeviceListResponse> {
|
|
960
|
+
return await this.transport.listAttachedBrowsers(workspaceId, options);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
device(workspaceId: string, deviceId: string): AttachedBrowserDeviceResource {
|
|
964
|
+
return new AttachedBrowserDeviceResource(this.transport, workspaceId, deviceId);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export class AttachedBrowserDeviceResource {
|
|
969
|
+
constructor(
|
|
970
|
+
private readonly transport: BrowserInteractionTransport,
|
|
971
|
+
readonly workspaceId: string,
|
|
972
|
+
readonly id: string,
|
|
973
|
+
) {}
|
|
974
|
+
|
|
975
|
+
async get(options: OpenGeniRequestOptions = {}): Promise<AttachedBrowserDevice> {
|
|
976
|
+
return await this.transport.getAttachedBrowser(this.workspaceId, this.id, options);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
export class BrowserIdentityCollection {
|
|
981
|
+
constructor(private readonly transport: BrowserInteractionTransport) {}
|
|
982
|
+
|
|
983
|
+
async list(
|
|
984
|
+
workspaceId: string,
|
|
985
|
+
options: BrowserIdentityListOptions = {},
|
|
986
|
+
): Promise<BrowserIdentityListResponse> {
|
|
987
|
+
return await this.transport.listBrowserIdentities(workspaceId, options);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
identity(workspaceId: string, identityId: string): BrowserIdentityResource {
|
|
991
|
+
return new BrowserIdentityResource(this.transport, workspaceId, identityId);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
async create(
|
|
995
|
+
workspaceId: string,
|
|
996
|
+
request: CreateBrowserIdentityRequest,
|
|
997
|
+
options: OpenGeniRequestOptions = {},
|
|
998
|
+
): Promise<BrowserIdentityResource> {
|
|
999
|
+
const response = await this.transport.createBrowserIdentity(workspaceId, request, options);
|
|
1000
|
+
return this.identity(workspaceId, response.identity.id);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
export class BrowserIdentityResource {
|
|
1005
|
+
constructor(
|
|
1006
|
+
private readonly transport: BrowserInteractionTransport,
|
|
1007
|
+
readonly workspaceId: string,
|
|
1008
|
+
readonly id: string,
|
|
1009
|
+
) {}
|
|
1010
|
+
|
|
1011
|
+
async get(options: OpenGeniRequestOptions = {}): Promise<BrowserIdentity> {
|
|
1012
|
+
return await this.transport.getBrowserIdentity(this.workspaceId, this.id, options);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
async revisions(options: OpenGeniRequestOptions = {}): Promise<BrowserRevisionListResponse> {
|
|
1016
|
+
return await this.transport.listBrowserRevisions(this.workspaceId, this.id, options);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
export class BrowserSessionCollection {
|
|
1021
|
+
constructor(private readonly transport: BrowserInteractionTransport) {}
|
|
1022
|
+
|
|
1023
|
+
async list(
|
|
1024
|
+
workspaceId: string,
|
|
1025
|
+
options: OpenGeniRequestOptions = {},
|
|
1026
|
+
): Promise<BrowserSessionListResponse> {
|
|
1027
|
+
return await this.transport.listBrowserSessions(workspaceId, options);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
session(workspaceId: string, browserSessionId: string): BrowserSessionResource {
|
|
1031
|
+
return new BrowserSessionResource(this.transport, workspaceId, browserSessionId);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
async open(
|
|
1035
|
+
workspaceId: string,
|
|
1036
|
+
request: CreateBrowserSessionRequest,
|
|
1037
|
+
options: OpenGeniRequestOptions = {},
|
|
1038
|
+
): Promise<BrowserSessionResource> {
|
|
1039
|
+
const response = await this.transport.createBrowserSession(workspaceId, request, options);
|
|
1040
|
+
return this.session(workspaceId, response.session.id);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
async currentOrOpen(options: CurrentOrOpenBrowserOptions): Promise<BrowserSessionResource> {
|
|
1044
|
+
const requestOptions = options.signal ? { signal: options.signal } : {};
|
|
1045
|
+
const listed = await this.list(options.workspaceId, requestOptions);
|
|
1046
|
+
const current = newestRelevantBrowser(listed.sessions, options.associationSessionId);
|
|
1047
|
+
if (current) {
|
|
1048
|
+
const resource = this.session(options.workspaceId, current.id);
|
|
1049
|
+
if (current.lifecycle === "suspended") {
|
|
1050
|
+
await resource.resume(
|
|
1051
|
+
{ operationId: options.operationId ?? crypto.randomUUID() },
|
|
1052
|
+
requestOptions,
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
return resource;
|
|
1056
|
+
}
|
|
1057
|
+
return await this.open(
|
|
1058
|
+
options.workspaceId,
|
|
1059
|
+
{
|
|
1060
|
+
operationId: options.operationId ?? crypto.randomUUID(),
|
|
1061
|
+
sessionId: options.associationSessionId,
|
|
1062
|
+
...(options.name !== undefined ? { name: options.name } : {}),
|
|
1063
|
+
...(options.initialUrl !== undefined ? { initialUrl: options.initialUrl } : {}),
|
|
1064
|
+
...(options.headless !== undefined ? { headless: options.headless } : {}),
|
|
1065
|
+
...(options.placement !== undefined ? { placement: options.placement } : {}),
|
|
1066
|
+
...(options.identityId !== undefined ? { identityId: options.identityId } : {}),
|
|
1067
|
+
...(options.baseRevisionId !== undefined ? { baseRevisionId: options.baseRevisionId } : {}),
|
|
1068
|
+
...(options.linkedComputerSessionId !== undefined
|
|
1069
|
+
? { linkedComputerSessionId: options.linkedComputerSessionId }
|
|
1070
|
+
: {}),
|
|
1071
|
+
},
|
|
1072
|
+
requestOptions,
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
export class BrowserSessionResource {
|
|
1078
|
+
readonly tabs: BrowserTargetCollection;
|
|
1079
|
+
/** Alias for hosts that prefer the protocol term. */
|
|
1080
|
+
readonly targets: BrowserTargetCollection;
|
|
1081
|
+
|
|
1082
|
+
constructor(
|
|
1083
|
+
private readonly transport: BrowserInteractionTransport,
|
|
1084
|
+
readonly workspaceId: string,
|
|
1085
|
+
readonly id: string,
|
|
1086
|
+
) {
|
|
1087
|
+
this.tabs = new BrowserTargetCollection(transport, workspaceId, id);
|
|
1088
|
+
this.targets = this.tabs;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
async get(options: OpenGeniRequestOptions = {}): Promise<BrowserSession> {
|
|
1092
|
+
return await this.transport.getBrowserSession(this.workspaceId, this.id, options);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
async observe(
|
|
1096
|
+
targetId: string,
|
|
1097
|
+
options: OpenGeniRequestOptions = {},
|
|
1098
|
+
): Promise<BrowserObservation> {
|
|
1099
|
+
return await this.transport.observeBrowserTarget(this.workspaceId, this.id, targetId, options);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
async act(
|
|
1103
|
+
request: BrowserActionRequest,
|
|
1104
|
+
options: OpenGeniRequestOptions = {},
|
|
1105
|
+
): Promise<BrowserActionReceipt> {
|
|
1106
|
+
return await this.transport.actInBrowser(this.workspaceId, this.id, request, options);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
async receipt(
|
|
1110
|
+
operationId: string,
|
|
1111
|
+
options: OpenGeniRequestOptions = {},
|
|
1112
|
+
): Promise<BrowserActionReceipt> {
|
|
1113
|
+
return await this.transport.getBrowserActionReceipt(
|
|
1114
|
+
this.workspaceId,
|
|
1115
|
+
this.id,
|
|
1116
|
+
operationId,
|
|
1117
|
+
options,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
async diagnostics(
|
|
1122
|
+
targetId: string,
|
|
1123
|
+
options: BrowserDiagnosticsOptions = {},
|
|
1124
|
+
): Promise<BrowserDiagnosticBatch> {
|
|
1125
|
+
return await this.transport.listBrowserDiagnostics(
|
|
1126
|
+
this.workspaceId,
|
|
1127
|
+
this.id,
|
|
1128
|
+
targetId,
|
|
1129
|
+
options,
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
async attach(
|
|
1134
|
+
request: BrowserSessionAttachmentRequest,
|
|
1135
|
+
options: OpenGeniRequestOptions = {},
|
|
1136
|
+
): Promise<BrowserSessionAttachment> {
|
|
1137
|
+
return await this.transport.attachBrowserSession(this.workspaceId, this.id, request, options);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
async heartbeat(options: OpenGeniRequestOptions = {}): Promise<BrowserSessionHeartbeatResponse> {
|
|
1141
|
+
return await this.transport.heartbeatBrowserSession(this.workspaceId, this.id, options);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
async publishRevision(
|
|
1145
|
+
request: PublishBrowserRevisionRequest,
|
|
1146
|
+
options: OpenGeniRequestOptions = {},
|
|
1147
|
+
): Promise<PublishBrowserRevisionResponse> {
|
|
1148
|
+
return await this.transport.publishBrowserRevision(this.workspaceId, this.id, request, options);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
async suspend(
|
|
1152
|
+
request: BrowserSessionLifecycleRequest = {
|
|
1153
|
+
operationId: crypto.randomUUID(),
|
|
1154
|
+
},
|
|
1155
|
+
options: OpenGeniRequestOptions = {},
|
|
1156
|
+
): Promise<BrowserSessionMutationResponse> {
|
|
1157
|
+
return await this.transport.suspendBrowserSession(this.workspaceId, this.id, request, options);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
async resume(
|
|
1161
|
+
request: BrowserSessionLifecycleRequest = {
|
|
1162
|
+
operationId: crypto.randomUUID(),
|
|
1163
|
+
},
|
|
1164
|
+
options: OpenGeniRequestOptions = {},
|
|
1165
|
+
): Promise<BrowserSessionMutationResponse> {
|
|
1166
|
+
return await this.transport.resumeBrowserSession(this.workspaceId, this.id, request, options);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
async end(
|
|
1170
|
+
request: BrowserSessionLifecycleRequest = {
|
|
1171
|
+
operationId: crypto.randomUUID(),
|
|
1172
|
+
},
|
|
1173
|
+
options: OpenGeniRequestOptions = {},
|
|
1174
|
+
): Promise<BrowserSessionMutationResponse> {
|
|
1175
|
+
return await this.transport.endBrowserSession(this.workspaceId, this.id, request, options);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
export class BrowserTargetCollection {
|
|
1180
|
+
constructor(
|
|
1181
|
+
private readonly transport: BrowserInteractionTransport,
|
|
1182
|
+
private readonly workspaceId: string,
|
|
1183
|
+
private readonly browserSessionId: string,
|
|
1184
|
+
) {}
|
|
1185
|
+
|
|
1186
|
+
async list(options: OpenGeniRequestOptions = {}): Promise<BrowserTargetListResponse> {
|
|
1187
|
+
return await this.transport.listBrowserTargets(
|
|
1188
|
+
this.workspaceId,
|
|
1189
|
+
this.browserSessionId,
|
|
1190
|
+
options,
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
async open(url?: string, options: OpenGeniRequestOptions = {}): Promise<BrowserTarget> {
|
|
1195
|
+
return await this.transport.openBrowserTarget(
|
|
1196
|
+
this.workspaceId,
|
|
1197
|
+
this.browserSessionId,
|
|
1198
|
+
url === undefined ? {} : { url },
|
|
1199
|
+
options,
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
async select(targetId: string, options: OpenGeniRequestOptions = {}): Promise<BrowserTarget> {
|
|
1204
|
+
return await this.transport.selectBrowserTarget(
|
|
1205
|
+
this.workspaceId,
|
|
1206
|
+
this.browserSessionId,
|
|
1207
|
+
targetId,
|
|
1208
|
+
options,
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
async close(
|
|
1213
|
+
targetId: string,
|
|
1214
|
+
options: OpenGeniRequestOptions = {},
|
|
1215
|
+
): Promise<BrowserTargetListResponse> {
|
|
1216
|
+
return await this.transport.closeBrowserTarget(
|
|
1217
|
+
this.workspaceId,
|
|
1218
|
+
this.browserSessionId,
|
|
1219
|
+
targetId,
|
|
1220
|
+
options,
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
export class ComputerSessionCollection {
|
|
1226
|
+
constructor(private readonly transport: BrowserInteractionTransport) {}
|
|
1227
|
+
|
|
1228
|
+
async list(
|
|
1229
|
+
workspaceId: string,
|
|
1230
|
+
options: OpenGeniRequestOptions = {},
|
|
1231
|
+
): Promise<ComputerSessionListResponse> {
|
|
1232
|
+
return await this.transport.listComputerSessions(workspaceId, options);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
session(workspaceId: string, computerSessionId: string): ComputerSessionResource {
|
|
1236
|
+
return new ComputerSessionResource(this.transport, workspaceId, computerSessionId);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
async open(
|
|
1240
|
+
workspaceId: string,
|
|
1241
|
+
request: CreateComputerSessionRequest,
|
|
1242
|
+
options: OpenGeniRequestOptions = {},
|
|
1243
|
+
): Promise<ComputerSessionResource> {
|
|
1244
|
+
const response = await this.transport.createComputerSession(workspaceId, request, options);
|
|
1245
|
+
return this.session(workspaceId, response.session.id);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
async currentOrOpen(options: CurrentOrOpenComputerOptions): Promise<ComputerSessionResource> {
|
|
1249
|
+
const requestOptions = options.signal ? { signal: options.signal } : {};
|
|
1250
|
+
const listed = await this.list(options.workspaceId, requestOptions);
|
|
1251
|
+
const current = newestRelevantComputer(listed.sessions, options.associationSessionId);
|
|
1252
|
+
if (current) {
|
|
1253
|
+
return this.session(options.workspaceId, current.id);
|
|
1254
|
+
}
|
|
1255
|
+
return await this.open(
|
|
1256
|
+
options.workspaceId,
|
|
1257
|
+
{
|
|
1258
|
+
operationId: options.operationId ?? crypto.randomUUID(),
|
|
1259
|
+
sessionId: options.associationSessionId,
|
|
1260
|
+
...(options.name !== undefined ? { name: options.name } : {}),
|
|
1261
|
+
...(options.placement !== undefined ? { placement: options.placement } : {}),
|
|
1262
|
+
},
|
|
1263
|
+
requestOptions,
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export class ComputerSessionResource {
|
|
1269
|
+
readonly targets: ComputerTargetCollection;
|
|
1270
|
+
/** Native application/window targets are also the public app collection. */
|
|
1271
|
+
readonly apps: ComputerTargetCollection;
|
|
1272
|
+
|
|
1273
|
+
constructor(
|
|
1274
|
+
private readonly transport: BrowserInteractionTransport,
|
|
1275
|
+
readonly workspaceId: string,
|
|
1276
|
+
readonly id: string,
|
|
1277
|
+
) {
|
|
1278
|
+
this.targets = new ComputerTargetCollection(transport, workspaceId, id);
|
|
1279
|
+
this.apps = this.targets;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
async get(options: OpenGeniRequestOptions = {}): Promise<ComputerSession> {
|
|
1283
|
+
return await this.transport.getComputerSession(this.workspaceId, this.id, options);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
async observe(
|
|
1287
|
+
targetId: string,
|
|
1288
|
+
options: OpenGeniRequestOptions = {},
|
|
1289
|
+
): Promise<ComputerObservation> {
|
|
1290
|
+
return await this.transport.observeComputerTarget(this.workspaceId, this.id, targetId, options);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
async act(
|
|
1294
|
+
request: ComputerActionRequest,
|
|
1295
|
+
options: OpenGeniRequestOptions = {},
|
|
1296
|
+
): Promise<ComputerActionReceipt> {
|
|
1297
|
+
return await this.transport.actInComputer(this.workspaceId, this.id, request, options);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
async receipt(
|
|
1301
|
+
operationId: string,
|
|
1302
|
+
options: OpenGeniRequestOptions = {},
|
|
1303
|
+
): Promise<ComputerActionReceipt> {
|
|
1304
|
+
return await this.transport.getComputerActionReceipt(
|
|
1305
|
+
this.workspaceId,
|
|
1306
|
+
this.id,
|
|
1307
|
+
operationId,
|
|
1308
|
+
options,
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
async attach(
|
|
1313
|
+
request: ComputerSessionAttachmentRequest,
|
|
1314
|
+
options: OpenGeniRequestOptions = {},
|
|
1315
|
+
): Promise<ComputerSessionAttachment> {
|
|
1316
|
+
return await this.transport.attachComputerSession(this.workspaceId, this.id, request, options);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
async heartbeat(options: OpenGeniRequestOptions = {}): Promise<ComputerSessionHeartbeatResponse> {
|
|
1320
|
+
return await this.transport.heartbeatComputerSession(this.workspaceId, this.id, options);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
async end(
|
|
1324
|
+
request: ComputerSessionLifecycleRequest = { operationId: crypto.randomUUID() },
|
|
1325
|
+
options: OpenGeniRequestOptions = {},
|
|
1326
|
+
): Promise<ComputerSessionMutationResponse> {
|
|
1327
|
+
return await this.transport.endComputerSession(this.workspaceId, this.id, request, options);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
export class ComputerTargetCollection {
|
|
1332
|
+
constructor(
|
|
1333
|
+
private readonly transport: BrowserInteractionTransport,
|
|
1334
|
+
private readonly workspaceId: string,
|
|
1335
|
+
private readonly computerSessionId: string,
|
|
1336
|
+
) {}
|
|
1337
|
+
|
|
1338
|
+
async list(options: OpenGeniRequestOptions = {}): Promise<ComputerTargetListResponse> {
|
|
1339
|
+
return await this.transport.listComputerTargets(
|
|
1340
|
+
this.workspaceId,
|
|
1341
|
+
this.computerSessionId,
|
|
1342
|
+
options,
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
export type BrowserFrameMetadata = {
|
|
1348
|
+
frameId: string;
|
|
1349
|
+
browserSessionId: string;
|
|
1350
|
+
controllerGeneration: string;
|
|
1351
|
+
targetId: string;
|
|
1352
|
+
targetGeneration: string;
|
|
1353
|
+
documentGeneration: string;
|
|
1354
|
+
sequence: number;
|
|
1355
|
+
mediaType: "image/jpeg" | "image/png";
|
|
1356
|
+
width: number;
|
|
1357
|
+
height: number;
|
|
1358
|
+
deviceScaleFactor: number;
|
|
1359
|
+
scrollX: number;
|
|
1360
|
+
scrollY: number;
|
|
1361
|
+
capturedAt: string;
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
export type BrowserFrame = BrowserFrameMetadata & { data: Uint8Array };
|
|
1365
|
+
|
|
1366
|
+
export type ComputerFrameMetadata = {
|
|
1367
|
+
frameId: string;
|
|
1368
|
+
computerSessionId: string;
|
|
1369
|
+
controllerGeneration: string;
|
|
1370
|
+
targetId: string;
|
|
1371
|
+
targetGeneration: string;
|
|
1372
|
+
sequence: number;
|
|
1373
|
+
mediaType: "image/jpeg" | "image/png";
|
|
1374
|
+
width: number;
|
|
1375
|
+
height: number;
|
|
1376
|
+
capturedAt: string;
|
|
1377
|
+
sha256: string;
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
export type ComputerFrame = ComputerFrameMetadata & { data: Uint8Array };
|
|
1381
|
+
|
|
1382
|
+
export type BrowserFrameStreamOptions = InteractionFrameStreamOptions;
|
|
1383
|
+
|
|
1384
|
+
/** Convert a short-lived HTTP attachment URL into its browser WebSocket URL. */
|
|
1385
|
+
export function browserFrameSocketUrl(
|
|
1386
|
+
attachment: BrowserSessionAttachment,
|
|
1387
|
+
options: BrowserFrameStreamOptions = {},
|
|
1388
|
+
): string {
|
|
1389
|
+
if (attachment.stream.kind !== "direct_websocket") {
|
|
1390
|
+
throw new Error("relay browser attachments are opened through the relay protocol");
|
|
1391
|
+
}
|
|
1392
|
+
const url = new URL(attachment.stream.url);
|
|
1393
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
1394
|
+
else if (url.protocol === "https:") url.protocol = "wss:";
|
|
1395
|
+
else if (url.protocol !== "ws:" && url.protocol !== "wss:") {
|
|
1396
|
+
throw new Error("browser frame attachment did not contain an HTTP or WebSocket URL");
|
|
1397
|
+
}
|
|
1398
|
+
if (options.format !== undefined) url.searchParams.set("format", options.format);
|
|
1399
|
+
setBoundedIntegerQuery(url, "quality", options.quality, 1, 100);
|
|
1400
|
+
setBoundedIntegerQuery(url, "maxWidth", options.maxWidth, 1, 4_096);
|
|
1401
|
+
setBoundedIntegerQuery(url, "maxHeight", options.maxHeight, 1, 4_096);
|
|
1402
|
+
setBoundedIntegerQuery(url, "everyNthFrame", options.everyNthFrame, 1, 60);
|
|
1403
|
+
return url.toString();
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/** Convert a direct ComputerSession attachment into its WebSocket URL. */
|
|
1407
|
+
export function computerFrameSocketUrl(
|
|
1408
|
+
attachment: ComputerSessionAttachment,
|
|
1409
|
+
options: ComputerFrameStreamOptions = {},
|
|
1410
|
+
): string {
|
|
1411
|
+
if (attachment.stream.kind !== "direct_websocket") {
|
|
1412
|
+
throw new Error("relay computer attachments are opened through the relay protocol");
|
|
1413
|
+
}
|
|
1414
|
+
const url = new URL(attachment.stream.url);
|
|
1415
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
1416
|
+
else if (url.protocol === "https:") url.protocol = "wss:";
|
|
1417
|
+
else if (url.protocol !== "ws:" && url.protocol !== "wss:") {
|
|
1418
|
+
throw new Error("computer frame attachment did not contain an HTTP or WebSocket URL");
|
|
1419
|
+
}
|
|
1420
|
+
if (options.format !== undefined) url.searchParams.set("format", options.format);
|
|
1421
|
+
setBoundedIntegerQuery(url, "quality", options.quality, 1, 100);
|
|
1422
|
+
setBoundedIntegerQuery(url, "maxWidth", options.maxWidth, 1, 4_096);
|
|
1423
|
+
setBoundedIntegerQuery(url, "maxHeight", options.maxHeight, 1, 4_096);
|
|
1424
|
+
setBoundedIntegerQuery(url, "everyNthFrame", options.everyNthFrame, 1, 60);
|
|
1425
|
+
return url.toString();
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/** Decode one bounded binary frame message from the placement controller. */
|
|
1429
|
+
export function decodeBrowserFrameMessage(message: ArrayBuffer | Uint8Array): BrowserFrame {
|
|
1430
|
+
const bytes = message instanceof Uint8Array ? message : new Uint8Array(message);
|
|
1431
|
+
if (
|
|
1432
|
+
bytes.byteLength < 5 ||
|
|
1433
|
+
bytes.byteLength > 4 + BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES + BROWSER_FRAME_MAX_BYTES
|
|
1434
|
+
) {
|
|
1435
|
+
throw new Error("browser frame message is truncated or too large");
|
|
1436
|
+
}
|
|
1437
|
+
const metadataLength = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(
|
|
1438
|
+
0,
|
|
1439
|
+
false,
|
|
1440
|
+
);
|
|
1441
|
+
if (
|
|
1442
|
+
metadataLength < 1 ||
|
|
1443
|
+
metadataLength > BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES ||
|
|
1444
|
+
metadataLength + 4 >= bytes.byteLength
|
|
1445
|
+
) {
|
|
1446
|
+
throw new Error("browser frame metadata length is invalid");
|
|
1447
|
+
}
|
|
1448
|
+
let value: unknown;
|
|
1449
|
+
try {
|
|
1450
|
+
value = JSON.parse(
|
|
1451
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(4, 4 + metadataLength)),
|
|
1452
|
+
);
|
|
1453
|
+
} catch {
|
|
1454
|
+
throw new Error("browser frame metadata is invalid JSON");
|
|
1455
|
+
}
|
|
1456
|
+
const metadata = parseBrowserFrameMetadata(value);
|
|
1457
|
+
const data = bytes.slice(4 + metadataLength);
|
|
1458
|
+
if (data.byteLength < 1 || data.byteLength > BROWSER_FRAME_MAX_BYTES) {
|
|
1459
|
+
throw new Error("browser frame image is empty or too large");
|
|
1460
|
+
}
|
|
1461
|
+
const dimensions = imageDimensions(data, metadata.mediaType);
|
|
1462
|
+
if (dimensions.width !== metadata.width || dimensions.height !== metadata.height) {
|
|
1463
|
+
throw new Error("browser frame image dimensions do not match metadata");
|
|
1464
|
+
}
|
|
1465
|
+
return { ...metadata, data };
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/** Decode and authenticate one bounded ComputerSession frame. Computer frames
|
|
1469
|
+
* carry a content digest because native capture adapters may cross process and
|
|
1470
|
+
* relay boundaries before reaching the SDK. */
|
|
1471
|
+
export async function decodeComputerFrameMessage(
|
|
1472
|
+
message: ArrayBuffer | Uint8Array,
|
|
1473
|
+
): Promise<ComputerFrame> {
|
|
1474
|
+
const bytes = message instanceof Uint8Array ? message : new Uint8Array(message);
|
|
1475
|
+
if (
|
|
1476
|
+
bytes.byteLength < 5 ||
|
|
1477
|
+
bytes.byteLength > 4 + BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES + BROWSER_FRAME_MAX_BYTES
|
|
1478
|
+
) {
|
|
1479
|
+
throw new Error("computer frame message is truncated or too large");
|
|
1480
|
+
}
|
|
1481
|
+
const metadataLength = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(
|
|
1482
|
+
0,
|
|
1483
|
+
false,
|
|
1484
|
+
);
|
|
1485
|
+
if (
|
|
1486
|
+
metadataLength < 1 ||
|
|
1487
|
+
metadataLength > BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES ||
|
|
1488
|
+
metadataLength + 4 >= bytes.byteLength
|
|
1489
|
+
) {
|
|
1490
|
+
throw new Error("computer frame metadata length is invalid");
|
|
1491
|
+
}
|
|
1492
|
+
let value: unknown;
|
|
1493
|
+
try {
|
|
1494
|
+
value = JSON.parse(
|
|
1495
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(4, 4 + metadataLength)),
|
|
1496
|
+
);
|
|
1497
|
+
} catch {
|
|
1498
|
+
throw new Error("computer frame metadata is invalid JSON");
|
|
1499
|
+
}
|
|
1500
|
+
const metadata = parseComputerFrameMetadata(value);
|
|
1501
|
+
const data = bytes.slice(4 + metadataLength);
|
|
1502
|
+
if (data.byteLength < 1 || data.byteLength > BROWSER_FRAME_MAX_BYTES) {
|
|
1503
|
+
throw new Error("computer frame image is empty or too large");
|
|
1504
|
+
}
|
|
1505
|
+
const dimensions = imageDimensions(data, metadata.mediaType);
|
|
1506
|
+
if (dimensions.width !== metadata.width || dimensions.height !== metadata.height) {
|
|
1507
|
+
throw new Error("computer frame image dimensions do not match metadata");
|
|
1508
|
+
}
|
|
1509
|
+
if ((await sha256Hex(data)) !== metadata.sha256) {
|
|
1510
|
+
throw new Error("computer frame digest does not match image");
|
|
1511
|
+
}
|
|
1512
|
+
return { ...metadata, data };
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
export function parseBrowserFrameMetadata(value: unknown): BrowserFrameMetadata {
|
|
1516
|
+
if (!isRecord(value)) throw new Error("browser frame metadata is invalid");
|
|
1517
|
+
const allowed = new Set([
|
|
1518
|
+
"frameId",
|
|
1519
|
+
"browserSessionId",
|
|
1520
|
+
"controllerGeneration",
|
|
1521
|
+
"targetId",
|
|
1522
|
+
"targetGeneration",
|
|
1523
|
+
"documentGeneration",
|
|
1524
|
+
"sequence",
|
|
1525
|
+
"mediaType",
|
|
1526
|
+
"width",
|
|
1527
|
+
"height",
|
|
1528
|
+
"deviceScaleFactor",
|
|
1529
|
+
"scrollX",
|
|
1530
|
+
"scrollY",
|
|
1531
|
+
"capturedAt",
|
|
1532
|
+
]);
|
|
1533
|
+
if (Object.keys(value).some((key) => !allowed.has(key))) {
|
|
1534
|
+
throw new Error("browser frame metadata contains unknown fields");
|
|
1535
|
+
}
|
|
1536
|
+
const strings = [
|
|
1537
|
+
"frameId",
|
|
1538
|
+
"browserSessionId",
|
|
1539
|
+
"controllerGeneration",
|
|
1540
|
+
"targetId",
|
|
1541
|
+
"targetGeneration",
|
|
1542
|
+
"documentGeneration",
|
|
1543
|
+
"mediaType",
|
|
1544
|
+
"capturedAt",
|
|
1545
|
+
] as const;
|
|
1546
|
+
for (const key of strings) {
|
|
1547
|
+
const item = value[key];
|
|
1548
|
+
const max = key === "targetId" ? 512 : key === "capturedAt" ? 128 : 256;
|
|
1549
|
+
if (typeof item !== "string" || item.length < 1 || utf8Bytes(item) > max) {
|
|
1550
|
+
throw new Error("browser frame metadata is invalid");
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const numbers = [
|
|
1554
|
+
"sequence",
|
|
1555
|
+
"width",
|
|
1556
|
+
"height",
|
|
1557
|
+
"deviceScaleFactor",
|
|
1558
|
+
"scrollX",
|
|
1559
|
+
"scrollY",
|
|
1560
|
+
] as const;
|
|
1561
|
+
for (const key of numbers) {
|
|
1562
|
+
if (typeof value[key] !== "number" || !Number.isFinite(value[key])) {
|
|
1563
|
+
throw new Error("browser frame metadata is invalid");
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
const metadata = value as unknown as BrowserFrameMetadata;
|
|
1567
|
+
if (
|
|
1568
|
+
!Number.isSafeInteger(metadata.sequence) ||
|
|
1569
|
+
metadata.sequence < 0 ||
|
|
1570
|
+
!Number.isSafeInteger(metadata.width) ||
|
|
1571
|
+
!Number.isSafeInteger(metadata.height)
|
|
1572
|
+
) {
|
|
1573
|
+
throw new Error("browser frame metadata integers are invalid");
|
|
1574
|
+
}
|
|
1575
|
+
assertImageDimensions(metadata.width, metadata.height);
|
|
1576
|
+
if (
|
|
1577
|
+
metadata.deviceScaleFactor <= 0 ||
|
|
1578
|
+
metadata.deviceScaleFactor > 16 ||
|
|
1579
|
+
Math.abs(metadata.scrollX) > 1_000_000_000 ||
|
|
1580
|
+
Math.abs(metadata.scrollY) > 1_000_000_000
|
|
1581
|
+
) {
|
|
1582
|
+
throw new Error("browser frame geometry metadata is invalid");
|
|
1583
|
+
}
|
|
1584
|
+
if (metadata.mediaType !== "image/jpeg" && metadata.mediaType !== "image/png") {
|
|
1585
|
+
throw new Error("browser frame media type is invalid");
|
|
1586
|
+
}
|
|
1587
|
+
if (!isUuid(metadata.browserSessionId)) throw new Error("browser frame session id is invalid");
|
|
1588
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(metadata.controllerGeneration)) {
|
|
1589
|
+
throw new Error("browser frame controller generation is invalid");
|
|
1590
|
+
}
|
|
1591
|
+
if (!Number.isFinite(new Date(metadata.capturedAt).valueOf())) {
|
|
1592
|
+
throw new Error("browser frame timestamp is invalid");
|
|
1593
|
+
}
|
|
1594
|
+
return metadata;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
export function parseComputerFrameMetadata(value: unknown): ComputerFrameMetadata {
|
|
1598
|
+
if (!isRecord(value)) throw new Error("computer frame metadata is invalid");
|
|
1599
|
+
const allowed = new Set([
|
|
1600
|
+
"frameId",
|
|
1601
|
+
"computerSessionId",
|
|
1602
|
+
"controllerGeneration",
|
|
1603
|
+
"targetId",
|
|
1604
|
+
"targetGeneration",
|
|
1605
|
+
"sequence",
|
|
1606
|
+
"mediaType",
|
|
1607
|
+
"width",
|
|
1608
|
+
"height",
|
|
1609
|
+
"capturedAt",
|
|
1610
|
+
"sha256",
|
|
1611
|
+
]);
|
|
1612
|
+
if (Object.keys(value).some((key) => !allowed.has(key))) {
|
|
1613
|
+
throw new Error("computer frame metadata contains unknown fields");
|
|
1614
|
+
}
|
|
1615
|
+
const strings = [
|
|
1616
|
+
"frameId",
|
|
1617
|
+
"computerSessionId",
|
|
1618
|
+
"controllerGeneration",
|
|
1619
|
+
"targetId",
|
|
1620
|
+
"targetGeneration",
|
|
1621
|
+
"mediaType",
|
|
1622
|
+
"capturedAt",
|
|
1623
|
+
"sha256",
|
|
1624
|
+
] as const;
|
|
1625
|
+
for (const key of strings) {
|
|
1626
|
+
const item = value[key];
|
|
1627
|
+
const max = key === "targetId" ? 512 : key === "capturedAt" ? 128 : 256;
|
|
1628
|
+
if (typeof item !== "string" || item.length < 1 || utf8Bytes(item) > max) {
|
|
1629
|
+
throw new Error("computer frame metadata is invalid");
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
const metadata = value as unknown as ComputerFrameMetadata;
|
|
1633
|
+
if (
|
|
1634
|
+
!Number.isSafeInteger(metadata.sequence) ||
|
|
1635
|
+
metadata.sequence < 0 ||
|
|
1636
|
+
!Number.isSafeInteger(metadata.width) ||
|
|
1637
|
+
!Number.isSafeInteger(metadata.height)
|
|
1638
|
+
) {
|
|
1639
|
+
throw new Error("computer frame metadata integers are invalid");
|
|
1640
|
+
}
|
|
1641
|
+
assertImageDimensions(metadata.width, metadata.height);
|
|
1642
|
+
if (metadata.mediaType !== "image/jpeg" && metadata.mediaType !== "image/png") {
|
|
1643
|
+
throw new Error("computer frame media type is invalid");
|
|
1644
|
+
}
|
|
1645
|
+
if (!isUuid(metadata.computerSessionId)) {
|
|
1646
|
+
throw new Error("computer frame session id is invalid");
|
|
1647
|
+
}
|
|
1648
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(metadata.controllerGeneration)) {
|
|
1649
|
+
throw new Error("computer frame controller generation is invalid");
|
|
1650
|
+
}
|
|
1651
|
+
if (!Number.isFinite(new Date(metadata.capturedAt).valueOf())) {
|
|
1652
|
+
throw new Error("computer frame timestamp is invalid");
|
|
1653
|
+
}
|
|
1654
|
+
if (!/^[0-9a-f]{64}$/u.test(metadata.sha256)) {
|
|
1655
|
+
throw new Error("computer frame digest is invalid");
|
|
1656
|
+
}
|
|
1657
|
+
return metadata;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
function newestRelevantBrowser(
|
|
1661
|
+
sessions: readonly BrowserSession[],
|
|
1662
|
+
associationSessionId: string,
|
|
1663
|
+
): BrowserSession | null {
|
|
1664
|
+
const live = sessions.filter(
|
|
1665
|
+
(session) =>
|
|
1666
|
+
!["ending", "ended", "failed", "lost"].includes(session.lifecycle) &&
|
|
1667
|
+
session.associations.some((association) => association.sessionId === associationSessionId),
|
|
1668
|
+
);
|
|
1669
|
+
live.sort(
|
|
1670
|
+
(left, right) =>
|
|
1671
|
+
browserRelevance(right, associationSessionId) - browserRelevance(left, associationSessionId),
|
|
1672
|
+
);
|
|
1673
|
+
return live[0] ?? null;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
function newestRelevantComputer(
|
|
1677
|
+
sessions: readonly ComputerSession[],
|
|
1678
|
+
associationSessionId: string,
|
|
1679
|
+
): ComputerSession | null {
|
|
1680
|
+
const live = sessions.filter(
|
|
1681
|
+
(session) =>
|
|
1682
|
+
["starting", "active", "restoring"].includes(session.lifecycle) &&
|
|
1683
|
+
session.associations.some((association) => association.sessionId === associationSessionId),
|
|
1684
|
+
);
|
|
1685
|
+
live.sort(
|
|
1686
|
+
(left, right) =>
|
|
1687
|
+
computerRelevance(right, associationSessionId) -
|
|
1688
|
+
computerRelevance(left, associationSessionId),
|
|
1689
|
+
);
|
|
1690
|
+
return live[0] ?? null;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
function browserRelevance(session: BrowserSession, associationSessionId: string): number {
|
|
1694
|
+
const association = session.associations
|
|
1695
|
+
.filter((item) => item.sessionId === associationSessionId)
|
|
1696
|
+
.map((item) => Date.parse(item.lastUsedAt))
|
|
1697
|
+
.filter(Number.isFinite)
|
|
1698
|
+
.reduce((best, timestamp) => Math.max(best, timestamp), 0);
|
|
1699
|
+
const used = Date.parse(session.lastUsedAt);
|
|
1700
|
+
return Math.max(association, Number.isFinite(used) ? used : 0);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
function computerRelevance(session: ComputerSession, associationSessionId: string): number {
|
|
1704
|
+
const association = session.associations
|
|
1705
|
+
.filter((item) => item.sessionId === associationSessionId)
|
|
1706
|
+
.map((item) => Date.parse(item.lastUsedAt))
|
|
1707
|
+
.filter(Number.isFinite)
|
|
1708
|
+
.reduce((best, timestamp) => Math.max(best, timestamp), 0);
|
|
1709
|
+
const used = Date.parse(session.lastUsedAt);
|
|
1710
|
+
return Math.max(association, Number.isFinite(used) ? used : 0);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
function setBoundedIntegerQuery(
|
|
1714
|
+
url: URL,
|
|
1715
|
+
name: string,
|
|
1716
|
+
value: number | undefined,
|
|
1717
|
+
min: number,
|
|
1718
|
+
max: number,
|
|
1719
|
+
): void {
|
|
1720
|
+
if (value === undefined) return;
|
|
1721
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) {
|
|
1722
|
+
throw new Error(`browser frame ${name} must be an integer between ${min} and ${max}`);
|
|
1723
|
+
}
|
|
1724
|
+
url.searchParams.set(name, String(value));
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
function imageDimensions(
|
|
1728
|
+
data: Uint8Array,
|
|
1729
|
+
mediaType: BrowserFrameMetadata["mediaType"],
|
|
1730
|
+
): { width: number; height: number } {
|
|
1731
|
+
const dimensions = mediaType === "image/png" ? pngDimensions(data) : jpegDimensions(data);
|
|
1732
|
+
assertImageDimensions(dimensions.width, dimensions.height);
|
|
1733
|
+
return dimensions;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
function assertImageDimensions(width: number, height: number): void {
|
|
1737
|
+
if (
|
|
1738
|
+
!Number.isSafeInteger(width) ||
|
|
1739
|
+
!Number.isSafeInteger(height) ||
|
|
1740
|
+
width < 1 ||
|
|
1741
|
+
height < 1 ||
|
|
1742
|
+
width > BROWSER_FRAME_MAX_DIMENSION ||
|
|
1743
|
+
height > BROWSER_FRAME_MAX_DIMENSION ||
|
|
1744
|
+
width * height > BROWSER_FRAME_MAX_PIXELS
|
|
1745
|
+
) {
|
|
1746
|
+
throw new Error("browser image dimensions exceed their bounded envelope");
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
function pngDimensions(data: Uint8Array): { width: number; height: number } {
|
|
1751
|
+
const signature = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
1752
|
+
if (
|
|
1753
|
+
data.byteLength < 24 ||
|
|
1754
|
+
!signature.every((byte, index) => data[index] === byte) ||
|
|
1755
|
+
String.fromCharCode(...data.slice(12, 16)) !== "IHDR"
|
|
1756
|
+
) {
|
|
1757
|
+
throw new Error("browser returned an invalid PNG image");
|
|
1758
|
+
}
|
|
1759
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1760
|
+
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
function jpegDimensions(data: Uint8Array): { width: number; height: number } {
|
|
1764
|
+
if (data.byteLength < 4 || data[0] !== 0xff || data[1] !== 0xd8) {
|
|
1765
|
+
throw new Error("browser returned an invalid JPEG image");
|
|
1766
|
+
}
|
|
1767
|
+
let offset = 2;
|
|
1768
|
+
while (offset + 3 < data.byteLength) {
|
|
1769
|
+
if (data[offset] !== 0xff) {
|
|
1770
|
+
offset += 1;
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
let marker = data[offset + 1]!;
|
|
1774
|
+
while (marker === 0xff) {
|
|
1775
|
+
offset += 1;
|
|
1776
|
+
marker = data[offset + 1]!;
|
|
1777
|
+
}
|
|
1778
|
+
if (
|
|
1779
|
+
marker === 0xd8 ||
|
|
1780
|
+
marker === 0xd9 ||
|
|
1781
|
+
marker === 0x01 ||
|
|
1782
|
+
(marker >= 0xd0 && marker <= 0xd7)
|
|
1783
|
+
) {
|
|
1784
|
+
offset += 2;
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
if (offset + 3 >= data.byteLength) break;
|
|
1788
|
+
const length = (data[offset + 2]! << 8) | data[offset + 3]!;
|
|
1789
|
+
if (length < 2 || offset + 2 + length > data.byteLength) break;
|
|
1790
|
+
if (isJpegStartOfFrame(marker)) {
|
|
1791
|
+
if (length < 7 || offset + 8 >= data.byteLength) break;
|
|
1792
|
+
return {
|
|
1793
|
+
height: (data[offset + 5]! << 8) | data[offset + 6]!,
|
|
1794
|
+
width: (data[offset + 7]! << 8) | data[offset + 8]!,
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
offset += 2 + length;
|
|
1798
|
+
}
|
|
1799
|
+
throw new Error("browser returned a JPEG without dimensions");
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
function isJpegStartOfFrame(marker: number): boolean {
|
|
1803
|
+
return (
|
|
1804
|
+
(marker >= 0xc0 && marker <= 0xc3) ||
|
|
1805
|
+
(marker >= 0xc5 && marker <= 0xc7) ||
|
|
1806
|
+
(marker >= 0xc9 && marker <= 0xcb) ||
|
|
1807
|
+
(marker >= 0xcd && marker <= 0xcf)
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
function utf8Bytes(value: string): number {
|
|
1812
|
+
return new TextEncoder().encode(value).byteLength;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
async function sha256Hex(value: Uint8Array): Promise<string> {
|
|
1816
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1817
|
+
if (!subtle) throw new Error("computer frame digest verification is unavailable");
|
|
1818
|
+
const copy = new Uint8Array(value.byteLength);
|
|
1819
|
+
copy.set(value);
|
|
1820
|
+
const digest = new Uint8Array(await subtle.digest("SHA-256", copy));
|
|
1821
|
+
return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
function isUuid(value: string): boolean {
|
|
1825
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
1829
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1830
|
+
}
|