browse-agent-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js ADDED
@@ -0,0 +1,218 @@
1
+ import { execSync } from "node:child_process";
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ //#region src/scripts/config.ts
6
+ const CLI_RUNTIME_DIR = dirname(fileURLToPath(import.meta.url));
7
+ const HOME = process.env.HOME || process.env.USERPROFILE || "";
8
+ const BROWSER_PATHS = {
9
+ chrome: {
10
+ darwin: ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"],
11
+ linux: ["google-chrome", "google-chrome-stable"],
12
+ win32: [
13
+ `${process.env.PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe`,
14
+ `${process.env["PROGRAMFILES(X86)"]}\\Google\\Chrome\\Application\\chrome.exe`,
15
+ `${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`
16
+ ],
17
+ aix: [],
18
+ android: [],
19
+ freebsd: [],
20
+ haiku: [],
21
+ openbsd: [],
22
+ sunos: [],
23
+ cygwin: [],
24
+ netbsd: []
25
+ },
26
+ chromium: {
27
+ darwin: ["/Applications/Chromium.app/Contents/MacOS/Chromium"],
28
+ linux: ["chromium-browser", "chromium"],
29
+ win32: [`${process.env.LOCALAPPDATA}\\Chromium\\Application\\chrome.exe`],
30
+ aix: [],
31
+ android: [],
32
+ freebsd: [],
33
+ haiku: [],
34
+ openbsd: [],
35
+ sunos: [],
36
+ cygwin: [],
37
+ netbsd: []
38
+ },
39
+ edge: {
40
+ darwin: ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"],
41
+ linux: ["microsoft-edge", "microsoft-edge-stable"],
42
+ win32: [`${process.env.PROGRAMFILES}\\Microsoft\\Edge\\Application\\msedge.exe`, `${process.env["PROGRAMFILES(X86)"]}\\Microsoft\\Edge\\Application\\msedge.exe`],
43
+ aix: [],
44
+ android: [],
45
+ freebsd: [],
46
+ haiku: [],
47
+ openbsd: [],
48
+ sunos: [],
49
+ cygwin: [],
50
+ netbsd: []
51
+ },
52
+ brave: {
53
+ darwin: ["/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],
54
+ linux: ["brave-browser", "brave"],
55
+ win32: [`${process.env.PROGRAMFILES}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`, `${process.env.LOCALAPPDATA}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`],
56
+ aix: [],
57
+ android: [],
58
+ freebsd: [],
59
+ haiku: [],
60
+ openbsd: [],
61
+ sunos: [],
62
+ cygwin: [],
63
+ netbsd: []
64
+ }
65
+ };
66
+ const BASE_DIR = join(CLI_RUNTIME_DIR, ".browse-agent");
67
+ const DEFAULT_SERVICE_PORT = Number(process.env.BROWSE_AGENT_SERVICE_PORT || 9316);
68
+ async function importSdk() {
69
+ try {
70
+ return await import("browse-agent-sdk");
71
+ } catch {}
72
+ const globalSdk = join(dirname(CLI_RUNTIME_DIR), "node_modules", "browse-agent-sdk");
73
+ if (existsSync(globalSdk)) {
74
+ const pkg = JSON.parse(readFileSync(join(globalSdk, "package.json"), "utf8"));
75
+ return await import(pathToFileURL(join(globalSdk, pkg.module || pkg.main || "index.js")).href);
76
+ }
77
+ throw new Error("browse-agent-sdk not found. Run \"setup\" first.");
78
+ }
79
+ const DEFAULT_PROFILE_PATHS = {
80
+ chrome: {
81
+ darwin: join(HOME, "Library", "Application Support", "Google", "Chrome"),
82
+ linux: join(HOME, ".config", "google-chrome"),
83
+ win32: join(process.env.LOCALAPPDATA || "", "Google", "Chrome", "User Data"),
84
+ aix: "",
85
+ android: "",
86
+ freebsd: "",
87
+ haiku: "",
88
+ openbsd: "",
89
+ sunos: "",
90
+ cygwin: "",
91
+ netbsd: ""
92
+ },
93
+ chromium: {
94
+ darwin: join(HOME, "Library", "Application Support", "Chromium"),
95
+ linux: join(HOME, ".config", "chromium"),
96
+ win32: join(process.env.LOCALAPPDATA || "", "Chromium", "User Data"),
97
+ aix: "",
98
+ android: "",
99
+ freebsd: "",
100
+ haiku: "",
101
+ openbsd: "",
102
+ sunos: "",
103
+ cygwin: "",
104
+ netbsd: ""
105
+ },
106
+ edge: {
107
+ darwin: join(HOME, "Library", "Application Support", "Microsoft Edge"),
108
+ linux: join(HOME, ".config", "microsoft-edge"),
109
+ win32: join(process.env.LOCALAPPDATA || "", "Microsoft", "Edge", "User Data"),
110
+ aix: "",
111
+ android: "",
112
+ freebsd: "",
113
+ haiku: "",
114
+ openbsd: "",
115
+ sunos: "",
116
+ cygwin: "",
117
+ netbsd: ""
118
+ },
119
+ brave: {
120
+ darwin: join(HOME, "Library", "Application Support", "BraveSoftware", "Brave-Browser"),
121
+ linux: join(HOME, ".config", "BraveSoftware", "Brave-Browser"),
122
+ win32: join(process.env.LOCALAPPDATA || "", "BraveSoftware", "Brave-Browser", "User Data"),
123
+ aix: "",
124
+ android: "",
125
+ freebsd: "",
126
+ haiku: "",
127
+ openbsd: "",
128
+ sunos: "",
129
+ cygwin: "",
130
+ netbsd: ""
131
+ }
132
+ };
133
+ function saveSession(data) {
134
+ mkdirSync(BASE_DIR, { recursive: true });
135
+ writeFileSync(join(BASE_DIR, "_session.json"), JSON.stringify(data, null, 2));
136
+ }
137
+ function loadSession() {
138
+ const stateFile = join(BASE_DIR, "_session.json");
139
+ if (!existsSync(stateFile)) return null;
140
+ return JSON.parse(readFileSync(stateFile, "utf8"));
141
+ }
142
+ function clearSession() {
143
+ const stateFile = join(BASE_DIR, "_session.json");
144
+ if (existsSync(stateFile)) rmSync(stateFile);
145
+ }
146
+ function saveServiceSession(data) {
147
+ mkdirSync(BASE_DIR, { recursive: true });
148
+ writeFileSync(join(BASE_DIR, "_service.json"), JSON.stringify(data, null, 2));
149
+ }
150
+ function loadServiceSession() {
151
+ const stateFile = join(BASE_DIR, "_service.json");
152
+ if (!existsSync(stateFile)) return null;
153
+ return JSON.parse(readFileSync(stateFile, "utf8"));
154
+ }
155
+ function clearServiceSession() {
156
+ const stateFile = join(BASE_DIR, "_service.json");
157
+ if (existsSync(stateFile)) rmSync(stateFile);
158
+ }
159
+ function findBrowser(browser) {
160
+ if (process.env.CHROME_PATH) return process.env.CHROME_PATH;
161
+ const paths = BROWSER_PATHS[browser];
162
+ if (!paths) throw new Error(`Unknown browser: ${browser}. Use: chrome, chromium, edge, brave`);
163
+ const isAbsolute = (path) => path.startsWith("/") || /^[a-zA-Z]:\\/.test(path);
164
+ const whichCmd = process.platform === "win32" ? "where" : "which";
165
+ for (const path of paths[process.platform] || []) try {
166
+ if (isAbsolute(path)) {
167
+ if (existsSync(path)) return path;
168
+ } else {
169
+ const resolved = execSync(`${whichCmd} "${path}"`, { stdio: "pipe" }).toString().trim().split("\n")[0];
170
+ if (resolved) return resolved;
171
+ }
172
+ } catch {}
173
+ if (browser !== "edge") {
174
+ console.error(`[browse-agent] ${browser} not found, falling back to edge...`);
175
+ return findBrowser("edge");
176
+ }
177
+ throw new Error("No supported browser found. Set CHROME_PATH env variable or install Chrome/Edge.");
178
+ }
179
+ function getProfileDir(browser, useUserProfile) {
180
+ if (useUserProfile) {
181
+ const profilePath = DEFAULT_PROFILE_PATHS[browser]?.[process.platform];
182
+ if (profilePath && existsSync(profilePath)) return profilePath;
183
+ console.error(`[browse-agent] Default ${browser} profile not found at ${profilePath}`);
184
+ console.error("[browse-agent] Falling back to isolated profile.");
185
+ }
186
+ const dir = join(BASE_DIR, "chrome-profile");
187
+ mkdirSync(dir, { recursive: true });
188
+ return dir;
189
+ }
190
+ function patchExtension(port, secret) {
191
+ const extensionSrc = join(BASE_DIR, "extension");
192
+ const extensionWork = join(BASE_DIR, "_ext_work");
193
+ if (!existsSync(join(extensionSrc, "manifest.json"))) throw new Error(`Extension is not installed at ${extensionSrc}. Run "browse-agent setup" first.`);
194
+ if (existsSync(extensionWork)) rmSync(extensionWork, { recursive: true });
195
+ cpSync(extensionSrc, extensionWork, { recursive: true });
196
+ const swPath = join(extensionWork, "service-worker.js");
197
+ const originalSW = readFileSync(swPath, "utf8");
198
+ writeFileSync(swPath, [`chrome.storage.local.set({ wsUrl: 'ws://127.0.0.1:${port}', secret: '${secret}' });`, originalSW].join("\n"));
199
+ return extensionWork;
200
+ }
201
+ function cleanExtensionWork() {
202
+ const extensionWork = join(BASE_DIR, "_ext_work");
203
+ if (existsSync(extensionWork)) rmSync(extensionWork, { recursive: true });
204
+ }
205
+ function resolveOptions(options = {}) {
206
+ return {
207
+ browser: options.browser ?? process.env.BROWSER ?? "chrome",
208
+ headless: options.headless ?? process.env.HEADLESS === "true",
209
+ useUserProfile: options.useUserProfile ?? process.env.USE_USER_PROFILE === "true",
210
+ port: options.port ?? Number(process.env.BROWSE_AGENT_PORT || 9315),
211
+ timeout: options.timeout ?? Number(process.env.CONNECTION_TIMEOUT || 3e4),
212
+ secret: options.secret ?? process.env.SHARED_SECRET ?? ""
213
+ };
214
+ }
215
+ //#endregion
216
+ export { clearSession as a, importSdk as c, patchExtension as d, resolveOptions as f, clearServiceSession as i, loadServiceSession as l, saveSession as m, DEFAULT_SERVICE_PORT as n, findBrowser as o, saveServiceSession as p, cleanExtensionWork as r, getProfileDir as s, BASE_DIR as t, loadSession as u };
217
+
218
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,113 @@
1
+ import "browse-agent-sdk";
2
+
3
+ //#region src/scripts/config.d.ts
4
+ type BrowserName = 'chrome' | 'chromium' | 'edge' | 'brave';
5
+ interface SessionData {
6
+ pid?: number;
7
+ port: number;
8
+ secret: string;
9
+ browser: BrowserName;
10
+ profileDir: string;
11
+ extensionWork: string;
12
+ startedAt: string;
13
+ _agent?: any;
14
+ _proc?: any;
15
+ }
16
+ //#endregion
17
+ //#region src/scripts/launch-browser.d.ts
18
+ interface LaunchOptions {
19
+ browser?: 'chrome' | 'chromium' | 'edge' | 'brave';
20
+ headless?: boolean;
21
+ useUserProfile?: boolean;
22
+ port?: number;
23
+ timeout?: number;
24
+ secret?: string;
25
+ }
26
+ declare function launchBrowser(options?: LaunchOptions): Promise<SessionData>;
27
+ //#endregion
28
+ //#region src/scripts/connect.d.ts
29
+ interface ConnectOptions {
30
+ port?: number;
31
+ secret?: string;
32
+ timeout?: number;
33
+ }
34
+ declare function connect(options?: ConnectOptions): Promise<any>;
35
+ //#endregion
36
+ //#region src/scripts/close-browser.d.ts
37
+ declare function closeBrowser(agent?: {
38
+ stop?: () => Promise<void>;
39
+ }): Promise<void>;
40
+ //#endregion
41
+ //#region src/scripts/clear.d.ts
42
+ declare function clear(): Promise<void>;
43
+ //#endregion
44
+ //#region src/scripts/navigate.d.ts
45
+ declare function navigate(agent: any, url: string, options?: Record<string, unknown>): Promise<unknown>;
46
+ //#endregion
47
+ //#region src/scripts/get-content.d.ts
48
+ interface GetContentOptions {
49
+ format?: string;
50
+ tabId?: number;
51
+ }
52
+ declare function getContent(agent: any, options?: GetContentOptions): Promise<unknown>;
53
+ //#endregion
54
+ //#region src/scripts/get-dom.d.ts
55
+ interface GetDomOptions {
56
+ property?: string;
57
+ all?: boolean;
58
+ tabId?: number;
59
+ }
60
+ declare function getDOM(agent: any, selector: string, options?: GetDomOptions): Promise<unknown>;
61
+ //#endregion
62
+ //#region src/scripts/evaluate.d.ts
63
+ interface EvaluateOptions {
64
+ tabId?: number;
65
+ }
66
+ declare function evaluate(agent: any, expression: string, options?: EvaluateOptions): Promise<unknown>;
67
+ //#endregion
68
+ //#region src/scripts/inject-script.d.ts
69
+ interface InjectScriptOptions {
70
+ tabId?: number;
71
+ }
72
+ declare function injectScript(agent: any, code: string, options?: InjectScriptOptions): Promise<unknown>;
73
+ //#endregion
74
+ //#region src/scripts/inject-css.d.ts
75
+ interface InjectCssOptions {
76
+ tabId?: number;
77
+ }
78
+ declare function injectCSS(agent: any, code: string, options?: InjectCssOptions): Promise<unknown>;
79
+ //#endregion
80
+ //#region src/scripts/screenshot.d.ts
81
+ interface ScreenshotClip {
82
+ x: number;
83
+ y: number;
84
+ width: number;
85
+ height: number;
86
+ }
87
+ interface ScreenshotOptions {
88
+ format?: string;
89
+ quality?: number;
90
+ tabId?: number;
91
+ clip?: ScreenshotClip;
92
+ }
93
+ declare function screenshot(agent: any, mode?: string, options?: ScreenshotOptions): Promise<unknown>;
94
+ //#endregion
95
+ //#region src/scripts/tabs.d.ts
96
+ declare function listTabs(agent: any): Promise<unknown>;
97
+ declare function closeTab(agent: any, tabId: number): Promise<void>;
98
+ declare function activateTab(agent: any, tabId: number): Promise<void>;
99
+ //#endregion
100
+ //#region src/scripts/browse.d.ts
101
+ interface BrowseOptions {
102
+ browser?: 'chrome' | 'chromium' | 'edge' | 'brave';
103
+ headless?: boolean;
104
+ useUserProfile?: boolean;
105
+ port?: number;
106
+ timeout?: number;
107
+ secret?: string;
108
+ printResult?: boolean;
109
+ }
110
+ declare function browse(task: (agent: any) => Promise<unknown>, options?: BrowseOptions): Promise<unknown>;
111
+ //#endregion
112
+ export { activateTab, browse, clear, closeBrowser, closeTab, connect, evaluate, getContent, getDOM, injectCSS, injectScript, launchBrowser, listTabs, navigate, screenshot };
113
+ //# sourceMappingURL=script.d.ts.map
package/dist/script.js ADDED
@@ -0,0 +1,53 @@
1
+ import { c as importSdk, f as resolveOptions, r as cleanExtensionWork, u as loadSession } from "./config.js";
2
+ import { t as clear } from "./clear.js";
3
+ import { a as injectCSS, c as getDOM, d as closeBrowser, f as launchBrowser, i as screenshot, l as getContent, n as closeTab, o as injectScript, r as listTabs, s as evaluate, t as activateTab, u as navigate } from "./tabs.js";
4
+ //#region src/scripts/connect.ts
5
+ async function connect(options = {}) {
6
+ const { BrowserAgent } = await importSdk();
7
+ const session = loadSession();
8
+ const port = options.port ?? session?.port ?? resolveOptions().port;
9
+ const secret = options.secret ?? session?.secret ?? resolveOptions().secret;
10
+ const timeout = options.timeout ?? resolveOptions().timeout;
11
+ const agent = new BrowserAgent({
12
+ secret,
13
+ port
14
+ });
15
+ await agent.start();
16
+ console.error(`[browse-agent] Connecting on port ${port}...`);
17
+ await agent.waitForConnection(timeout);
18
+ console.error("[browse-agent] Extension connected");
19
+ return agent;
20
+ }
21
+ //#endregion
22
+ //#region src/scripts/browse.ts
23
+ async function browse(task, options = {}) {
24
+ const opts = resolveOptions(options);
25
+ let session;
26
+ let agent;
27
+ let taskResult;
28
+ try {
29
+ session = await launchBrowser(opts);
30
+ agent = session._agent;
31
+ await agent.waitForConnection(opts.timeout);
32
+ console.error("[browse-agent] Extension connected");
33
+ taskResult = await task(agent);
34
+ if (options.printResult !== false && taskResult !== void 0) console.log(JSON.stringify(taskResult, null, 2));
35
+ } catch (err) {
36
+ const message = err instanceof Error ? err.message : String(err);
37
+ console.error("[browse-agent] Error:", message);
38
+ process.exit(1);
39
+ } finally {
40
+ if (session?._proc) try {
41
+ session._proc.kill();
42
+ } catch {}
43
+ if (agent) try {
44
+ await agent.stop();
45
+ } catch {}
46
+ cleanExtensionWork();
47
+ }
48
+ return taskResult;
49
+ }
50
+ //#endregion
51
+ export { activateTab, browse, clear, closeBrowser, closeTab, connect, evaluate, getContent, getDOM, injectCSS, injectScript, launchBrowser, listTabs, navigate, screenshot };
52
+
53
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,156 @@
1
+ import { f as resolveOptions, i as clearServiceSession, n as DEFAULT_SERVICE_PORT, p as saveServiceSession } from "./config.js";
2
+ import { a as injectCSS, c as getDOM, d as closeBrowser, f as launchBrowser, i as screenshot, l as getContent, n as closeTab, o as injectScript, r as listTabs, s as evaluate, t as activateTab, u as navigate } from "./tabs.js";
3
+ import { createServer } from "node:http";
4
+ //#region src/service.ts
5
+ function toCommandRequest(value) {
6
+ const name = value.name;
7
+ if (typeof name !== "string" || name.length === 0) throw new Error("Invalid command request: \"name\" must be a non-empty string.");
8
+ const payloadRaw = value.payload;
9
+ return {
10
+ name,
11
+ payload: payloadRaw !== void 0 && payloadRaw !== null && typeof payloadRaw === "object" ? payloadRaw : void 0
12
+ };
13
+ }
14
+ let currentSession = null;
15
+ let currentAgent = null;
16
+ function getServicePort() {
17
+ const index = process.argv.findIndex((arg) => arg === "--service-port");
18
+ if (index !== -1 && process.argv[index + 1]) {
19
+ const parsed = Number(process.argv[index + 1]);
20
+ if (!Number.isNaN(parsed)) return parsed;
21
+ }
22
+ return DEFAULT_SERVICE_PORT;
23
+ }
24
+ function writeJson(res, status, body) {
25
+ res.statusCode = status;
26
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
27
+ res.end(JSON.stringify(body));
28
+ }
29
+ async function readJson(req) {
30
+ const chunks = [];
31
+ for await (const chunk of req) chunks.push(Buffer.from(chunk));
32
+ if (chunks.length === 0) return {};
33
+ const raw = Buffer.concat(chunks).toString("utf8");
34
+ if (!raw) return {};
35
+ return JSON.parse(raw);
36
+ }
37
+ function sessionInfo(session) {
38
+ if (!session) return { running: false };
39
+ const { _agent, _proc, ...info } = session;
40
+ return {
41
+ running: true,
42
+ ...info
43
+ };
44
+ }
45
+ async function handleLaunch(body) {
46
+ if (currentSession && currentAgent) return sessionInfo(currentSession);
47
+ const options = {
48
+ browser: body.browser,
49
+ headless: body.headless,
50
+ port: body.port,
51
+ timeout: body.timeout,
52
+ secret: body.secret
53
+ };
54
+ const session = await launchBrowser(options);
55
+ currentSession = session;
56
+ currentAgent = session._agent;
57
+ const timeout = resolveOptions(options).timeout;
58
+ await currentAgent.waitForConnection(timeout);
59
+ return sessionInfo(session);
60
+ }
61
+ async function handleCommand(body) {
62
+ if (!currentAgent) throw new Error("Service is running but browser session is not started. Run \"browse-agent launch\" first.");
63
+ const payload = body.payload ?? {};
64
+ switch (body.name) {
65
+ case "navigate": return navigate(currentAgent, String(payload.url), payload.options ?? {});
66
+ case "get-content": return getContent(currentAgent, payload.options ?? {});
67
+ case "get-dom": return getDOM(currentAgent, String(payload.selector), payload.options ?? {});
68
+ case "evaluate": return evaluate(currentAgent, String(payload.expression), payload.options ?? {});
69
+ case "inject-script": return injectScript(currentAgent, String(payload.code), payload.options ?? {});
70
+ case "inject-css": return injectCSS(currentAgent, String(payload.code), payload.options ?? {});
71
+ case "screenshot": return screenshot(currentAgent, String(payload.mode ?? "visible"), payload.options ?? {});
72
+ case "tabs-list": return listTabs(currentAgent);
73
+ case "tabs-close":
74
+ await closeTab(currentAgent, Number(payload.tabId));
75
+ return { closed: Number(payload.tabId) };
76
+ case "tabs-activate":
77
+ await activateTab(currentAgent, Number(payload.tabId));
78
+ return { activated: Number(payload.tabId) };
79
+ default: throw new Error(`Unknown command: ${body.name}`);
80
+ }
81
+ }
82
+ async function stopBrowserSession() {
83
+ if (!currentSession && !currentAgent) return;
84
+ await closeBrowser(currentAgent ?? void 0);
85
+ currentSession = null;
86
+ currentAgent = null;
87
+ }
88
+ const servicePort = getServicePort();
89
+ const server = createServer(async (req, res) => {
90
+ try {
91
+ const method = req.method ?? "GET";
92
+ const url = req.url ?? "/";
93
+ if (method === "GET" && url === "/health") {
94
+ writeJson(res, 200, {
95
+ ok: true,
96
+ servicePort,
97
+ pid: process.pid
98
+ });
99
+ return;
100
+ }
101
+ if (method === "GET" && url === "/status") {
102
+ writeJson(res, 200, {
103
+ servicePort,
104
+ pid: process.pid,
105
+ ...sessionInfo(currentSession)
106
+ });
107
+ return;
108
+ }
109
+ if (method === "POST" && url === "/launch") {
110
+ writeJson(res, 200, await handleLaunch(await readJson(req)));
111
+ return;
112
+ }
113
+ if (method === "POST" && url === "/command") {
114
+ writeJson(res, 200, await handleCommand(toCommandRequest(await readJson(req))));
115
+ return;
116
+ }
117
+ if (method === "POST" && url === "/close") {
118
+ await stopBrowserSession();
119
+ writeJson(res, 200, { closed: true });
120
+ return;
121
+ }
122
+ if (method === "POST" && url === "/shutdown") {
123
+ await stopBrowserSession();
124
+ clearServiceSession();
125
+ writeJson(res, 200, { stopped: true });
126
+ server.close(() => {
127
+ process.exit(0);
128
+ });
129
+ return;
130
+ }
131
+ writeJson(res, 404, { error: `Unknown route: ${method} ${url}` });
132
+ } catch (err) {
133
+ writeJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
134
+ }
135
+ });
136
+ server.listen(servicePort, "127.0.0.1", () => {
137
+ saveServiceSession({
138
+ pid: process.pid,
139
+ servicePort,
140
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
141
+ });
142
+ });
143
+ process.on("SIGINT", async () => {
144
+ await stopBrowserSession();
145
+ clearServiceSession();
146
+ process.exit(0);
147
+ });
148
+ process.on("SIGTERM", async () => {
149
+ await stopBrowserSession();
150
+ clearServiceSession();
151
+ process.exit(0);
152
+ });
153
+ //#endregion
154
+ export {};
155
+
156
+ //# sourceMappingURL=service.js.map
package/dist/tabs.js ADDED
@@ -0,0 +1,126 @@
1
+ import { a as clearSession, c as importSdk, d as patchExtension, f as resolveOptions, m as saveSession, o as findBrowser, r as cleanExtensionWork, s as getProfileDir, u as loadSession } from "./config.js";
2
+ import { spawn } from "node:child_process";
3
+ //#region src/scripts/launch-browser.ts
4
+ async function launchBrowser(options = {}) {
5
+ const { BrowserAgent } = await importSdk();
6
+ const opts = resolveOptions(options);
7
+ const extensionWork = patchExtension(opts.port, opts.secret);
8
+ const profileDir = getProfileDir(opts.browser, opts.useUserProfile ?? true);
9
+ const agent = new BrowserAgent({
10
+ secret: opts.secret,
11
+ port: opts.port
12
+ });
13
+ await agent.start();
14
+ console.error(`[browse-agent] Server started on port ${opts.port}`);
15
+ const launchArgs = [
16
+ `--load-extension=${extensionWork}`,
17
+ "--no-first-run",
18
+ "--no-default-browser-check"
19
+ ];
20
+ if (opts.headless) launchArgs.push("--headless=new");
21
+ const proc = spawn(findBrowser(opts.browser), launchArgs, {
22
+ stdio: "ignore",
23
+ detached: true
24
+ });
25
+ proc.on("error", (err) => {
26
+ console.error(`[browse-agent] ${opts.browser} launch failed:`, err.message);
27
+ process.exit(1);
28
+ });
29
+ proc.unref();
30
+ const session = {
31
+ pid: proc.pid,
32
+ port: opts.port,
33
+ secret: opts.secret,
34
+ browser: opts.browser,
35
+ profileDir,
36
+ extensionWork,
37
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
38
+ _agent: agent,
39
+ _proc: proc
40
+ };
41
+ saveSession(session);
42
+ console.error(`[browse-agent] Browser launched (PID: ${proc.pid})`);
43
+ return session;
44
+ }
45
+ //#endregion
46
+ //#region src/scripts/close-browser.ts
47
+ async function closeBrowser(agent) {
48
+ const session = loadSession();
49
+ if (session?.pid) try {
50
+ process.kill(session.pid);
51
+ console.error(`[browse-agent] Browser (PID: ${session.pid}) killed`);
52
+ } catch (err) {
53
+ const killErr = err;
54
+ if (killErr.code === "ESRCH") console.error(`[browse-agent] Browser (PID: ${session.pid}) already exited`);
55
+ else console.error("[browse-agent] Failed to kill browser:", killErr.message);
56
+ }
57
+ if (agent?.stop) try {
58
+ await agent.stop();
59
+ } catch {}
60
+ cleanExtensionWork();
61
+ clearSession();
62
+ console.error("[browse-agent] Session cleaned up");
63
+ }
64
+ //#endregion
65
+ //#region src/scripts/navigate.ts
66
+ async function navigate(agent, url, options = {}) {
67
+ return await agent.navigate(url, options);
68
+ }
69
+ //#endregion
70
+ //#region src/scripts/get-content.ts
71
+ async function getContent(agent, options = {}) {
72
+ return await agent.getContent(options);
73
+ }
74
+ //#endregion
75
+ //#region src/scripts/get-dom.ts
76
+ async function getDOM(agent, selector, options = {}) {
77
+ return await agent.getDOM(selector, options);
78
+ }
79
+ //#endregion
80
+ //#region src/scripts/evaluate.ts
81
+ async function evaluate(agent, expression, options = {}) {
82
+ return await agent.evaluate(expression, options.tabId);
83
+ }
84
+ //#endregion
85
+ //#region src/scripts/inject-script.ts
86
+ async function injectScript(agent, code, options = {}) {
87
+ return await agent.injectScript(code, options.tabId);
88
+ }
89
+ //#endregion
90
+ //#region src/scripts/inject-css.ts
91
+ async function injectCSS(agent, code, options = {}) {
92
+ return await agent.injectCSS(code, options.tabId);
93
+ }
94
+ //#endregion
95
+ //#region src/scripts/screenshot.ts
96
+ async function screenshot(agent, mode = "visible", options = {}) {
97
+ let result;
98
+ switch (mode) {
99
+ case "fullPage":
100
+ result = await agent.screenshotFullPage(options);
101
+ break;
102
+ case "area":
103
+ if (!options.clip) throw new Error("clip option required for area screenshot");
104
+ result = await agent.screenshotArea(options.clip, options);
105
+ break;
106
+ default:
107
+ result = await agent.screenshotVisible(options);
108
+ break;
109
+ }
110
+ return result;
111
+ }
112
+ //#endregion
113
+ //#region src/scripts/tabs.ts
114
+ async function listTabs(agent) {
115
+ return await agent.listTabs();
116
+ }
117
+ async function closeTab(agent, tabId) {
118
+ await agent.closeTab(tabId);
119
+ }
120
+ async function activateTab(agent, tabId) {
121
+ await agent.activateTab(tabId);
122
+ }
123
+ //#endregion
124
+ export { injectCSS as a, getDOM as c, closeBrowser as d, launchBrowser as f, screenshot as i, getContent as l, closeTab as n, injectScript as o, listTabs as r, evaluate as s, activateTab as t, navigate as u };
125
+
126
+ //# sourceMappingURL=tabs.js.map