@opengeni/codemode 0.2.2
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 +71 -0
- package/dist/artifacts.d.ts +99 -0
- package/dist/declarations.d.ts +12 -0
- package/dist/environment.d.ts +28 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +1543 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +1529 -0
- package/dist/structured.d.ts +11 -0
- package/package.json +43 -0
- package/src/artifacts.ts +313 -0
- package/src/declarations.ts +345 -0
- package/src/environment.ts +101 -0
- package/src/index.ts +736 -0
- package/src/interaction.ts +726 -0
- package/src/structured.ts +52 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AttachedBrowserDevice,
|
|
3
|
+
BrowserAction,
|
|
4
|
+
BrowserActionReceipt,
|
|
5
|
+
BrowserDiagnosticBatch,
|
|
6
|
+
BrowserDiagnosticKind,
|
|
7
|
+
BrowserIdentity,
|
|
8
|
+
BrowserIdentityListResponse,
|
|
9
|
+
BrowserIdentityMutationResponse,
|
|
10
|
+
BrowserLocator,
|
|
11
|
+
BrowserObservation,
|
|
12
|
+
BrowserRevisionListResponse,
|
|
13
|
+
BrowserSession,
|
|
14
|
+
BrowserSessionMutationResponse,
|
|
15
|
+
BrowserTarget,
|
|
16
|
+
BrowserTargetListResponse,
|
|
17
|
+
ComputerAction,
|
|
18
|
+
ComputerActionReceipt,
|
|
19
|
+
ComputerLocator,
|
|
20
|
+
ComputerObservation,
|
|
21
|
+
ComputerSession,
|
|
22
|
+
ComputerSessionMutationResponse,
|
|
23
|
+
ComputerTarget,
|
|
24
|
+
ComputerTargetListResponse,
|
|
25
|
+
InteractionPlacement,
|
|
26
|
+
PublishBrowserRevisionResponse,
|
|
27
|
+
} from "@opengeni/contracts";
|
|
28
|
+
import type { CodemodeCallOptions, CodemodeClient } from "./index";
|
|
29
|
+
import { environmentCodemodeClient, type CodemodeClientProvider } from "./environment";
|
|
30
|
+
import { CodemodeArtifactCollection } from "./artifacts";
|
|
31
|
+
import { callStructured, codemodeClientProvider } from "./structured";
|
|
32
|
+
|
|
33
|
+
export { CodemodeToolExecutionError } from "./structured";
|
|
34
|
+
|
|
35
|
+
const PATH = {
|
|
36
|
+
discover: ["interaction", "discover"],
|
|
37
|
+
browserOpen: ["interaction", "browser", "open"],
|
|
38
|
+
browserTabs: ["interaction", "browser", "tabs"],
|
|
39
|
+
browserObserve: ["interaction", "browser", "observe"],
|
|
40
|
+
browserAct: ["interaction", "browser", "act"],
|
|
41
|
+
browserDebug: ["interaction", "browser", "debug"],
|
|
42
|
+
browserIdentity: ["interaction", "browser", "identity"],
|
|
43
|
+
browserPublish: ["interaction", "browser", "publish"],
|
|
44
|
+
browserLifecycle: ["interaction", "browser", "lifecycle"],
|
|
45
|
+
computerOpen: ["interaction", "computer", "open"],
|
|
46
|
+
computerTargets: ["interaction", "computer", "targets"],
|
|
47
|
+
computerObserve: ["interaction", "computer", "observe"],
|
|
48
|
+
computerAct: ["interaction", "computer", "act"],
|
|
49
|
+
computerLifecycle: ["interaction", "computer", "lifecycle"],
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
export type InteractionDiscovery = {
|
|
53
|
+
browserRevision: number;
|
|
54
|
+
computerRevision: number;
|
|
55
|
+
identityRevision: number;
|
|
56
|
+
attachedBrowserRevision: number;
|
|
57
|
+
browsers: BrowserSession[];
|
|
58
|
+
computers: ComputerSession[];
|
|
59
|
+
identities: BrowserIdentity[];
|
|
60
|
+
attachedBrowsers: AttachedBrowserDevice[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type CodemodeBrowserOpenOptions = {
|
|
64
|
+
browserSessionId?: string | undefined;
|
|
65
|
+
mode?: "reuse_or_create" | "new" | undefined;
|
|
66
|
+
name?: string | undefined;
|
|
67
|
+
initialUrl?: string | undefined;
|
|
68
|
+
headless?: boolean | undefined;
|
|
69
|
+
placement?: InteractionPlacement | undefined;
|
|
70
|
+
identityId?: string | undefined;
|
|
71
|
+
baseRevisionId?: string | undefined;
|
|
72
|
+
linkedComputerSessionId?: string | undefined;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type CodemodeComputerOpenOptions = {
|
|
76
|
+
computerSessionId?: string | undefined;
|
|
77
|
+
mode?: "reuse_or_create" | "new" | undefined;
|
|
78
|
+
name?: string | undefined;
|
|
79
|
+
placement?: InteractionPlacement | undefined;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type BrowserActionFences = {
|
|
83
|
+
expectedTargetGeneration?: string | undefined;
|
|
84
|
+
expectedDocumentGeneration?: string | null | undefined;
|
|
85
|
+
expectedFrameId?: string | null | undefined;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type ComputerActionFences = {
|
|
89
|
+
expectedTargetGeneration?: string | undefined;
|
|
90
|
+
expectedObservationId?: string | null | undefined;
|
|
91
|
+
expectedFrameId?: string | null | undefined;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Authored object facade over the exact atomic Browser/Computer catalog. */
|
|
95
|
+
export class OpenGeniCodemode {
|
|
96
|
+
readonly browsers: CodemodeBrowserCollection;
|
|
97
|
+
readonly computers: CodemodeComputerCollection;
|
|
98
|
+
readonly artifacts: CodemodeArtifactCollection;
|
|
99
|
+
|
|
100
|
+
constructor(client: CodemodeClient | CodemodeClientProvider = () => environmentCodemodeClient()) {
|
|
101
|
+
const provider = codemodeClientProvider(client);
|
|
102
|
+
this.browsers = new CodemodeBrowserCollection(provider);
|
|
103
|
+
this.computers = new CodemodeComputerCollection(provider);
|
|
104
|
+
this.artifacts = new CodemodeArtifactCollection(provider);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async discover(
|
|
108
|
+
options: {
|
|
109
|
+
includeTerminal?: boolean | undefined;
|
|
110
|
+
includeArchivedIdentities?: boolean | undefined;
|
|
111
|
+
includeDisconnectedDevices?: boolean | undefined;
|
|
112
|
+
} = {},
|
|
113
|
+
callOptions: CodemodeCallOptions = {},
|
|
114
|
+
): Promise<InteractionDiscovery> {
|
|
115
|
+
return await callStructured(this.browsers.client, PATH.discover, options, callOptions);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class CodemodeBrowserCollection {
|
|
120
|
+
readonly identities: CodemodeBrowserIdentityCollection;
|
|
121
|
+
|
|
122
|
+
constructor(readonly client: CodemodeClientProvider) {
|
|
123
|
+
this.identities = new CodemodeBrowserIdentityCollection(client);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async list(
|
|
127
|
+
options: { includeTerminal?: boolean | undefined } = {},
|
|
128
|
+
callOptions: CodemodeCallOptions = {},
|
|
129
|
+
): Promise<BrowserSession[]> {
|
|
130
|
+
return (
|
|
131
|
+
await callStructured<InteractionDiscovery>(this.client, PATH.discover, options, callOptions)
|
|
132
|
+
).browsers;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async open(
|
|
136
|
+
options: CodemodeBrowserOpenOptions = {},
|
|
137
|
+
callOptions: CodemodeCallOptions = {},
|
|
138
|
+
): Promise<CodemodeBrowser> {
|
|
139
|
+
const opened = await callStructured<{ session: BrowserSession; targets: BrowserTarget[] }>(
|
|
140
|
+
this.client,
|
|
141
|
+
PATH.browserOpen,
|
|
142
|
+
options,
|
|
143
|
+
callOptions,
|
|
144
|
+
);
|
|
145
|
+
return new CodemodeBrowser(this.client, opened.session.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
use(browserSessionId: string): CodemodeBrowser {
|
|
149
|
+
return new CodemodeBrowser(this.client, browserSessionId);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export class CodemodeBrowserIdentityCollection {
|
|
154
|
+
constructor(private readonly client: CodemodeClientProvider) {}
|
|
155
|
+
|
|
156
|
+
async list(
|
|
157
|
+
options: { includeArchived?: boolean | undefined } = {},
|
|
158
|
+
callOptions: CodemodeCallOptions = {},
|
|
159
|
+
): Promise<BrowserIdentityListResponse> {
|
|
160
|
+
const response = await callStructured<{
|
|
161
|
+
operation: "list";
|
|
162
|
+
result: BrowserIdentityListResponse;
|
|
163
|
+
}>(this.client, PATH.browserIdentity, { operation: "list", ...options }, callOptions);
|
|
164
|
+
return response.result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async get(identityId: string, callOptions: CodemodeCallOptions = {}): Promise<BrowserIdentity> {
|
|
168
|
+
const response = await callStructured<{ operation: "get"; result: BrowserIdentity }>(
|
|
169
|
+
this.client,
|
|
170
|
+
PATH.browserIdentity,
|
|
171
|
+
{ operation: "get", identityId },
|
|
172
|
+
callOptions,
|
|
173
|
+
);
|
|
174
|
+
return response.result;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async create(
|
|
178
|
+
name: string,
|
|
179
|
+
callOptions: CodemodeCallOptions = {},
|
|
180
|
+
): Promise<BrowserIdentityMutationResponse> {
|
|
181
|
+
const response = await callStructured<{
|
|
182
|
+
operation: "create";
|
|
183
|
+
result: BrowserIdentityMutationResponse;
|
|
184
|
+
}>(this.client, PATH.browserIdentity, { operation: "create", name }, callOptions);
|
|
185
|
+
return response.result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async revisions(
|
|
189
|
+
identityId: string,
|
|
190
|
+
callOptions: CodemodeCallOptions = {},
|
|
191
|
+
): Promise<BrowserRevisionListResponse> {
|
|
192
|
+
const response = await callStructured<{
|
|
193
|
+
operation: "revisions";
|
|
194
|
+
result: BrowserRevisionListResponse;
|
|
195
|
+
}>(this.client, PATH.browserIdentity, { operation: "revisions", identityId }, callOptions);
|
|
196
|
+
return response.result;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export class CodemodeBrowser {
|
|
201
|
+
readonly tabs: CodemodeBrowserTabCollection;
|
|
202
|
+
|
|
203
|
+
constructor(
|
|
204
|
+
private readonly client: CodemodeClientProvider,
|
|
205
|
+
readonly id: string,
|
|
206
|
+
) {
|
|
207
|
+
this.tabs = new CodemodeBrowserTabCollection(client, id);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async refresh(callOptions: CodemodeCallOptions = {}): Promise<BrowserSession> {
|
|
211
|
+
const result = await callStructured<{ session: BrowserSession; targets: BrowserTarget[] }>(
|
|
212
|
+
this.client,
|
|
213
|
+
PATH.browserOpen,
|
|
214
|
+
{ browserSessionId: this.id },
|
|
215
|
+
callOptions,
|
|
216
|
+
);
|
|
217
|
+
return result.session;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async observe(
|
|
221
|
+
targetId?: string,
|
|
222
|
+
callOptions: CodemodeCallOptions = {},
|
|
223
|
+
): Promise<BrowserObservation> {
|
|
224
|
+
return await this.tabs
|
|
225
|
+
.use(await this.tabs.resolveId(targetId, callOptions))
|
|
226
|
+
.observe(callOptions);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async act(
|
|
230
|
+
action: BrowserAction,
|
|
231
|
+
options: BrowserActionFences & { targetId?: string | undefined } = {},
|
|
232
|
+
callOptions: CodemodeCallOptions = {},
|
|
233
|
+
): Promise<BrowserActionReceipt> {
|
|
234
|
+
const targetId = await this.tabs.resolveId(options.targetId, callOptions);
|
|
235
|
+
return await this.tabs.use(targetId).act(action, options, callOptions);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async diagnostics(
|
|
239
|
+
targetId?: string,
|
|
240
|
+
options: {
|
|
241
|
+
kinds?: BrowserDiagnosticKind[] | undefined;
|
|
242
|
+
after?: number | undefined;
|
|
243
|
+
limit?: number | undefined;
|
|
244
|
+
} = {},
|
|
245
|
+
callOptions: CodemodeCallOptions = {},
|
|
246
|
+
): Promise<BrowserDiagnosticBatch> {
|
|
247
|
+
return await callStructured(
|
|
248
|
+
this.client,
|
|
249
|
+
PATH.browserDebug,
|
|
250
|
+
{
|
|
251
|
+
browserSessionId: this.id,
|
|
252
|
+
targetId: await this.tabs.resolveId(targetId, callOptions),
|
|
253
|
+
...options,
|
|
254
|
+
},
|
|
255
|
+
callOptions,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async publish(
|
|
260
|
+
input: {
|
|
261
|
+
identityId: string;
|
|
262
|
+
expectedHeadGeneration: number;
|
|
263
|
+
advanceDefault?: boolean | undefined;
|
|
264
|
+
},
|
|
265
|
+
callOptions: CodemodeCallOptions = {},
|
|
266
|
+
): Promise<PublishBrowserRevisionResponse> {
|
|
267
|
+
return await callStructured(
|
|
268
|
+
this.client,
|
|
269
|
+
PATH.browserPublish,
|
|
270
|
+
{ browserSessionId: this.id, ...input },
|
|
271
|
+
callOptions,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async suspend(callOptions: CodemodeCallOptions = {}): Promise<BrowserSessionMutationResponse> {
|
|
276
|
+
return await this.lifecycle("suspend", callOptions);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async resume(callOptions: CodemodeCallOptions = {}): Promise<BrowserSessionMutationResponse> {
|
|
280
|
+
return await this.lifecycle("resume", callOptions);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async end(callOptions: CodemodeCallOptions = {}): Promise<BrowserSessionMutationResponse> {
|
|
284
|
+
return await this.lifecycle("end", callOptions);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private async lifecycle(
|
|
288
|
+
action: "suspend" | "resume" | "end",
|
|
289
|
+
callOptions: CodemodeCallOptions,
|
|
290
|
+
): Promise<BrowserSessionMutationResponse> {
|
|
291
|
+
return await callStructured(
|
|
292
|
+
this.client,
|
|
293
|
+
PATH.browserLifecycle,
|
|
294
|
+
{ browserSessionId: this.id, action },
|
|
295
|
+
callOptions,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export class CodemodeBrowserTabCollection {
|
|
301
|
+
constructor(
|
|
302
|
+
private readonly client: CodemodeClientProvider,
|
|
303
|
+
private readonly browserSessionId: string,
|
|
304
|
+
) {}
|
|
305
|
+
|
|
306
|
+
async list(callOptions: CodemodeCallOptions = {}): Promise<BrowserTargetListResponse> {
|
|
307
|
+
return await callStructured(
|
|
308
|
+
this.client,
|
|
309
|
+
PATH.browserTabs,
|
|
310
|
+
{ operation: "list", browserSessionId: this.browserSessionId },
|
|
311
|
+
callOptions,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async selected(callOptions: CodemodeCallOptions = {}): Promise<CodemodeBrowserTab> {
|
|
316
|
+
return this.use(await this.resolveId(undefined, callOptions));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
use(targetId: string): CodemodeBrowserTab {
|
|
320
|
+
return new CodemodeBrowserTab(this.client, this.browserSessionId, targetId);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async open(url?: string, callOptions: CodemodeCallOptions = {}): Promise<CodemodeBrowserTab> {
|
|
324
|
+
const before = await this.list(callOptions);
|
|
325
|
+
const after = await callStructured<BrowserTargetListResponse>(
|
|
326
|
+
this.client,
|
|
327
|
+
PATH.browserTabs,
|
|
328
|
+
{
|
|
329
|
+
operation: "open",
|
|
330
|
+
browserSessionId: this.browserSessionId,
|
|
331
|
+
...(url ? { url } : {}),
|
|
332
|
+
},
|
|
333
|
+
callOptions,
|
|
334
|
+
);
|
|
335
|
+
const beforeIds = new Set(before.targets.map((target) => target.id));
|
|
336
|
+
const opened =
|
|
337
|
+
after.targets.find((target) => !beforeIds.has(target.id)) ??
|
|
338
|
+
after.targets.find((target) => target.selected);
|
|
339
|
+
if (!opened) throw new Error("Browser opened no discoverable tab");
|
|
340
|
+
return this.use(opened.id);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async select(
|
|
344
|
+
targetId: string,
|
|
345
|
+
callOptions: CodemodeCallOptions = {},
|
|
346
|
+
): Promise<CodemodeBrowserTab> {
|
|
347
|
+
const after = await callStructured<BrowserTargetListResponse>(
|
|
348
|
+
this.client,
|
|
349
|
+
PATH.browserTabs,
|
|
350
|
+
{ operation: "select", browserSessionId: this.browserSessionId, targetId },
|
|
351
|
+
callOptions,
|
|
352
|
+
);
|
|
353
|
+
if (!after.targets.some((target) => target.id === targetId && target.selected)) {
|
|
354
|
+
throw new Error("Browser did not select the requested tab");
|
|
355
|
+
}
|
|
356
|
+
return this.use(targetId);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async close(
|
|
360
|
+
targetId: string,
|
|
361
|
+
callOptions: CodemodeCallOptions = {},
|
|
362
|
+
): Promise<BrowserTargetListResponse> {
|
|
363
|
+
return await callStructured(
|
|
364
|
+
this.client,
|
|
365
|
+
PATH.browserTabs,
|
|
366
|
+
{ operation: "close", browserSessionId: this.browserSessionId, targetId },
|
|
367
|
+
callOptions,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async resolveId(targetId: string | undefined, callOptions: CodemodeCallOptions): Promise<string> {
|
|
372
|
+
const listed = await this.list(callOptions);
|
|
373
|
+
if (targetId) {
|
|
374
|
+
if (!listed.targets.some((target) => target.id === targetId)) {
|
|
375
|
+
throw new Error(`Browser tab is unavailable: ${targetId}`);
|
|
376
|
+
}
|
|
377
|
+
return targetId;
|
|
378
|
+
}
|
|
379
|
+
const selected = listed.targets.filter((target) => target.selected);
|
|
380
|
+
if (selected.length === 1) return selected[0]!.id;
|
|
381
|
+
if (listed.targets.length === 1) return listed.targets[0]!.id;
|
|
382
|
+
if (listed.targets.length === 0) throw new Error("Browser has no tabs");
|
|
383
|
+
throw new Error("Browser target is ambiguous; select an exact tab");
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export class CodemodeBrowserTab {
|
|
388
|
+
constructor(
|
|
389
|
+
private readonly client: CodemodeClientProvider,
|
|
390
|
+
readonly browserSessionId: string,
|
|
391
|
+
readonly id: string,
|
|
392
|
+
) {}
|
|
393
|
+
|
|
394
|
+
async observe(callOptions: CodemodeCallOptions = {}): Promise<BrowserObservation> {
|
|
395
|
+
return await callStructured(
|
|
396
|
+
this.client,
|
|
397
|
+
PATH.browserObserve,
|
|
398
|
+
{ browserSessionId: this.browserSessionId, targetId: this.id },
|
|
399
|
+
callOptions,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async act(
|
|
404
|
+
action: BrowserAction,
|
|
405
|
+
fences: BrowserActionFences = {},
|
|
406
|
+
callOptions: CodemodeCallOptions = {},
|
|
407
|
+
): Promise<BrowserActionReceipt> {
|
|
408
|
+
return await callStructured(
|
|
409
|
+
this.client,
|
|
410
|
+
PATH.browserAct,
|
|
411
|
+
{ browserSessionId: this.browserSessionId, targetId: this.id, action, ...fences },
|
|
412
|
+
callOptions,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async navigate(url: string, callOptions: CodemodeCallOptions = {}) {
|
|
417
|
+
return await this.act({ type: "navigate", url }, {}, callOptions);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
getByRole(role: string, options: { name?: string; exact?: boolean } = {}) {
|
|
421
|
+
return new CodemodeBrowserLocator(this, { kind: "role", role, ...options });
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
getByText(text: string) {
|
|
425
|
+
return new CodemodeBrowserLocator(this, { kind: "text", text });
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getByLabel(text: string) {
|
|
429
|
+
return new CodemodeBrowserLocator(this, { kind: "label", text });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
getByPlaceholder(text: string) {
|
|
433
|
+
return new CodemodeBrowserLocator(this, { kind: "placeholder", text });
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
getByTestId(value: string) {
|
|
437
|
+
return new CodemodeBrowserLocator(this, { kind: "test_id", value });
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
locator(selector: string) {
|
|
441
|
+
return new CodemodeBrowserLocator(this, { kind: "css", selector });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
ref(ref: string) {
|
|
445
|
+
return new CodemodeBrowserLocator(this, { kind: "ref", ref });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export class CodemodeBrowserLocator {
|
|
450
|
+
constructor(
|
|
451
|
+
private readonly tab: CodemodeBrowserTab,
|
|
452
|
+
readonly locator: BrowserLocator,
|
|
453
|
+
) {}
|
|
454
|
+
|
|
455
|
+
async click(
|
|
456
|
+
options: { button?: "left" | "right" | "middle" } = {},
|
|
457
|
+
callOptions: CodemodeCallOptions = {},
|
|
458
|
+
) {
|
|
459
|
+
return await this.tab.act(
|
|
460
|
+
{ type: "click", locator: this.locator, ...options },
|
|
461
|
+
{},
|
|
462
|
+
callOptions,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async doubleClick(callOptions: CodemodeCallOptions = {}) {
|
|
467
|
+
return await this.tab.act({ type: "double_click", locator: this.locator }, {}, callOptions);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async hover(callOptions: CodemodeCallOptions = {}) {
|
|
471
|
+
return await this.tab.act({ type: "hover", locator: this.locator }, {}, callOptions);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async fill(value: string, callOptions: CodemodeCallOptions = {}) {
|
|
475
|
+
return await this.tab.act({ type: "fill", locator: this.locator, value }, {}, callOptions);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async type(text: string, callOptions: CodemodeCallOptions = {}) {
|
|
479
|
+
return await this.tab.act({ type: "type", locator: this.locator, text }, {}, callOptions);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async press(key: string, callOptions: CodemodeCallOptions = {}) {
|
|
483
|
+
return await this.tab.act({ type: "press", locator: this.locator, key }, {}, callOptions);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async select(values: string[], callOptions: CodemodeCallOptions = {}) {
|
|
487
|
+
return await this.tab.act({ type: "select", locator: this.locator, values }, {}, callOptions);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async check(checked = true, callOptions: CodemodeCallOptions = {}) {
|
|
491
|
+
return await this.tab.act({ type: "check", locator: this.locator, checked }, {}, callOptions);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export class CodemodeComputerCollection {
|
|
496
|
+
constructor(private readonly client: CodemodeClientProvider) {}
|
|
497
|
+
|
|
498
|
+
async list(
|
|
499
|
+
options: { includeTerminal?: boolean | undefined } = {},
|
|
500
|
+
callOptions: CodemodeCallOptions = {},
|
|
501
|
+
): Promise<ComputerSession[]> {
|
|
502
|
+
return (
|
|
503
|
+
await callStructured<InteractionDiscovery>(this.client, PATH.discover, options, callOptions)
|
|
504
|
+
).computers;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async open(
|
|
508
|
+
options: CodemodeComputerOpenOptions = {},
|
|
509
|
+
callOptions: CodemodeCallOptions = {},
|
|
510
|
+
): Promise<CodemodeComputer> {
|
|
511
|
+
const opened = await callStructured<{ session: ComputerSession; targets: ComputerTarget[] }>(
|
|
512
|
+
this.client,
|
|
513
|
+
PATH.computerOpen,
|
|
514
|
+
options,
|
|
515
|
+
callOptions,
|
|
516
|
+
);
|
|
517
|
+
return new CodemodeComputer(this.client, opened.session.id);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
use(computerSessionId: string): CodemodeComputer {
|
|
521
|
+
return new CodemodeComputer(this.client, computerSessionId);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export class CodemodeComputer {
|
|
526
|
+
readonly targets: CodemodeComputerTargetCollection;
|
|
527
|
+
readonly apps: CodemodeComputerTargetCollection;
|
|
528
|
+
|
|
529
|
+
constructor(
|
|
530
|
+
private readonly client: CodemodeClientProvider,
|
|
531
|
+
readonly id: string,
|
|
532
|
+
) {
|
|
533
|
+
this.targets = new CodemodeComputerTargetCollection(client, id);
|
|
534
|
+
this.apps = this.targets;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async refresh(callOptions: CodemodeCallOptions = {}): Promise<ComputerSession> {
|
|
538
|
+
const result = await callStructured<{ session: ComputerSession; targets: ComputerTarget[] }>(
|
|
539
|
+
this.client,
|
|
540
|
+
PATH.computerOpen,
|
|
541
|
+
{ computerSessionId: this.id },
|
|
542
|
+
callOptions,
|
|
543
|
+
);
|
|
544
|
+
return result.session;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async observe(
|
|
548
|
+
targetId?: string,
|
|
549
|
+
callOptions: CodemodeCallOptions = {},
|
|
550
|
+
): Promise<ComputerObservation> {
|
|
551
|
+
return await this.targets
|
|
552
|
+
.use(await this.targets.resolveId(targetId, callOptions))
|
|
553
|
+
.observe(callOptions);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async act(
|
|
557
|
+
action: ComputerAction,
|
|
558
|
+
options: ComputerActionFences & { targetId?: string | undefined } = {},
|
|
559
|
+
callOptions: CodemodeCallOptions = {},
|
|
560
|
+
): Promise<ComputerActionReceipt> {
|
|
561
|
+
return await this.targets
|
|
562
|
+
.use(await this.targets.resolveId(options.targetId, callOptions))
|
|
563
|
+
.act(action, options, callOptions);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async launch(applicationId: string, callOptions: CodemodeCallOptions = {}) {
|
|
567
|
+
const targetId = await this.targets.resolveId(undefined, callOptions);
|
|
568
|
+
await this.targets.use(targetId).act({ type: "launch", applicationId }, {}, callOptions);
|
|
569
|
+
return await this.targets.list(callOptions);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async end(callOptions: CodemodeCallOptions = {}): Promise<ComputerSessionMutationResponse> {
|
|
573
|
+
return await callStructured(
|
|
574
|
+
this.client,
|
|
575
|
+
PATH.computerLifecycle,
|
|
576
|
+
{ computerSessionId: this.id, action: "end" },
|
|
577
|
+
callOptions,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export class CodemodeComputerTargetCollection {
|
|
583
|
+
constructor(
|
|
584
|
+
private readonly client: CodemodeClientProvider,
|
|
585
|
+
private readonly computerSessionId: string,
|
|
586
|
+
) {}
|
|
587
|
+
|
|
588
|
+
async list(callOptions: CodemodeCallOptions = {}): Promise<ComputerTargetListResponse> {
|
|
589
|
+
return await callStructured(
|
|
590
|
+
this.client,
|
|
591
|
+
PATH.computerTargets,
|
|
592
|
+
{ computerSessionId: this.computerSessionId },
|
|
593
|
+
callOptions,
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
use(targetId: string): CodemodeComputerTarget {
|
|
598
|
+
return new CodemodeComputerTarget(this.client, this.computerSessionId, targetId);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async focused(callOptions: CodemodeCallOptions = {}): Promise<CodemodeComputerTarget> {
|
|
602
|
+
return this.use(await this.resolveId(undefined, callOptions));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async resolveId(targetId: string | undefined, callOptions: CodemodeCallOptions): Promise<string> {
|
|
606
|
+
const listed = await this.list(callOptions);
|
|
607
|
+
if (targetId) {
|
|
608
|
+
if (!listed.targets.some((target) => target.id === targetId)) {
|
|
609
|
+
throw new Error(`Computer target is unavailable: ${targetId}`);
|
|
610
|
+
}
|
|
611
|
+
return targetId;
|
|
612
|
+
}
|
|
613
|
+
const focused = listed.targets.filter((target) => target.focused);
|
|
614
|
+
if (focused.length === 1) return focused[0]!.id;
|
|
615
|
+
if (listed.targets.length === 1) return listed.targets[0]!.id;
|
|
616
|
+
if (listed.targets.length === 0)
|
|
617
|
+
throw new Error("Computer has no app, window, or screen targets");
|
|
618
|
+
throw new Error("Computer target is ambiguous; select an exact app or window");
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export class CodemodeComputerTarget {
|
|
623
|
+
constructor(
|
|
624
|
+
private readonly client: CodemodeClientProvider,
|
|
625
|
+
readonly computerSessionId: string,
|
|
626
|
+
readonly id: string,
|
|
627
|
+
) {}
|
|
628
|
+
|
|
629
|
+
async observe(callOptions: CodemodeCallOptions = {}): Promise<ComputerObservation> {
|
|
630
|
+
return await callStructured(
|
|
631
|
+
this.client,
|
|
632
|
+
PATH.computerObserve,
|
|
633
|
+
{ computerSessionId: this.computerSessionId, targetId: this.id },
|
|
634
|
+
callOptions,
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async act(
|
|
639
|
+
action: ComputerAction,
|
|
640
|
+
fences: ComputerActionFences = {},
|
|
641
|
+
callOptions: CodemodeCallOptions = {},
|
|
642
|
+
): Promise<ComputerActionReceipt> {
|
|
643
|
+
return await callStructured(
|
|
644
|
+
this.client,
|
|
645
|
+
PATH.computerAct,
|
|
646
|
+
{ computerSessionId: this.computerSessionId, targetId: this.id, action, ...fences },
|
|
647
|
+
callOptions,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
getByRole(role: string, options: { name?: string; exact?: boolean } = {}) {
|
|
652
|
+
return new CodemodeComputerLocator(this, { kind: "role", role, ...options });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
getByText(text: string, exact?: boolean) {
|
|
656
|
+
return new CodemodeComputerLocator(this, { kind: "text", text, ...(exact ? { exact } : {}) });
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
getByLabel(text: string, exact?: boolean) {
|
|
660
|
+
return new CodemodeComputerLocator(this, { kind: "label", text, ...(exact ? { exact } : {}) });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
getByIdentifier(value: string) {
|
|
664
|
+
return new CodemodeComputerLocator(this, { kind: "identifier", value });
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
ref(ref: string) {
|
|
668
|
+
return new CodemodeComputerLocator(this, { kind: "ref", ref });
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export class CodemodeComputerLocator {
|
|
673
|
+
constructor(
|
|
674
|
+
private readonly target: CodemodeComputerTarget,
|
|
675
|
+
readonly locator: ComputerLocator,
|
|
676
|
+
) {}
|
|
677
|
+
|
|
678
|
+
async invoke(callOptions: CodemodeCallOptions = {}) {
|
|
679
|
+
return await this.semantic("invoke", undefined, callOptions);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async focus(callOptions: CodemodeCallOptions = {}) {
|
|
683
|
+
return await this.semantic("focus", undefined, callOptions);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async setValue(value: string | number | boolean, callOptions: CodemodeCallOptions = {}) {
|
|
687
|
+
return await this.semantic("set_value", value, callOptions);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async select(callOptions: CodemodeCallOptions = {}) {
|
|
691
|
+
return await this.semantic("select", undefined, callOptions);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async expand(callOptions: CodemodeCallOptions = {}) {
|
|
695
|
+
return await this.semantic("expand", undefined, callOptions);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
async collapse(callOptions: CodemodeCallOptions = {}) {
|
|
699
|
+
return await this.semantic("collapse", undefined, callOptions);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
private async semantic(
|
|
703
|
+
action: Extract<ComputerAction, { type: "semantic" }>["action"],
|
|
704
|
+
value: string | number | boolean | undefined,
|
|
705
|
+
callOptions: CodemodeCallOptions,
|
|
706
|
+
) {
|
|
707
|
+
return await this.target.act(
|
|
708
|
+
{
|
|
709
|
+
type: "semantic",
|
|
710
|
+
locator: this.locator,
|
|
711
|
+
action,
|
|
712
|
+
...(value === undefined ? {} : { value }),
|
|
713
|
+
},
|
|
714
|
+
{},
|
|
715
|
+
callOptions,
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
export function createOpenGeniCodemode(
|
|
721
|
+
client: CodemodeClient | CodemodeClientProvider = () => environmentCodemodeClient(),
|
|
722
|
+
) {
|
|
723
|
+
return new OpenGeniCodemode(client);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export const openGeni = createOpenGeniCodemode();
|