gologin-agent-browser-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gologin-agent-browser-cli might be problematic. Click here for more details.

Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +220 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +352 -0
  5. package/dist/commands/check.d.ts +2 -0
  6. package/dist/commands/check.js +17 -0
  7. package/dist/commands/click.d.ts +2 -0
  8. package/dist/commands/click.js +17 -0
  9. package/dist/commands/close.d.ts +2 -0
  10. package/dist/commands/close.js +12 -0
  11. package/dist/commands/current.d.ts +2 -0
  12. package/dist/commands/current.js +9 -0
  13. package/dist/commands/dblclick.d.ts +2 -0
  14. package/dist/commands/dblclick.js +17 -0
  15. package/dist/commands/fill.d.ts +2 -0
  16. package/dist/commands/fill.js +18 -0
  17. package/dist/commands/find.d.ts +2 -0
  18. package/dist/commands/find.js +86 -0
  19. package/dist/commands/focus.d.ts +2 -0
  20. package/dist/commands/focus.js +17 -0
  21. package/dist/commands/get.d.ts +2 -0
  22. package/dist/commands/get.js +19 -0
  23. package/dist/commands/hover.d.ts +2 -0
  24. package/dist/commands/hover.js +17 -0
  25. package/dist/commands/open.d.ts +2 -0
  26. package/dist/commands/open.js +67 -0
  27. package/dist/commands/pdf.d.ts +2 -0
  28. package/dist/commands/pdf.js +18 -0
  29. package/dist/commands/press.d.ts +2 -0
  30. package/dist/commands/press.js +19 -0
  31. package/dist/commands/screenshot.d.ts +2 -0
  32. package/dist/commands/screenshot.js +20 -0
  33. package/dist/commands/scroll.d.ts +2 -0
  34. package/dist/commands/scroll.js +25 -0
  35. package/dist/commands/scrollIntoView.d.ts +2 -0
  36. package/dist/commands/scrollIntoView.js +17 -0
  37. package/dist/commands/select.d.ts +2 -0
  38. package/dist/commands/select.js +18 -0
  39. package/dist/commands/sessions.d.ts +2 -0
  40. package/dist/commands/sessions.js +15 -0
  41. package/dist/commands/shared.d.ts +3 -0
  42. package/dist/commands/shared.js +13 -0
  43. package/dist/commands/snapshot.d.ts +2 -0
  44. package/dist/commands/snapshot.js +16 -0
  45. package/dist/commands/type.d.ts +2 -0
  46. package/dist/commands/type.js +18 -0
  47. package/dist/commands/uncheck.d.ts +2 -0
  48. package/dist/commands/uncheck.js +17 -0
  49. package/dist/commands/upload.d.ts +2 -0
  50. package/dist/commands/upload.js +18 -0
  51. package/dist/commands/wait.d.ts +2 -0
  52. package/dist/commands/wait.js +41 -0
  53. package/dist/daemon/browser.d.ts +37 -0
  54. package/dist/daemon/browser.js +557 -0
  55. package/dist/daemon/refStore.d.ts +9 -0
  56. package/dist/daemon/refStore.js +26 -0
  57. package/dist/daemon/server.d.ts +1 -0
  58. package/dist/daemon/server.js +235 -0
  59. package/dist/daemon/sessionManager.d.ts +50 -0
  60. package/dist/daemon/sessionManager.js +512 -0
  61. package/dist/daemon/snapshot.d.ts +8 -0
  62. package/dist/daemon/snapshot.js +285 -0
  63. package/dist/lib/config.d.ts +3 -0
  64. package/dist/lib/config.js +58 -0
  65. package/dist/lib/errors.d.ts +12 -0
  66. package/dist/lib/errors.js +63 -0
  67. package/dist/lib/types.d.ts +301 -0
  68. package/dist/lib/types.js +2 -0
  69. package/dist/lib/utils.d.ts +27 -0
  70. package/dist/lib/utils.js +165 -0
  71. package/examples/agent-flow.sh +19 -0
  72. package/package.json +59 -0
@@ -0,0 +1,37 @@
1
+ import { type Locator, type Page } from "playwright";
2
+ import type { AgentConfig, GetKind, ProxyConfig, ProxySummary, RefDescriptor, ScrollDirection, SemanticFindAction, SemanticLocatorQuery, SessionRecord, WaitLoadState } from "../lib/types";
3
+ export declare function buildConnectUrl(config: AgentConfig, token: string, profileId?: string): string;
4
+ export declare function toPlaywrightCdpUrl(connectUrl: string): string;
5
+ export declare function createQuickProfile(token: string, sessionId: string): Promise<string>;
6
+ export declare function createManagedProfile(token: string, sessionId: string, proxy?: ProxyConfig): Promise<{
7
+ profileId: string;
8
+ proxy?: ProxySummary;
9
+ }>;
10
+ export declare function deleteProfile(token: string, profileId: string): Promise<void>;
11
+ export declare function getCloudLiveViewUrl(token: string, profileId: string): Promise<string | undefined>;
12
+ export declare function getCloudProfileProxy(token: string, profileId: string): Promise<ProxySummary | undefined>;
13
+ export declare function connectToBrowser(config: AgentConfig, token: string, profileId?: string): Promise<Pick<SessionRecord, "browser" | "context" | "page" | "connectUrl">>;
14
+ export declare function navigatePage(page: Page, url: string, timeoutMs: number): Promise<string>;
15
+ export declare function resolveDescriptorLocator(page: Page, descriptor: RefDescriptor): Promise<Locator>;
16
+ export declare function resolveSelectorLocator(page: Page, selector: string, timeoutMs: number): Promise<Locator>;
17
+ export declare function resolveSemanticLocator(page: Page, query: SemanticLocatorQuery, timeoutMs: number): Promise<Locator>;
18
+ export declare function performLocatorAction(page: Page, locator: Locator, action: SemanticFindAction | "focus" | "dblclick" | "select" | "check" | "uncheck", timeoutMs: number, value?: string): Promise<string | undefined>;
19
+ export declare function actionMutatesSnapshot(action: SemanticFindAction): boolean;
20
+ export declare function pressKey(page: Page, key: string, timeoutMs: number): Promise<void>;
21
+ export declare function waitForTarget(locator: Locator, timeoutMs: number): Promise<void>;
22
+ export declare function waitForPageText(page: Page, text: string, timeoutMs: number, exact?: boolean): Promise<void>;
23
+ export declare function waitForPageUrl(page: Page, urlPattern: string, timeoutMs: number): Promise<void>;
24
+ export declare function waitForPageLoad(page: Page, state: WaitLoadState, timeoutMs: number): Promise<void>;
25
+ export declare function readLocatorValue(locator: Locator, kind: GetKind, timeoutMs: number): Promise<string>;
26
+ export declare function scrollPage(page: Page, direction: ScrollDirection, pixels: number): Promise<void>;
27
+ export declare function scrollElement(locator: Locator, direction: ScrollDirection, pixels: number): Promise<void>;
28
+ export declare function scrollLocatorIntoView(locator: Locator, timeoutMs: number): Promise<void>;
29
+ export declare function uploadFiles(locator: Locator, files: string[], timeoutMs: number): Promise<void>;
30
+ export declare function savePdf(page: Page, targetPath: string): Promise<void>;
31
+ export declare function annotatePageWithRefs(page: Page, refs: Array<{
32
+ ref: string;
33
+ x: number;
34
+ y: number;
35
+ }>): Promise<void>;
36
+ export declare function clearPageAnnotations(page: Page): Promise<void>;
37
+ export declare function closeSessionHandles(session: SessionRecord): Promise<void>;
@@ -0,0 +1,557 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildConnectUrl = buildConnectUrl;
4
+ exports.toPlaywrightCdpUrl = toPlaywrightCdpUrl;
5
+ exports.createQuickProfile = createQuickProfile;
6
+ exports.createManagedProfile = createManagedProfile;
7
+ exports.deleteProfile = deleteProfile;
8
+ exports.getCloudLiveViewUrl = getCloudLiveViewUrl;
9
+ exports.getCloudProfileProxy = getCloudProfileProxy;
10
+ exports.connectToBrowser = connectToBrowser;
11
+ exports.navigatePage = navigatePage;
12
+ exports.resolveDescriptorLocator = resolveDescriptorLocator;
13
+ exports.resolveSelectorLocator = resolveSelectorLocator;
14
+ exports.resolveSemanticLocator = resolveSemanticLocator;
15
+ exports.performLocatorAction = performLocatorAction;
16
+ exports.actionMutatesSnapshot = actionMutatesSnapshot;
17
+ exports.pressKey = pressKey;
18
+ exports.waitForTarget = waitForTarget;
19
+ exports.waitForPageText = waitForPageText;
20
+ exports.waitForPageUrl = waitForPageUrl;
21
+ exports.waitForPageLoad = waitForPageLoad;
22
+ exports.readLocatorValue = readLocatorValue;
23
+ exports.scrollPage = scrollPage;
24
+ exports.scrollElement = scrollElement;
25
+ exports.scrollLocatorIntoView = scrollLocatorIntoView;
26
+ exports.uploadFiles = uploadFiles;
27
+ exports.savePdf = savePdf;
28
+ exports.annotatePageWithRefs = annotatePageWithRefs;
29
+ exports.clearPageAnnotations = clearPageAnnotations;
30
+ exports.closeSessionHandles = closeSessionHandles;
31
+ const playwright_1 = require("playwright");
32
+ const errors_1 = require("../lib/errors");
33
+ function asAriaRole(role) {
34
+ return role && role !== "paragraph" ? role : undefined;
35
+ }
36
+ const GOLOGIN_API_BASE = "https://api.gologin.com";
37
+ function trimToUndefined(value) {
38
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
39
+ }
40
+ function normalizeProxySummary(proxy, fallback) {
41
+ if (!proxy || typeof proxy !== "object") {
42
+ return fallback
43
+ ? {
44
+ mode: fallback.mode,
45
+ country: fallback.country,
46
+ host: fallback.host,
47
+ port: fallback.port,
48
+ username: fallback.username
49
+ }
50
+ : undefined;
51
+ }
52
+ const value = proxy;
53
+ const mode = trimToUndefined(value.mode) ?? fallback?.mode;
54
+ if (!mode) {
55
+ return undefined;
56
+ }
57
+ const host = trimToUndefined(value.host) ?? fallback?.host;
58
+ const country = trimToUndefined(value.autoProxyRegion) ?? trimToUndefined(value.country) ?? fallback?.country;
59
+ const username = trimToUndefined(value.username) ?? fallback?.username;
60
+ const port = typeof value.port === "number"
61
+ ? value.port
62
+ : typeof fallback?.port === "number"
63
+ ? fallback.port
64
+ : undefined;
65
+ return {
66
+ mode,
67
+ country,
68
+ host,
69
+ port,
70
+ username
71
+ };
72
+ }
73
+ function buildCloudProfileBody(sessionId, proxy) {
74
+ const body = {
75
+ name: `gologin-agent-browser-${sessionId}`,
76
+ os: "lin"
77
+ };
78
+ if (!proxy) {
79
+ return body;
80
+ }
81
+ if (proxy.mode === "gologin") {
82
+ body.proxy = {
83
+ mode: "gologin",
84
+ autoProxyRegion: (proxy.country ?? "us").toLowerCase()
85
+ };
86
+ return body;
87
+ }
88
+ body.proxy = {
89
+ mode: proxy.mode,
90
+ host: proxy.host,
91
+ port: proxy.port,
92
+ username: proxy.username ?? "",
93
+ password: proxy.password ?? ""
94
+ };
95
+ return body;
96
+ }
97
+ async function pickContextAndPage(browser) {
98
+ const existingContext = browser.contexts()[0] ?? (await browser.newContext());
99
+ const page = existingContext.pages()[0] ?? (await existingContext.newPage());
100
+ return { context: existingContext, page };
101
+ }
102
+ async function ensureLocatorReady(locator, description, timeoutMs) {
103
+ const count = await locator.count();
104
+ if (count === 0) {
105
+ throw new errors_1.AppError("BAD_REQUEST", `${description} did not match any element`, 404);
106
+ }
107
+ const resolved = locator.first();
108
+ await resolved.waitFor({ state: "visible", timeout: timeoutMs });
109
+ return resolved;
110
+ }
111
+ function buildConnectUrl(config, token, profileId) {
112
+ const url = new URL(config.connectBase);
113
+ url.searchParams.set("token", token);
114
+ if (profileId) {
115
+ url.searchParams.set("profile", profileId);
116
+ }
117
+ return url.toString();
118
+ }
119
+ function toPlaywrightCdpUrl(connectUrl) {
120
+ const url = new URL(connectUrl);
121
+ if (url.protocol === "https:") {
122
+ url.protocol = "wss:";
123
+ }
124
+ else if (url.protocol === "http:") {
125
+ url.protocol = "ws:";
126
+ }
127
+ return url.toString();
128
+ }
129
+ function extractProfileId(payload) {
130
+ if (typeof payload === "string" && payload.length > 0) {
131
+ return payload;
132
+ }
133
+ if (!payload || typeof payload !== "object") {
134
+ return undefined;
135
+ }
136
+ const value = payload;
137
+ if (typeof value.id === "string" && value.id.length > 0) {
138
+ return value.id;
139
+ }
140
+ if (typeof value._id === "string" && value._id.length > 0) {
141
+ return value._id;
142
+ }
143
+ return undefined;
144
+ }
145
+ async function createQuickProfile(token, sessionId) {
146
+ const response = await fetch(`${GOLOGIN_API_BASE}/browser/quick`, {
147
+ method: "POST",
148
+ headers: {
149
+ Authorization: `Bearer ${token}`,
150
+ "Content-Type": "application/json"
151
+ },
152
+ body: JSON.stringify({
153
+ name: `gologin-agent-browser-${sessionId}`,
154
+ os: "lin"
155
+ })
156
+ });
157
+ if (!response.ok) {
158
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", `Failed to create GoLogin profile: ${response.status}`, 502);
159
+ }
160
+ const payload = (await response.json());
161
+ const profileId = extractProfileId(payload);
162
+ if (!profileId) {
163
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", "GoLogin profile creation returned no profile id", 502);
164
+ }
165
+ return profileId;
166
+ }
167
+ async function createManagedProfile(token, sessionId, proxy) {
168
+ if (!proxy) {
169
+ return {
170
+ profileId: await createQuickProfile(token, sessionId)
171
+ };
172
+ }
173
+ const response = await fetch(`${GOLOGIN_API_BASE}/browser/custom`, {
174
+ method: "POST",
175
+ headers: {
176
+ Authorization: `Bearer ${token}`,
177
+ "Content-Type": "application/json"
178
+ },
179
+ body: JSON.stringify(buildCloudProfileBody(sessionId, proxy))
180
+ });
181
+ if (!response.ok) {
182
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", `Failed to create GoLogin cloud profile: ${response.status}`, 502);
183
+ }
184
+ const payload = (await response.json());
185
+ const profileId = extractProfileId(payload);
186
+ if (!profileId) {
187
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", "GoLogin profile creation returned no profile id", 502);
188
+ }
189
+ let proxySummary;
190
+ if (payload && typeof payload === "object") {
191
+ proxySummary = normalizeProxySummary(payload.proxy, proxy);
192
+ }
193
+ return {
194
+ profileId,
195
+ proxy: proxySummary ?? normalizeProxySummary(undefined, proxy)
196
+ };
197
+ }
198
+ async function deleteProfile(token, profileId) {
199
+ const response = await fetch(`${GOLOGIN_API_BASE}/browser/${profileId}`, {
200
+ method: "DELETE",
201
+ headers: {
202
+ Authorization: `Bearer ${token}`
203
+ }
204
+ });
205
+ if (!response.ok) {
206
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", `Failed to delete GoLogin profile ${profileId}`, 502, {
207
+ profileId
208
+ });
209
+ }
210
+ }
211
+ async function getCloudLiveViewUrl(token, profileId) {
212
+ const response = await fetch(`${GOLOGIN_API_BASE}/browser/${profileId}/web`, {
213
+ method: "POST",
214
+ headers: {
215
+ Authorization: `Bearer ${token}`,
216
+ "Content-Type": "application/json"
217
+ },
218
+ body: JSON.stringify({})
219
+ });
220
+ if (!response.ok) {
221
+ return undefined;
222
+ }
223
+ const payload = (await response.json());
224
+ return trimToUndefined(payload.remoteOrbitaUrl);
225
+ }
226
+ async function getCloudProfileProxy(token, profileId) {
227
+ const response = await fetch(`${GOLOGIN_API_BASE}/browser/${profileId}`, {
228
+ headers: {
229
+ Authorization: `Bearer ${token}`
230
+ }
231
+ });
232
+ if (!response.ok) {
233
+ return undefined;
234
+ }
235
+ const payload = (await response.json());
236
+ return normalizeProxySummary(payload.proxy);
237
+ }
238
+ async function connectToBrowser(config, token, profileId) {
239
+ const connectUrl = toPlaywrightCdpUrl(buildConnectUrl(config, token, profileId));
240
+ try {
241
+ const browser = await playwright_1.chromium.connectOverCDP(connectUrl);
242
+ const { context, page } = await pickContextAndPage(browser);
243
+ return { browser, context, page, connectUrl };
244
+ }
245
+ catch (error) {
246
+ throw new errors_1.AppError("BROWSER_CONNECTION_FAILED", error instanceof Error ? error.message : String(error), 502, {
247
+ profileId
248
+ });
249
+ }
250
+ }
251
+ async function navigatePage(page, url, timeoutMs) {
252
+ try {
253
+ await page.goto(url, {
254
+ waitUntil: "domcontentloaded",
255
+ timeout: timeoutMs
256
+ });
257
+ await page.waitForLoadState("networkidle", { timeout: 5_000 }).catch(() => undefined);
258
+ return page.url();
259
+ }
260
+ catch (error) {
261
+ throw new errors_1.AppError("NAVIGATION_TIMEOUT", error instanceof Error ? error.message : `Failed to navigate to ${url}`, 504, {
262
+ url
263
+ });
264
+ }
265
+ }
266
+ async function locatorFromRole(page, descriptor) {
267
+ const role = asAriaRole(descriptor.role);
268
+ const name = descriptor.accessibleName ?? descriptor.text;
269
+ if (!role || !name) {
270
+ return undefined;
271
+ }
272
+ const locator = page.getByRole(role, { name });
273
+ const count = await locator.count();
274
+ if (count === 0) {
275
+ return undefined;
276
+ }
277
+ if (count === 1) {
278
+ return locator.first();
279
+ }
280
+ if (descriptor.nth !== undefined && descriptor.nth < count) {
281
+ return locator.nth(descriptor.nth);
282
+ }
283
+ return undefined;
284
+ }
285
+ async function locatorFromLabel(page, descriptor) {
286
+ const label = descriptor.ariaLabel ?? descriptor.accessibleName;
287
+ if (!label) {
288
+ return undefined;
289
+ }
290
+ const locator = page.getByLabel(label);
291
+ const count = await locator.count();
292
+ if (count === 0) {
293
+ return undefined;
294
+ }
295
+ if (count === 1) {
296
+ return locator.first();
297
+ }
298
+ if (descriptor.nth !== undefined && descriptor.nth < count) {
299
+ return locator.nth(descriptor.nth);
300
+ }
301
+ return undefined;
302
+ }
303
+ async function locatorFromPlaceholder(page, descriptor) {
304
+ if (!descriptor.placeholder) {
305
+ return undefined;
306
+ }
307
+ const locator = page.getByPlaceholder(descriptor.placeholder);
308
+ const count = await locator.count();
309
+ if (count === 0) {
310
+ return undefined;
311
+ }
312
+ if (count === 1) {
313
+ return locator.first();
314
+ }
315
+ if (descriptor.nth !== undefined && descriptor.nth < count) {
316
+ return locator.nth(descriptor.nth);
317
+ }
318
+ return undefined;
319
+ }
320
+ async function locatorFromTagAndText(page, descriptor) {
321
+ const text = descriptor.text ?? descriptor.accessibleName;
322
+ if (!text) {
323
+ return undefined;
324
+ }
325
+ const locator = page.locator(descriptor.tag).filter({ hasText: text });
326
+ const count = await locator.count();
327
+ if (count === 0) {
328
+ return undefined;
329
+ }
330
+ if (count === 1) {
331
+ return locator.first();
332
+ }
333
+ if (descriptor.nth !== undefined && descriptor.nth < count) {
334
+ return locator.nth(descriptor.nth);
335
+ }
336
+ return undefined;
337
+ }
338
+ async function locatorFromFallbackTag(page, descriptor) {
339
+ const locator = page.locator(descriptor.tag);
340
+ const count = await locator.count();
341
+ if (count === 0) {
342
+ return undefined;
343
+ }
344
+ if (count === 1) {
345
+ return locator.first();
346
+ }
347
+ if (descriptor.nth !== undefined && descriptor.nth < count) {
348
+ return locator.nth(descriptor.nth);
349
+ }
350
+ return undefined;
351
+ }
352
+ async function resolveDescriptorLocator(page, descriptor) {
353
+ const strategies = [
354
+ locatorFromRole,
355
+ locatorFromLabel,
356
+ locatorFromPlaceholder,
357
+ locatorFromTagAndText,
358
+ locatorFromFallbackTag
359
+ ];
360
+ for (const strategy of strategies) {
361
+ const locator = await strategy(page, descriptor);
362
+ if (locator) {
363
+ return locator;
364
+ }
365
+ }
366
+ throw new errors_1.AppError("REF_NOT_FOUND", `ref ${descriptor.ref} is not available on the current page`, 404, {
367
+ ref: descriptor.ref
368
+ });
369
+ }
370
+ async function resolveSelectorLocator(page, selector, timeoutMs) {
371
+ return ensureLocatorReady(page.locator(selector), `selector ${selector}`, timeoutMs);
372
+ }
373
+ async function resolveSemanticLocator(page, query, timeoutMs) {
374
+ switch (query.strategy) {
375
+ case "role":
376
+ if (!query.role) {
377
+ throw new errors_1.AppError("BAD_REQUEST", "find role requires a role value", 400);
378
+ }
379
+ return ensureLocatorReady(query.name
380
+ ? page.getByRole(query.role, { name: query.name, exact: query.exact })
381
+ : page.getByRole(query.role), `role ${query.role}`, timeoutMs);
382
+ case "text":
383
+ if (!query.text) {
384
+ throw new errors_1.AppError("BAD_REQUEST", "find text requires a text value", 400);
385
+ }
386
+ return ensureLocatorReady(page.getByText(query.text, { exact: query.exact }), `text ${query.text}`, timeoutMs);
387
+ case "label":
388
+ if (!query.label) {
389
+ throw new errors_1.AppError("BAD_REQUEST", "find label requires a label value", 400);
390
+ }
391
+ return ensureLocatorReady(page.getByLabel(query.label, { exact: query.exact }), `label ${query.label}`, timeoutMs);
392
+ case "placeholder":
393
+ if (!query.placeholder) {
394
+ throw new errors_1.AppError("BAD_REQUEST", "find placeholder requires a placeholder value", 400);
395
+ }
396
+ return ensureLocatorReady(page.getByPlaceholder(query.placeholder, { exact: query.exact }), `placeholder ${query.placeholder}`, timeoutMs);
397
+ case "first":
398
+ if (!query.selector) {
399
+ throw new errors_1.AppError("BAD_REQUEST", "find first requires a selector value", 400);
400
+ }
401
+ return ensureLocatorReady(page.locator(query.selector).first(), `first ${query.selector}`, timeoutMs);
402
+ case "last":
403
+ if (!query.selector) {
404
+ throw new errors_1.AppError("BAD_REQUEST", "find last requires a selector value", 400);
405
+ }
406
+ return ensureLocatorReady(page.locator(query.selector).last(), `last ${query.selector}`, timeoutMs);
407
+ case "nth":
408
+ if (!query.selector || query.nth === undefined) {
409
+ throw new errors_1.AppError("BAD_REQUEST", "find nth requires both an index and selector", 400);
410
+ }
411
+ return ensureLocatorReady(page.locator(query.selector).nth(query.nth), `nth ${query.nth} ${query.selector}`, timeoutMs);
412
+ }
413
+ }
414
+ async function performLocatorAction(page, locator, action, timeoutMs, value) {
415
+ switch (action) {
416
+ case "click":
417
+ await locator.click({ timeout: timeoutMs });
418
+ await page.waitForLoadState("domcontentloaded", { timeout: 5_000 }).catch(() => undefined);
419
+ return undefined;
420
+ case "fill":
421
+ await locator.fill(value ?? "", { timeout: timeoutMs });
422
+ return undefined;
423
+ case "type":
424
+ await locator.pressSequentially(value ?? "", { timeout: timeoutMs });
425
+ return undefined;
426
+ case "hover":
427
+ await locator.hover({ timeout: timeoutMs });
428
+ return undefined;
429
+ case "focus":
430
+ await locator.focus({ timeout: timeoutMs });
431
+ return undefined;
432
+ case "dblclick":
433
+ await locator.dblclick({ timeout: timeoutMs });
434
+ await page.waitForLoadState("domcontentloaded", { timeout: 5_000 }).catch(() => undefined);
435
+ return undefined;
436
+ case "select":
437
+ await locator.selectOption(value ?? "", { timeout: timeoutMs });
438
+ return undefined;
439
+ case "check":
440
+ await locator.check({ timeout: timeoutMs });
441
+ return undefined;
442
+ case "uncheck":
443
+ await locator.uncheck({ timeout: timeoutMs });
444
+ return undefined;
445
+ case "text":
446
+ return await locator.innerText({ timeout: timeoutMs });
447
+ }
448
+ }
449
+ function actionMutatesSnapshot(action) {
450
+ return action !== "text";
451
+ }
452
+ async function pressKey(page, key, timeoutMs) {
453
+ void timeoutMs;
454
+ await page.keyboard.press(key);
455
+ }
456
+ async function waitForTarget(locator, timeoutMs) {
457
+ await locator.waitFor({ state: "visible", timeout: timeoutMs });
458
+ }
459
+ async function waitForPageText(page, text, timeoutMs, exact = false) {
460
+ await page.getByText(text, { exact }).first().waitFor({ state: "visible", timeout: timeoutMs });
461
+ }
462
+ async function waitForPageUrl(page, urlPattern, timeoutMs) {
463
+ await page.waitForURL(urlPattern, { timeout: timeoutMs });
464
+ }
465
+ async function waitForPageLoad(page, state, timeoutMs) {
466
+ await page.waitForLoadState(state, { timeout: timeoutMs });
467
+ }
468
+ async function readLocatorValue(locator, kind, timeoutMs) {
469
+ switch (kind) {
470
+ case "text":
471
+ return await locator.innerText({ timeout: timeoutMs });
472
+ case "value":
473
+ return await locator.inputValue({ timeout: timeoutMs });
474
+ case "html":
475
+ return await locator.innerHTML({ timeout: timeoutMs });
476
+ default:
477
+ throw new errors_1.AppError("BAD_REQUEST", `Unsupported get kind for element target: ${kind}`, 400);
478
+ }
479
+ }
480
+ function scrollDelta(direction, pixels) {
481
+ switch (direction) {
482
+ case "up":
483
+ return { dx: 0, dy: -pixels };
484
+ case "down":
485
+ return { dx: 0, dy: pixels };
486
+ case "left":
487
+ return { dx: -pixels, dy: 0 };
488
+ case "right":
489
+ return { dx: pixels, dy: 0 };
490
+ }
491
+ }
492
+ async function scrollPage(page, direction, pixels) {
493
+ const { dx, dy } = scrollDelta(direction, pixels);
494
+ await page.mouse.wheel(dx, dy);
495
+ }
496
+ async function scrollElement(locator, direction, pixels) {
497
+ const { dx, dy } = scrollDelta(direction, pixels);
498
+ await locator.evaluate((element, delta) => {
499
+ if (element instanceof HTMLElement) {
500
+ element.scrollBy(delta.dx, delta.dy);
501
+ }
502
+ }, { dx, dy });
503
+ }
504
+ async function scrollLocatorIntoView(locator, timeoutMs) {
505
+ await locator.scrollIntoViewIfNeeded({ timeout: timeoutMs });
506
+ }
507
+ async function uploadFiles(locator, files, timeoutMs) {
508
+ await locator.setInputFiles(files, { timeout: timeoutMs });
509
+ }
510
+ async function savePdf(page, targetPath) {
511
+ await page.pdf({
512
+ path: targetPath,
513
+ format: "A4",
514
+ printBackground: true
515
+ });
516
+ }
517
+ async function annotatePageWithRefs(page, refs) {
518
+ await page.evaluate((labels) => {
519
+ document.getElementById("__gologin-agent-browser-annotations")?.remove();
520
+ const root = document.createElement("div");
521
+ root.id = "__gologin-agent-browser-annotations";
522
+ root.style.position = "absolute";
523
+ root.style.left = "0";
524
+ root.style.top = "0";
525
+ root.style.width = "100%";
526
+ root.style.height = "100%";
527
+ root.style.pointerEvents = "none";
528
+ root.style.zIndex = "2147483647";
529
+ for (const label of labels) {
530
+ const node = document.createElement("div");
531
+ node.textContent = label.ref;
532
+ node.style.position = "absolute";
533
+ node.style.left = `${Math.max(0, label.x)}px`;
534
+ node.style.top = `${Math.max(0, label.y)}px`;
535
+ node.style.padding = "2px 6px";
536
+ node.style.borderRadius = "999px";
537
+ node.style.background = "#ffcf33";
538
+ node.style.color = "#111";
539
+ node.style.font = "600 12px/1.2 Menlo, Monaco, monospace";
540
+ node.style.boxShadow = "0 1px 2px rgba(0,0,0,0.3)";
541
+ node.style.border = "1px solid rgba(0,0,0,0.35)";
542
+ root.appendChild(node);
543
+ }
544
+ document.body.appendChild(root);
545
+ }, refs);
546
+ }
547
+ async function clearPageAnnotations(page) {
548
+ await page.evaluate(() => {
549
+ document.getElementById("__gologin-agent-browser-annotations")?.remove();
550
+ });
551
+ }
552
+ async function closeSessionHandles(session) {
553
+ await session.browser.close().catch(async () => {
554
+ await session.page.close().catch(() => undefined);
555
+ await session.context.close().catch(() => undefined);
556
+ });
557
+ }
@@ -0,0 +1,9 @@
1
+ import type { RefDescriptor } from "../lib/types";
2
+ export declare class RefStore {
3
+ private readonly store;
4
+ set(sessionId: string, refs: RefDescriptor[]): void;
5
+ get(sessionId: string, ref: string): RefDescriptor | undefined;
6
+ list(sessionId: string): RefDescriptor[];
7
+ clear(sessionId: string): void;
8
+ has(sessionId: string): boolean;
9
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RefStore = void 0;
4
+ class RefStore {
5
+ store = new Map();
6
+ set(sessionId, refs) {
7
+ const map = new Map();
8
+ for (const descriptor of refs) {
9
+ map.set(descriptor.ref, descriptor);
10
+ }
11
+ this.store.set(sessionId, map);
12
+ }
13
+ get(sessionId, ref) {
14
+ return this.store.get(sessionId)?.get(ref);
15
+ }
16
+ list(sessionId) {
17
+ return Array.from(this.store.get(sessionId)?.values() ?? []);
18
+ }
19
+ clear(sessionId) {
20
+ this.store.delete(sessionId);
21
+ }
22
+ has(sessionId) {
23
+ return this.store.has(sessionId);
24
+ }
25
+ }
26
+ exports.RefStore = RefStore;
@@ -0,0 +1 @@
1
+ export {};