browser-autopilot 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.
Files changed (53) hide show
  1. package/README.md +251 -0
  2. package/dist/agent/history.d.ts +41 -0
  3. package/dist/agent/history.js +98 -0
  4. package/dist/agent/history.js.map +1 -0
  5. package/dist/agent/loop.d.ts +34 -0
  6. package/dist/agent/loop.js +278 -0
  7. package/dist/agent/loop.js.map +1 -0
  8. package/dist/agent/run.d.ts +4 -0
  9. package/dist/agent/run.js +67 -0
  10. package/dist/agent/run.js.map +1 -0
  11. package/dist/agent/state.d.ts +37 -0
  12. package/dist/agent/state.js +82 -0
  13. package/dist/agent/state.js.map +1 -0
  14. package/dist/agent/tools.d.ts +414 -0
  15. package/dist/agent/tools.js +399 -0
  16. package/dist/agent/tools.js.map +1 -0
  17. package/dist/browser/cdp.d.ts +91 -0
  18. package/dist/browser/cdp.js +470 -0
  19. package/dist/browser/cdp.js.map +1 -0
  20. package/dist/browser/dom.d.ts +30 -0
  21. package/dist/browser/dom.js +79 -0
  22. package/dist/browser/dom.js.map +1 -0
  23. package/dist/browser/snapshot.d.ts +19 -0
  24. package/dist/browser/snapshot.js +70 -0
  25. package/dist/browser/snapshot.js.map +1 -0
  26. package/dist/captcha/solver.d.ts +20 -0
  27. package/dist/captcha/solver.js +101 -0
  28. package/dist/captcha/solver.js.map +1 -0
  29. package/dist/config.d.ts +36 -0
  30. package/dist/config.js +44 -0
  31. package/dist/config.js.map +1 -0
  32. package/dist/index.d.ts +20 -0
  33. package/dist/index.js +43 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/orchestrator.d.ts +33 -0
  36. package/dist/orchestrator.js +197 -0
  37. package/dist/orchestrator.js.map +1 -0
  38. package/dist/viewer/server.d.ts +14 -0
  39. package/dist/viewer/server.js +93 -0
  40. package/dist/viewer/server.js.map +1 -0
  41. package/dist/x11/agent.d.ts +34 -0
  42. package/dist/x11/agent.js +103 -0
  43. package/dist/x11/agent.js.map +1 -0
  44. package/dist/x11/chrome.d.ts +9 -0
  45. package/dist/x11/chrome.js +107 -0
  46. package/dist/x11/chrome.js.map +1 -0
  47. package/dist/x11/input.d.ts +13 -0
  48. package/dist/x11/input.js +75 -0
  49. package/dist/x11/input.js.map +1 -0
  50. package/dist/x11/login.d.ts +6 -0
  51. package/dist/x11/login.js +76 -0
  52. package/dist/x11/login.js.map +1 -0
  53. package/package.json +79 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Live browser viewer — streams the Xvfb display over WebSocket.
3
+ *
4
+ * Uses x11vnc + websockify to expose the virtual display as a
5
+ * noVNC-compatible stream. Open the URL in any browser to watch
6
+ * the agent work in real-time.
7
+ *
8
+ * Architecture:
9
+ * Xvfb (:99) → x11vnc (port 5900) → websockify (port 6080) → browser
10
+ */
11
+ import { spawn } from "child_process";
12
+ import { existsSync, writeFileSync, mkdirSync } from "fs";
13
+ const VNC_PORT = parseInt(process.env.VNC_PORT ?? "5900");
14
+ const WEB_PORT = parseInt(process.env.VIEWER_PORT ?? "6080");
15
+ const NOVNC_PATH = "/opt/novnc";
16
+ const VIEWER_HTML = `<!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <title>browser-autopilot — live agent view</title>
20
+ <style>
21
+ * { margin: 0; padding: 0; box-sizing: border-box; }
22
+ body { background: #0a0a0a; font-family: system-ui, sans-serif; color: #e0e0e0; }
23
+ #header { padding: 12px 20px; background: #111; border-bottom: 1px solid #222; display: flex; align-items: center; gap: 12px; }
24
+ #header h1 { font-size: 16px; font-weight: 500; }
25
+ #status { font-size: 13px; padding: 4px 10px; border-radius: 12px; background: #1a3a1a; color: #4ade80; }
26
+ #status.disconnected { background: #3a1a1a; color: #f87171; }
27
+ #viewer { width: 100vw; height: calc(100vh - 48px); }
28
+ #viewer canvas { width: 100%; height: 100%; object-fit: contain; }
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <div id="header">
33
+ <h1>browser-autopilot</h1>
34
+ <div id="status">connecting...</div>
35
+ </div>
36
+ <div id="viewer"></div>
37
+ <script type="module">
38
+ import RFB from './novnc/core/rfb.js';
39
+ const host = location.hostname;
40
+ const port = location.port || '${WEB_PORT}';
41
+ const url = 'ws://' + host + ':' + port + '/websockify';
42
+ const status = document.getElementById('status');
43
+ try {
44
+ const rfb = new RFB(document.getElementById('viewer'), url, { scaleViewport: true, resizeSession: false });
45
+ rfb.viewOnly = true;
46
+ rfb.addEventListener('connect', () => { status.textContent = 'live'; status.className = ''; });
47
+ rfb.addEventListener('disconnect', () => { status.textContent = 'disconnected'; status.className = 'disconnected'; });
48
+ } catch(e) {
49
+ status.textContent = 'error: ' + e.message;
50
+ status.className = 'disconnected';
51
+ }
52
+ </script>
53
+ </body>
54
+ </html>`;
55
+ export function startViewer() {
56
+ // Start x11vnc (Xvfb → VNC)
57
+ const vnc = spawn("x11vnc", [
58
+ "-display", ":99",
59
+ "-nopw",
60
+ "-listen", "0.0.0.0",
61
+ "-rfbport", String(VNC_PORT),
62
+ "-shared",
63
+ "-forever",
64
+ "-noxdamage",
65
+ ], { stdio: "ignore", detached: true });
66
+ vnc.unref();
67
+ // Write viewer HTML
68
+ mkdirSync("/tmp/viewer", { recursive: true });
69
+ writeFileSync("/tmp/viewer/index.html", VIEWER_HTML);
70
+ // Start websockify (VNC → WebSocket + serve HTML)
71
+ const ws = spawn("websockify", [
72
+ "--web", existsSync(NOVNC_PATH) ? NOVNC_PATH : "/tmp/viewer",
73
+ String(WEB_PORT),
74
+ `localhost:${VNC_PORT}`,
75
+ ], { stdio: "ignore", detached: true });
76
+ ws.unref();
77
+ const url = `http://0.0.0.0:${WEB_PORT}/index.html`;
78
+ console.log(`\n👁 Live viewer: ${url}\n`);
79
+ return {
80
+ url,
81
+ stop: () => {
82
+ try {
83
+ process.kill(-vnc.pid);
84
+ }
85
+ catch { }
86
+ try {
87
+ process.kill(-ws.pid);
88
+ }
89
+ catch { }
90
+ },
91
+ };
92
+ }
93
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/viewer/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAY,KAAK,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAG1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;AAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC,CAAC;AAC7D,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;qCAwBiB,QAAQ;;;;;;;;;;;;;;QAcrC,CAAC;AAET,MAAM,UAAU,WAAW;IAC1B,4BAA4B;IAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE;QAC3B,UAAU,EAAE,KAAK;QACjB,OAAO;QACP,SAAS,EAAE,SAAS;QACpB,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC5B,SAAS;QACT,UAAU;QACV,YAAY;KACZ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,GAAG,CAAC,KAAK,EAAE,CAAC;IAEZ,oBAAoB;IACpB,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;IAErD,kDAAkD;IAClD,MAAM,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE;QAC9B,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa;QAC5D,MAAM,CAAC,QAAQ,CAAC;QAChB,aAAa,QAAQ,EAAE;KACvB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,EAAE,CAAC,KAAK,EAAE,CAAC;IAEX,MAAM,GAAG,GAAG,kBAAkB,QAAQ,aAAa,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IAE3C,OAAO;QACN,GAAG;QACH,IAAI,EAAE,GAAG,EAAE;YACV,IAAI,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACzC,IAAI,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,GAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACzC,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Generalized X11 browser agent.
3
+ *
4
+ * Drives any browser interaction via LLM + xdotool. No CDP during execution,
5
+ * so it bypasses all automation detection (Twitter, Cloudflare, DataDome, etc.).
6
+ *
7
+ * Usage:
8
+ * const agent = new X11Agent();
9
+ * const ok = await agent.run({
10
+ * systemPrompt: "You are logging into example.com...",
11
+ * successCheck: () => pageUrlContains("/dashboard"),
12
+ * });
13
+ */
14
+ export interface X11AgentOptions {
15
+ model?: string;
16
+ screenshotDir?: string;
17
+ }
18
+ export interface RunOptions {
19
+ systemPrompt: string;
20
+ successCheck?: () => boolean;
21
+ maxSteps?: number;
22
+ stepDelayMs?: number;
23
+ onAction?: (type: string, value: string) => void;
24
+ }
25
+ export declare class X11Agent {
26
+ private client;
27
+ private model;
28
+ private screenshotDir;
29
+ constructor(opts?: X11AgentOptions);
30
+ run(opts: RunOptions): Promise<boolean>;
31
+ private execute;
32
+ private parseAction;
33
+ private log;
34
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Generalized X11 browser agent.
3
+ *
4
+ * Drives any browser interaction via LLM + xdotool. No CDP during execution,
5
+ * so it bypasses all automation detection (Twitter, Cloudflare, DataDome, etc.).
6
+ *
7
+ * Usage:
8
+ * const agent = new X11Agent();
9
+ * const ok = await agent.run({
10
+ * systemPrompt: "You are logging into example.com...",
11
+ * successCheck: () => pageUrlContains("/dashboard"),
12
+ * });
13
+ */
14
+ import Anthropic from "@anthropic-ai/sdk";
15
+ import * as x11 from "./input.js";
16
+ import { config } from "../config.js";
17
+ export class X11Agent {
18
+ client;
19
+ model;
20
+ screenshotDir;
21
+ constructor(opts = {}) {
22
+ this.client = new Anthropic({ timeout: 120_000 });
23
+ this.model = opts.model ?? "claude-sonnet-4-6";
24
+ this.screenshotDir = opts.screenshotDir ?? config.browser.dataDir;
25
+ }
26
+ async run(opts) {
27
+ const { systemPrompt, successCheck, maxSteps = 25, stepDelayMs = 1000, onAction, } = opts;
28
+ const messages = [];
29
+ for (let step = 0; step < maxSteps; step++) {
30
+ if (successCheck?.()) {
31
+ this.log(step, "SUCCESS (check passed)");
32
+ return true;
33
+ }
34
+ const imgPath = `${this.screenshotDir}/x11_step_${step}.png`;
35
+ const img = x11.screenshot(imgPath);
36
+ messages.push({
37
+ role: "user",
38
+ content: [
39
+ { type: "text", text: `Step ${step + 1}. Screenshot:` },
40
+ { type: "image", source: { type: "base64", media_type: "image/png", data: img } },
41
+ ],
42
+ });
43
+ const resp = await this.client.messages.create({
44
+ model: this.model,
45
+ max_tokens: 64000,
46
+ system: [{ type: "text", text: systemPrompt, cache_control: { type: "ephemeral" } }],
47
+ messages,
48
+ });
49
+ const raw = resp.content[0].text.trim();
50
+ messages.push({ role: "assistant", content: raw });
51
+ const action = this.parseAction(raw);
52
+ this.log(step, action);
53
+ const result = this.execute(action, messages, onAction);
54
+ if (result !== undefined)
55
+ return result;
56
+ x11.sleep(stepDelayMs);
57
+ }
58
+ this.log(-1, "max steps reached");
59
+ return false;
60
+ }
61
+ execute(action, messages, onAction) {
62
+ if (action.startsWith("ACTION: TYPE ")) {
63
+ const text = action.slice("ACTION: TYPE ".length).trim();
64
+ onAction?.("type", text);
65
+ x11.typeText(text);
66
+ }
67
+ else if (action.startsWith("ACTION: KEY ")) {
68
+ const key = action.slice("ACTION: KEY ".length).trim().split(/\s+/)[0];
69
+ onAction?.("key", key);
70
+ x11.pressKey(key);
71
+ }
72
+ else if (action.startsWith("ACTION: WAIT")) {
73
+ const parts = action.split(/\s+/);
74
+ const secs = Math.min(parseInt(parts[2] ?? "3") || 3, 3);
75
+ onAction?.("wait", String(secs));
76
+ x11.sleep(secs * 1000);
77
+ }
78
+ else if (action.startsWith("ACTION: INJECT ")) {
79
+ const value = action.slice("ACTION: INJECT ".length).trim();
80
+ onAction?.("inject", value);
81
+ }
82
+ else if (action.startsWith("ACTION: DONE")) {
83
+ return true;
84
+ }
85
+ else if (action.startsWith("ACTION: FAILED")) {
86
+ return false;
87
+ }
88
+ return undefined;
89
+ }
90
+ parseAction(raw) {
91
+ for (const line of raw.split("\n")) {
92
+ const trimmed = line.trim();
93
+ if (trimmed.startsWith("ACTION:"))
94
+ return trimmed;
95
+ }
96
+ return raw;
97
+ }
98
+ log(step, msg) {
99
+ const prefix = step >= 0 ? ` [${step + 1}]` : " [!]";
100
+ console.log(`${prefix} ${msg}`);
101
+ }
102
+ }
103
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/x11/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAetC,MAAM,OAAO,QAAQ;IACZ,MAAM,CAAY;IAClB,KAAK,CAAS;IAEd,aAAa,CAAS;IAE9B,YAAY,OAAwB,EAAE;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,mBAAmB,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAgB;QACzB,MAAM,EACL,YAAY,EACZ,YAAY,EACZ,QAAQ,GAAG,EAAE,EACb,WAAW,GAAG,IAAI,EAClB,QAAQ,GACR,GAAG,IAAI,CAAC;QAET,MAAM,QAAQ,GAA6B,EAAE,CAAC;QAE9C,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YAC5C,IAAI,YAAY,EAAE,EAAE,EAAE,CAAC;gBACtB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;gBACzC,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,aAAa,aAAa,IAAI,MAAM,CAAC;YAC7D,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAEpC,QAAQ,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,IAAI,GAAG,CAAC,eAAe,EAAE;oBACvD,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;iBACjF;aACD,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9C,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;gBACpF,QAAQ;aACR,CAAC,CAAC;YAEH,MAAM,GAAG,GAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACxD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YAExC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC;IACd,CAAC;IAEO,OAAO,CACd,MAAc,EACd,QAAkC,EAClC,QAAgD;QAEhD,IAAI,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACzD,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACzB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACvB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACzD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACjC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5D,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC;QACb,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;IAEO,WAAW,CAAC,GAAW;QAC9B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,OAAO,CAAC;QACnD,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;IAEO,GAAG,CAAC,IAAY,EAAE,GAAW;QACpC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IACjC,CAAC;CACD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Chrome lifecycle — launch and status checks.
3
+ * No CDP client attached during login phase.
4
+ *
5
+ * Platform-aware: resolves the Chrome binary for Linux, macOS, and Windows.
6
+ */
7
+ export declare function launch(url: string, profileName?: string): number;
8
+ export declare function getPageUrls(): string[];
9
+ export declare function pageUrlContains(substring: string): boolean;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Chrome lifecycle — launch and status checks.
3
+ * No CDP client attached during login phase.
4
+ *
5
+ * Platform-aware: resolves the Chrome binary for Linux, macOS, and Windows.
6
+ */
7
+ import { spawn, execSync } from "child_process";
8
+ import { existsSync, mkdirSync, rmSync } from "fs";
9
+ import { join } from "path";
10
+ import { config } from "../config.js";
11
+ function findChromeBinary() {
12
+ if (process.env.CHROME_PATH)
13
+ return process.env.CHROME_PATH;
14
+ const candidates = {
15
+ darwin: [
16
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
17
+ "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
18
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
19
+ ],
20
+ win32: [
21
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
22
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
23
+ ],
24
+ linux: [
25
+ "google-chrome-stable",
26
+ "google-chrome",
27
+ "chromium-browser",
28
+ "chromium",
29
+ ],
30
+ };
31
+ const platform = process.platform;
32
+ const paths = candidates[platform] ?? candidates.linux;
33
+ for (const p of paths) {
34
+ // Absolute paths: check file exists. Bare commands (linux): trust PATH.
35
+ if (p.includes("/") || p.includes("\\")) {
36
+ if (existsSync(p))
37
+ return p;
38
+ }
39
+ else {
40
+ try {
41
+ execSync(`which ${p}`, { stdio: "pipe" });
42
+ return p;
43
+ }
44
+ catch { }
45
+ }
46
+ }
47
+ throw new Error(`Chrome not found. Set CHROME_PATH or install Google Chrome.\n` +
48
+ `Searched: ${paths.join(", ")}`);
49
+ }
50
+ export function launch(url, profileName = "default") {
51
+ const profileDir = join(config.browser.profileDir, profileName);
52
+ mkdirSync(profileDir, { recursive: true });
53
+ // Remove stale lock files from previous runs
54
+ for (const lockFile of ["SingletonLock", "SingletonSocket", "SingletonCookie"]) {
55
+ try {
56
+ rmSync(join(profileDir, lockFile));
57
+ }
58
+ catch { }
59
+ try {
60
+ rmSync(join(profileDir, "Default", lockFile));
61
+ }
62
+ catch { }
63
+ }
64
+ const chromeBin = findChromeBinary();
65
+ const args = [
66
+ "--no-first-run",
67
+ "--no-default-browser-check",
68
+ "--disable-blink-features=AutomationControlled",
69
+ "--disable-infobars",
70
+ "--js-flags=--max-old-space-size=2048",
71
+ "--renderer-process-limit=4",
72
+ "--disable-background-networking",
73
+ `--user-data-dir=${profileDir}`,
74
+ "--profile-directory=Default",
75
+ "--window-size=1920,1080",
76
+ `--remote-debugging-port=${config.browser.cdpPort}`,
77
+ ];
78
+ // Docker/Linux-specific flags (sandbox + shared memory)
79
+ if (config.isDocker) {
80
+ args.unshift("--no-sandbox", "--disable-dev-shm-usage");
81
+ }
82
+ // Only add proxy if explicitly configured (not the default localhost:1080)
83
+ if (process.env.PROXY_HOST) {
84
+ args.push(`--proxy-server=${config.socks5Url}`);
85
+ }
86
+ args.push(url);
87
+ const proc = spawn(chromeBin, args, {
88
+ stdio: "ignore",
89
+ detached: true,
90
+ });
91
+ proc.unref();
92
+ return proc.pid;
93
+ }
94
+ export function getPageUrls() {
95
+ try {
96
+ const out = execSync(`curl -s http://127.0.0.1:${config.browser.cdpPort}/json/list`, { encoding: "utf-8", timeout: 5000 });
97
+ const pages = JSON.parse(out);
98
+ return pages.map((p) => p.url ?? "");
99
+ }
100
+ catch {
101
+ return [];
102
+ }
103
+ }
104
+ export function pageUrlContains(substring) {
105
+ return getPageUrls().some((url) => url.includes(substring));
106
+ }
107
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/x11/chrome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * X11 input simulation via xdotool + screenshots via ImageMagick.
3
+ *
4
+ * These go through the X Window System input pipeline — the same path
5
+ * a physical keyboard uses. Completely undetectable by in-page JavaScript.
6
+ */
7
+ export declare function typeText(text: string, delayMs?: number): void;
8
+ export declare function pressKey(key: string): void;
9
+ export declare function clickAt(x: number, y: number): void;
10
+ export declare function screenshot(savePath?: string): string;
11
+ export declare function focusWindow(...titles: string[]): string;
12
+ declare function sleep(ms: number): void;
13
+ export { sleep };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * X11 input simulation via xdotool + screenshots via ImageMagick.
3
+ *
4
+ * These go through the X Window System input pipeline — the same path
5
+ * a physical keyboard uses. Completely undetectable by in-page JavaScript.
6
+ */
7
+ import { execSync, execFileSync } from "child_process";
8
+ import { readFileSync } from "fs";
9
+ function sh(cmd, check = true) {
10
+ try {
11
+ return execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12
+ }
13
+ catch (e) {
14
+ if (check)
15
+ throw new Error(`cmd failed: ${cmd}\n${e.stderr ?? e.message}`);
16
+ return (e.stdout ?? "").toString().trim();
17
+ }
18
+ }
19
+ export function typeText(text, delayMs = 60) {
20
+ execFileSync("xdotool", ["type", "--delay", String(delayMs), "--", text]);
21
+ }
22
+ export function pressKey(key) {
23
+ execFileSync("xdotool", ["key", "--", key]);
24
+ sleep(300);
25
+ }
26
+ export function clickAt(x, y) {
27
+ // Get active window position to convert screenshot-relative coords to absolute
28
+ const wid = sh("xdotool getactivewindow 2>/dev/null || echo ''");
29
+ if (wid) {
30
+ const geo = sh(`xdotool getwindowgeometry --shell ${wid} 2>/dev/null || echo ""`);
31
+ const xMatch = geo.match(/X=(\d+)/);
32
+ const yMatch = geo.match(/Y=(\d+)/);
33
+ const offsetX = xMatch ? parseInt(xMatch[1]) : 0;
34
+ const offsetY = yMatch ? parseInt(yMatch[1]) : 0;
35
+ execFileSync("xdotool", ["mousemove", String(x + offsetX), String(y + offsetY), "click", "1"]);
36
+ }
37
+ else {
38
+ execFileSync("xdotool", ["mousemove", String(x), String(y), "click", "1"]);
39
+ }
40
+ sleep(300);
41
+ }
42
+ export function screenshot(savePath = "/tmp/x11_screenshot.png") {
43
+ const wid = sh("xdotool getactivewindow 2>/dev/null || echo ''");
44
+ const target = wid || "root";
45
+ try {
46
+ execSync(`import -window ${target} ${savePath}`, { stdio: "pipe" });
47
+ }
48
+ catch { }
49
+ return readFileSync(savePath).toString("base64");
50
+ }
51
+ export function focusWindow(...titles) {
52
+ for (let attempt = 0; attempt < 30; attempt++) {
53
+ // Try specific titles first (with --onlyvisible)
54
+ for (const title of titles) {
55
+ const wid = sh(`xdotool search --onlyvisible --name "${title}" 2>/dev/null | head -1 || true`, false).trim();
56
+ if (wid) {
57
+ sh(`xdotool windowactivate --sync ${wid}`, false);
58
+ return wid;
59
+ }
60
+ }
61
+ // Fallback: any Chrome window (drop --onlyvisible, it can race with WM)
62
+ const anyWid = sh("xdotool search --class chrome 2>/dev/null | head -1 || true", false).trim();
63
+ if (anyWid) {
64
+ sh(`xdotool windowactivate --sync ${anyWid}`, false);
65
+ return anyWid;
66
+ }
67
+ sleep(1000);
68
+ }
69
+ throw new Error(`Window not found: ${titles.join(", ")}`);
70
+ }
71
+ function sleep(ms) {
72
+ execSync(`sleep ${ms / 1000}`);
73
+ }
74
+ export { sleep };
75
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/x11/input.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Twitter/X login using the generalized X11Agent.
3
+ * Site-specific knowledge lives in the prompt. The agent is generic.
4
+ */
5
+ import { type Credentials } from "../config.js";
6
+ export declare function login(creds?: Credentials, startUrl?: string): Promise<boolean>;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Twitter/X login using the generalized X11Agent.
3
+ * Site-specific knowledge lives in the prompt. The agent is generic.
4
+ */
5
+ import { authenticator } from "otplib";
6
+ import { X11Agent } from "./agent.js";
7
+ import * as chrome from "./chrome.js";
8
+ import * as x11 from "./input.js";
9
+ import { config } from "../config.js";
10
+ function buildPrompt(creds) {
11
+ return `You are controlling a Chrome browser via X11 keyboard input to log into Twitter/X.
12
+ You see screenshots of the browser window. Based on what you see, tell me what action to take.
13
+
14
+ Login credentials:
15
+ - Username: ${creds.username}
16
+ - Email (for identity verification): ${creds.email}
17
+ - Password: ${creds.password}
18
+ - For 2FA/TOTP codes, say ACTION: INJECT TOTP and I'll provide a fresh code
19
+
20
+ IMPORTANT RULES:
21
+ - The login form INPUT FIELD usually has focus already. Just TYPE the username — don't click first.
22
+ - If you see a login form, TYPE the username immediately. Do NOT wait, do NOT click.
23
+ - After typing, press KEY Return to submit (equivalent to clicking Next/Log in).
24
+ - If you see "Enter your phone number or email address" verification, TYPE the email, then KEY Return.
25
+ - If you see a password field, TYPE the password, then KEY Return.
26
+ - If you see a 2FA/verification code field, use INJECT TOTP, then KEY Return.
27
+ - Use ONLY keyboard actions. You cannot click — only type and press keys.
28
+ - After typing a username/email/password, press KEY Return to submit the form.
29
+ - To move between fields, use KEY Tab.
30
+ - Only use WAIT if the page is clearly mid-transition (spinner, completely blank).
31
+ - NEVER wait more than 3 seconds. NEVER wait more than 2 times in a row.
32
+
33
+ Available actions (ONE per message, nothing else):
34
+ ACTION: TYPE text_to_type
35
+ ACTION: KEY keyname (Return, Tab, Escape)
36
+ ACTION: WAIT seconds (max 3)
37
+ ACTION: INJECT TOTP
38
+ ACTION: DONE (when you see the home timeline/feed)
39
+ ACTION: FAILED reason`;
40
+ }
41
+ export async function login(creds = config.credentials, startUrl = "https://x.com/login") {
42
+ console.log("=".repeat(50));
43
+ console.log("X11 Login");
44
+ console.log(` user: ${creds.username}`);
45
+ console.log(` url: ${startUrl}`);
46
+ console.log("=".repeat(50));
47
+ const pid = chrome.launch(startUrl, creds.username);
48
+ console.log(`chrome pid=${pid}`);
49
+ console.log("waiting for Chrome + page load...");
50
+ // Wait for CDP to be responsive first, then wait for page
51
+ for (let i = 0; i < 30; i++) {
52
+ if (chrome.getPageUrls().length > 0)
53
+ break;
54
+ x11.sleep(1000);
55
+ }
56
+ x11.sleep(5000);
57
+ x11.focusWindow("Log in", "X", "x.com", "Twitter", "Google Chrome");
58
+ x11.sleep(2000);
59
+ const agent = new X11Agent();
60
+ return agent.run({
61
+ systemPrompt: buildPrompt(creds),
62
+ successCheck: () => chrome.pageUrlContains("/home"),
63
+ onAction: (type, value) => {
64
+ if (type === "inject" && value === "TOTP") {
65
+ const code = authenticator.generate(creds.totpKey);
66
+ console.log(` totp: ${code}`);
67
+ x11.typeText(code, 100);
68
+ }
69
+ },
70
+ });
71
+ }
72
+ // CLI entrypoint
73
+ if (process.argv[1]?.endsWith("login.ts") || process.argv[1]?.endsWith("login.js")) {
74
+ login().then((ok) => process.exit(ok ? 0 : 1));
75
+ }
76
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/x11/login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,MAAM,EAAoB,MAAM,cAAc,CAAC;AAExD,SAAS,WAAW,CAAC,KAAkB;IACtC,OAAO;;;;cAIM,KAAK,CAAC,QAAQ;uCACW,KAAK,CAAC,KAAK;cACpC,KAAK,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;sBAsBN,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAC1B,QAAqB,MAAM,CAAC,WAAW,EACvC,QAAQ,GAAG,qBAAqB;IAEhC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,WAAW,QAAQ,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACjD,0DAA0D;IAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM;QAC3C,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChB,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IACpE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEhB,MAAM,KAAK,GAAG,IAAI,QAAQ,EAAE,CAAC;IAE7B,OAAO,KAAK,CAAC,GAAG,CAAC;QAChB,YAAY,EAAE,WAAW,CAAC,KAAK,CAAC;QAChC,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC;QACnD,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACnD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;gBAC/B,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACzB,CAAC;QACF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,iBAAiB;AACjB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;IACpF,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "browser-autopilot",
3
+ "version": "0.1.0",
4
+ "description": "Autonomous browser agent — LLM-driven CDP automation with X11 stealth fallback, CAPTCHA solving, and extensible skills",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./x11/chrome": {
14
+ "types": "./dist/x11/chrome.d.ts",
15
+ "import": "./dist/x11/chrome.js"
16
+ },
17
+ "./browser/cdp": {
18
+ "types": "./dist/browser/cdp.d.ts",
19
+ "import": "./dist/browser/cdp.js"
20
+ },
21
+ "./agent/loop": {
22
+ "types": "./dist/agent/loop.d.ts",
23
+ "import": "./dist/agent/loop.js"
24
+ },
25
+ "./captcha/solver": {
26
+ "types": "./dist/captcha/solver.d.ts",
27
+ "import": "./dist/captcha/solver.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "login": "tsx src/x11/login.ts",
36
+ "agent": "tsx src/agent/run.ts",
37
+ "start": "tsx src/index.ts",
38
+ "dev": "tsx watch src/index.ts",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "prepublishOnly": "npm run build"
42
+ },
43
+ "keywords": [
44
+ "browser",
45
+ "automation",
46
+ "agent",
47
+ "cdp",
48
+ "chrome",
49
+ "llm",
50
+ "ai",
51
+ "web-agent",
52
+ "browser-use",
53
+ "autonomous",
54
+ "scraping",
55
+ "captcha"
56
+ ],
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/Layr-Labs/encumbrance-exp.git"
60
+ },
61
+ "license": "MIT",
62
+ "dependencies": {
63
+ "@ai-sdk/anthropic": "^1",
64
+ "@anthropic-ai/sdk": "^0.40",
65
+ "eigenmail-sdk": "^0.1.1",
66
+ "ai": "^4",
67
+ "chrome-remote-interface": "^0.33",
68
+ "ethers": "^6.16.0",
69
+ "otplib": "^12.0.1",
70
+ "sharp": "^0.33",
71
+ "zod": "^3.22"
72
+ },
73
+ "devDependencies": {
74
+ "@types/node": "^22.19.13",
75
+ "tsx": "^4.6",
76
+ "typescript": "^5.5",
77
+ "vitest": "^3.2.4"
78
+ }
79
+ }