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,285 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSnapshotModel = buildSnapshotModel;
4
+ exports.buildSnapshot = buildSnapshot;
5
+ const INTERACTIVE_KINDS = new Set([
6
+ "link",
7
+ "button",
8
+ "input",
9
+ "checkbox",
10
+ "radio",
11
+ "textarea",
12
+ "select"
13
+ ]);
14
+ function normalizeWhitespace(value) {
15
+ if (!value) {
16
+ return undefined;
17
+ }
18
+ const normalized = value.replace(/\s+/g, " ").trim();
19
+ return normalized.length > 0 ? normalized : undefined;
20
+ }
21
+ function buildCandidateKey(candidate) {
22
+ return [
23
+ candidate.kind,
24
+ candidate.tag,
25
+ candidate.role ?? "",
26
+ normalizeWhitespace(candidate.accessibleName) ?? "",
27
+ normalizeWhitespace(candidate.text) ?? "",
28
+ normalizeWhitespace(candidate.ariaLabel) ?? "",
29
+ normalizeWhitespace(candidate.placeholder) ?? "",
30
+ normalizeWhitespace(candidate.name) ?? "",
31
+ normalizeWhitespace(candidate.href) ?? "",
32
+ normalizeWhitespace(candidate.inputType) ?? ""
33
+ ].join("|");
34
+ }
35
+ function candidateText(candidate) {
36
+ return (normalizeWhitespace(candidate.accessibleName) ??
37
+ normalizeWhitespace(candidate.text) ??
38
+ normalizeWhitespace(candidate.selectedText) ??
39
+ normalizeWhitespace(candidate.placeholder) ??
40
+ normalizeWhitespace(candidate.name) ??
41
+ normalizeWhitespace(candidate.href));
42
+ }
43
+ function buildSnapshotModel(rawCandidates, options = {}) {
44
+ const counters = new Map();
45
+ const items = [];
46
+ const refs = [];
47
+ for (const candidate of rawCandidates) {
48
+ if (options.interactive && !INTERACTIVE_KINDS.has(candidate.kind)) {
49
+ continue;
50
+ }
51
+ const text = candidateText(candidate);
52
+ if (!text) {
53
+ continue;
54
+ }
55
+ const key = buildCandidateKey(candidate);
56
+ const nth = counters.get(key) ?? 0;
57
+ counters.set(key, nth + 1);
58
+ const ref = `@e${items.length + 1}`;
59
+ items.push({
60
+ ref,
61
+ kind: candidate.kind,
62
+ text,
63
+ role: candidate.role,
64
+ flags: [
65
+ ...(candidate.checked === true ? ["checked"] : []),
66
+ ...(candidate.disabled === true ? ["disabled"] : []),
67
+ ...(normalizeWhitespace(candidate.selectedText) ? [`selected=${normalizeWhitespace(candidate.selectedText)}`] : [])
68
+ ]
69
+ });
70
+ refs.push({
71
+ ref,
72
+ kind: candidate.kind,
73
+ tag: candidate.tag,
74
+ role: candidate.role,
75
+ text: normalizeWhitespace(candidate.text),
76
+ accessibleName: normalizeWhitespace(candidate.accessibleName),
77
+ ariaLabel: normalizeWhitespace(candidate.ariaLabel),
78
+ placeholder: normalizeWhitespace(candidate.placeholder),
79
+ inputType: normalizeWhitespace(candidate.inputType),
80
+ name: normalizeWhitespace(candidate.name),
81
+ href: normalizeWhitespace(candidate.href),
82
+ nth,
83
+ checked: candidate.checked,
84
+ disabled: candidate.disabled,
85
+ selectedText: normalizeWhitespace(candidate.selectedText)
86
+ });
87
+ }
88
+ return { items, refs };
89
+ }
90
+ async function buildSnapshot(page, options = {}) {
91
+ const candidates = await page.evaluate(() => {
92
+ const selectors = [
93
+ "h1",
94
+ "h2",
95
+ "h3",
96
+ "h4",
97
+ "h5",
98
+ "h6",
99
+ "a",
100
+ "button",
101
+ "input",
102
+ "textarea",
103
+ "select",
104
+ "p",
105
+ "[role='heading']",
106
+ "[role='link']",
107
+ "[role='button']",
108
+ "[role='textbox']"
109
+ ].join(",");
110
+ const seen = new WeakSet();
111
+ function normalize(value) {
112
+ if (!value) {
113
+ return undefined;
114
+ }
115
+ const trimmed = value.replace(/\s+/g, " ").trim();
116
+ return trimmed.length > 0 ? trimmed : undefined;
117
+ }
118
+ function isVisible(element) {
119
+ const htmlElement = element;
120
+ const style = window.getComputedStyle(htmlElement);
121
+ const rect = htmlElement.getBoundingClientRect();
122
+ return (style.display !== "none" &&
123
+ style.visibility !== "hidden" &&
124
+ style.opacity !== "0" &&
125
+ rect.width > 0 &&
126
+ rect.height > 0);
127
+ }
128
+ function implicitRole(element) {
129
+ const tag = element.tagName.toLowerCase();
130
+ if (/^h[1-6]$/.test(tag)) {
131
+ return "heading";
132
+ }
133
+ if (tag === "a") {
134
+ return "link";
135
+ }
136
+ if (tag === "button") {
137
+ return "button";
138
+ }
139
+ if (tag === "textarea") {
140
+ return "textbox";
141
+ }
142
+ if (tag === "select") {
143
+ return "combobox";
144
+ }
145
+ if (tag === "input") {
146
+ const type = element.type.toLowerCase();
147
+ if (type === "submit" || type === "button" || type === "reset") {
148
+ return "button";
149
+ }
150
+ if (type === "checkbox") {
151
+ return "checkbox";
152
+ }
153
+ if (type === "radio") {
154
+ return "radio";
155
+ }
156
+ return "textbox";
157
+ }
158
+ return undefined;
159
+ }
160
+ function associatedLabel(element) {
161
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
162
+ const labels = Array.from(element.labels ?? []);
163
+ const text = labels
164
+ .map((label) => normalize(label.textContent))
165
+ .filter((value) => Boolean(value))
166
+ .join(" ");
167
+ return normalize(text);
168
+ }
169
+ return undefined;
170
+ }
171
+ function kindFor(element) {
172
+ const tag = element.tagName.toLowerCase();
173
+ const role = element.getAttribute("role");
174
+ if (/^h[1-6]$/.test(tag) || role === "heading") {
175
+ return "heading";
176
+ }
177
+ if (tag === "a" || role === "link") {
178
+ return "link";
179
+ }
180
+ if (tag === "button" || role === "button") {
181
+ return "button";
182
+ }
183
+ if (tag === "textarea") {
184
+ return "textarea";
185
+ }
186
+ if (tag === "select") {
187
+ return "select";
188
+ }
189
+ if (tag === "input") {
190
+ const inputType = element.type.toLowerCase();
191
+ if (inputType === "checkbox") {
192
+ return "checkbox";
193
+ }
194
+ if (inputType === "radio") {
195
+ return "radio";
196
+ }
197
+ return "input";
198
+ }
199
+ if (role === "textbox") {
200
+ return "input";
201
+ }
202
+ if (tag === "p") {
203
+ return "paragraph";
204
+ }
205
+ return undefined;
206
+ }
207
+ function isParagraphWrappedInteractive(element) {
208
+ if (element.tagName.toLowerCase() !== "p") {
209
+ return false;
210
+ }
211
+ const children = Array.from(element.children);
212
+ if (children.length !== 1) {
213
+ return false;
214
+ }
215
+ const childTag = children[0]?.tagName.toLowerCase();
216
+ return childTag === "a" || childTag === "button";
217
+ }
218
+ const result = [];
219
+ for (const element of Array.from(document.querySelectorAll(selectors))) {
220
+ if (seen.has(element) || !isVisible(element)) {
221
+ continue;
222
+ }
223
+ seen.add(element);
224
+ const tag = element.tagName.toLowerCase();
225
+ const kind = kindFor(element);
226
+ if (!kind) {
227
+ continue;
228
+ }
229
+ if (isParagraphWrappedInteractive(element)) {
230
+ continue;
231
+ }
232
+ const explicitRole = normalize(element.getAttribute("role"));
233
+ const role = explicitRole ?? implicitRole(element);
234
+ const text = tag === "input" || tag === "textarea" || tag === "select"
235
+ ? undefined
236
+ : normalize(element.innerText || element.textContent);
237
+ const placeholder = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement
238
+ ? normalize(element.placeholder)
239
+ : undefined;
240
+ const inputType = element instanceof HTMLInputElement ? normalize(element.type) : undefined;
241
+ const name = element instanceof HTMLInputElement ||
242
+ element instanceof HTMLTextAreaElement ||
243
+ element instanceof HTMLSelectElement
244
+ ? normalize(element.name)
245
+ : undefined;
246
+ const ariaLabel = normalize(element.getAttribute("aria-label"));
247
+ const label = associatedLabel(element);
248
+ const checked = element instanceof HTMLInputElement ? element.checked : undefined;
249
+ const disabled = element instanceof HTMLInputElement ||
250
+ element instanceof HTMLTextAreaElement ||
251
+ element instanceof HTMLSelectElement ||
252
+ element instanceof HTMLButtonElement
253
+ ? element.disabled
254
+ : element.hasAttribute("disabled");
255
+ const selectedText = element instanceof HTMLSelectElement
256
+ ? normalize(Array.from(element.selectedOptions)
257
+ .map((option) => option.textContent ?? "")
258
+ .join(" "))
259
+ : undefined;
260
+ const accessibleName = ariaLabel ??
261
+ label ??
262
+ normalize(element.innerText || element.textContent) ??
263
+ placeholder ??
264
+ name;
265
+ const href = element instanceof HTMLAnchorElement ? normalize(element.href) : undefined;
266
+ result.push({
267
+ kind,
268
+ tag,
269
+ role,
270
+ text,
271
+ accessibleName,
272
+ ariaLabel,
273
+ placeholder,
274
+ inputType,
275
+ name,
276
+ href,
277
+ checked,
278
+ disabled,
279
+ selectedText
280
+ });
281
+ }
282
+ return result;
283
+ });
284
+ return buildSnapshotModel(candidates, options);
285
+ }
@@ -0,0 +1,3 @@
1
+ import type { AgentConfig } from "./types";
2
+ export declare function loadConfig(): AgentConfig;
3
+ export declare function ensureStateDir(config: AgentConfig): void;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadConfig = loadConfig;
7
+ exports.ensureStateDir = ensureStateDir;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_os_1 = __importDefault(require("node:os"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const DEFAULT_CONNECT_BASE = "https://cloudbrowser.gologin.com/connect";
12
+ const DEFAULT_DAEMON_PORT = 44777;
13
+ const DEFAULT_DAEMON_HOST = "127.0.0.1";
14
+ const DEFAULT_NAVIGATION_TIMEOUT_MS = 30_000;
15
+ const DEFAULT_ACTION_TIMEOUT_MS = 10_000;
16
+ function parsePort(value, fallback) {
17
+ if (!value) {
18
+ return fallback;
19
+ }
20
+ const port = Number(value);
21
+ return Number.isInteger(port) && port > 0 ? port : fallback;
22
+ }
23
+ function readConfigFile(configPath) {
24
+ if (!node_fs_1.default.existsSync(configPath)) {
25
+ return {};
26
+ }
27
+ try {
28
+ const raw = node_fs_1.default.readFileSync(configPath, "utf8");
29
+ const parsed = JSON.parse(raw);
30
+ return parsed;
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ }
36
+ function loadConfig() {
37
+ const homeDir = node_os_1.default.homedir();
38
+ const baseDir = node_path_1.default.join(homeDir, ".gologin-agent-browser");
39
+ const configPath = node_path_1.default.join(baseDir, "config.json");
40
+ const logPath = node_path_1.default.join(baseDir, "daemon.log");
41
+ const socketPath = node_path_1.default.join(node_os_1.default.tmpdir(), "gologin-agent-browser.sock");
42
+ const fileConfig = readConfigFile(configPath);
43
+ return {
44
+ token: process.env.GOLOGIN_TOKEN ?? fileConfig.token,
45
+ defaultProfileId: process.env.GOLOGIN_PROFILE_ID ?? fileConfig.defaultProfileId,
46
+ connectBase: process.env.GOLOGIN_CONNECT_BASE ?? fileConfig.connectBase ?? DEFAULT_CONNECT_BASE,
47
+ daemonPort: parsePort(process.env.GOLOGIN_DAEMON_PORT, fileConfig.daemonPort ?? DEFAULT_DAEMON_PORT),
48
+ daemonHost: DEFAULT_DAEMON_HOST,
49
+ socketPath,
50
+ configPath,
51
+ logPath,
52
+ navigationTimeoutMs: DEFAULT_NAVIGATION_TIMEOUT_MS,
53
+ actionTimeoutMs: DEFAULT_ACTION_TIMEOUT_MS
54
+ };
55
+ }
56
+ function ensureStateDir(config) {
57
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(config.configPath), { recursive: true });
58
+ }
@@ -0,0 +1,12 @@
1
+ import type { DaemonErrorPayload } from "./types";
2
+ export type ErrorCode = "DAEMON_UNREACHABLE" | "TOKEN_MISSING" | "PROFILE_MISSING" | "SESSION_NOT_FOUND" | "SESSION_EXPIRED" | "REF_NOT_FOUND" | "BROWSER_CONNECTION_FAILED" | "NAVIGATION_TIMEOUT" | "SCREENSHOT_FAILED" | "PDF_FAILED" | "UPLOAD_FAILED" | "BAD_REQUEST" | "INTERNAL_ERROR";
3
+ export declare class AppError extends Error {
4
+ readonly code: ErrorCode;
5
+ readonly status: number;
6
+ readonly details?: Record<string, unknown>;
7
+ constructor(code: ErrorCode, message: string, status?: number, details?: Record<string, unknown>);
8
+ }
9
+ export declare function isDaemonErrorPayload(value: unknown): value is DaemonErrorPayload;
10
+ export declare function serializeError(error: unknown): DaemonErrorPayload;
11
+ export declare function fromDaemonError(payload: DaemonErrorPayload): AppError;
12
+ export declare function formatErrorLine(error: unknown): string;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppError = void 0;
4
+ exports.isDaemonErrorPayload = isDaemonErrorPayload;
5
+ exports.serializeError = serializeError;
6
+ exports.fromDaemonError = fromDaemonError;
7
+ exports.formatErrorLine = formatErrorLine;
8
+ class AppError extends Error {
9
+ code;
10
+ status;
11
+ details;
12
+ constructor(code, message, status = 500, details) {
13
+ super(message);
14
+ this.name = "AppError";
15
+ this.code = code;
16
+ this.status = status;
17
+ this.details = details;
18
+ }
19
+ }
20
+ exports.AppError = AppError;
21
+ function isDaemonErrorPayload(value) {
22
+ if (!value || typeof value !== "object") {
23
+ return false;
24
+ }
25
+ const candidate = value;
26
+ return (typeof candidate.code === "string" &&
27
+ typeof candidate.message === "string" &&
28
+ typeof candidate.status === "number");
29
+ }
30
+ function serializeError(error) {
31
+ if (error instanceof AppError) {
32
+ return {
33
+ code: error.code,
34
+ message: error.message,
35
+ status: error.status,
36
+ details: error.details
37
+ };
38
+ }
39
+ if (error instanceof Error) {
40
+ return {
41
+ code: "INTERNAL_ERROR",
42
+ message: error.message,
43
+ status: 500
44
+ };
45
+ }
46
+ return {
47
+ code: "INTERNAL_ERROR",
48
+ message: String(error),
49
+ status: 500
50
+ };
51
+ }
52
+ function fromDaemonError(payload) {
53
+ return new AppError(payload.code, payload.message, payload.status, payload.details);
54
+ }
55
+ function formatErrorLine(error) {
56
+ if (error instanceof AppError) {
57
+ return `${error.code}: ${error.message}`;
58
+ }
59
+ if (error instanceof Error) {
60
+ return `${error.name}: ${error.message}`;
61
+ }
62
+ return `INTERNAL_ERROR: ${String(error)}`;
63
+ }