browsy-ai 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.
@@ -0,0 +1,70 @@
1
+ import type { BrowsyResponse } from "./types.js";
2
+ /**
3
+ * HTTP client for the browsy REST API.
4
+ * Uses Node.js built-in fetch(). Passes and extracts X-Browsy-Session header.
5
+ */
6
+ export declare class BrowsyClient {
7
+ private baseUrl;
8
+ constructor(port: number);
9
+ /** GET /health */
10
+ health(): Promise<BrowsyResponse>;
11
+ /** POST /api/browse */
12
+ browse(params: {
13
+ url: string;
14
+ format?: string;
15
+ scope?: string;
16
+ }, session?: string): Promise<BrowsyResponse>;
17
+ /** POST /api/click */
18
+ click(params: {
19
+ id: number;
20
+ }, session?: string): Promise<BrowsyResponse>;
21
+ /** POST /api/type */
22
+ typeText(params: {
23
+ id: number;
24
+ text: string;
25
+ }, session?: string): Promise<BrowsyResponse>;
26
+ /** POST /api/check */
27
+ check(params: {
28
+ id: number;
29
+ }, session?: string): Promise<BrowsyResponse>;
30
+ /** POST /api/uncheck */
31
+ uncheck(params: {
32
+ id: number;
33
+ }, session?: string): Promise<BrowsyResponse>;
34
+ /** POST /api/select */
35
+ select(params: {
36
+ id: number;
37
+ value: string;
38
+ }, session?: string): Promise<BrowsyResponse>;
39
+ /** POST /api/search */
40
+ search(params: {
41
+ query: string;
42
+ engine?: string;
43
+ }, session?: string): Promise<BrowsyResponse>;
44
+ /** POST /api/login */
45
+ login(params: {
46
+ username: string;
47
+ password: string;
48
+ }, session?: string): Promise<BrowsyResponse>;
49
+ /** POST /api/enter-code */
50
+ enterCode(params: {
51
+ code: string;
52
+ }, session?: string): Promise<BrowsyResponse>;
53
+ /** POST /api/find */
54
+ find(params: {
55
+ text?: string;
56
+ role?: string;
57
+ }, session?: string): Promise<BrowsyResponse>;
58
+ /** GET /api/page */
59
+ getPage(params?: {
60
+ format?: string;
61
+ scope?: string;
62
+ }, session?: string): Promise<BrowsyResponse>;
63
+ /** GET /api/page-info */
64
+ pageInfo(session?: string): Promise<BrowsyResponse>;
65
+ /** GET /api/tables */
66
+ tables(session?: string): Promise<BrowsyResponse>;
67
+ /** POST /api/back */
68
+ back(session?: string): Promise<BrowsyResponse>;
69
+ private request;
70
+ }
package/dist/client.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * HTTP client for the browsy REST API.
3
+ * Uses Node.js built-in fetch(). Passes and extracts X-Browsy-Session header.
4
+ */
5
+ export class BrowsyClient {
6
+ baseUrl;
7
+ constructor(port) {
8
+ this.baseUrl = `http://127.0.0.1:${port}`;
9
+ }
10
+ /** GET /health */
11
+ async health() {
12
+ return this.request("GET", "/health");
13
+ }
14
+ /** POST /api/browse */
15
+ async browse(params, session) {
16
+ return this.request("POST", "/api/browse", params, session);
17
+ }
18
+ /** POST /api/click */
19
+ async click(params, session) {
20
+ return this.request("POST", "/api/click", params, session);
21
+ }
22
+ /** POST /api/type */
23
+ async typeText(params, session) {
24
+ return this.request("POST", "/api/type", params, session);
25
+ }
26
+ /** POST /api/check */
27
+ async check(params, session) {
28
+ return this.request("POST", "/api/check", params, session);
29
+ }
30
+ /** POST /api/uncheck */
31
+ async uncheck(params, session) {
32
+ return this.request("POST", "/api/uncheck", params, session);
33
+ }
34
+ /** POST /api/select */
35
+ async select(params, session) {
36
+ return this.request("POST", "/api/select", params, session);
37
+ }
38
+ /** POST /api/search */
39
+ async search(params, session) {
40
+ return this.request("POST", "/api/search", params, session);
41
+ }
42
+ /** POST /api/login */
43
+ async login(params, session) {
44
+ return this.request("POST", "/api/login", params, session);
45
+ }
46
+ /** POST /api/enter-code */
47
+ async enterCode(params, session) {
48
+ return this.request("POST", "/api/enter-code", params, session);
49
+ }
50
+ /** POST /api/find */
51
+ async find(params, session) {
52
+ return this.request("POST", "/api/find", params, session);
53
+ }
54
+ /** GET /api/page */
55
+ async getPage(params, session) {
56
+ const query = new URLSearchParams();
57
+ if (params?.format)
58
+ query.set("format", params.format);
59
+ if (params?.scope)
60
+ query.set("scope", params.scope);
61
+ const qs = query.toString();
62
+ return this.request("GET", `/api/page${qs ? `?${qs}` : ""}`, undefined, session);
63
+ }
64
+ /** GET /api/page-info */
65
+ async pageInfo(session) {
66
+ return this.request("GET", "/api/page-info", undefined, session);
67
+ }
68
+ /** GET /api/tables */
69
+ async tables(session) {
70
+ return this.request("GET", "/api/tables", undefined, session);
71
+ }
72
+ /** POST /api/back */
73
+ async back(session) {
74
+ return this.request("POST", "/api/back", undefined, session);
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Internal
78
+ // ---------------------------------------------------------------------------
79
+ async request(method, path, body, session) {
80
+ const headers = {};
81
+ if (session) {
82
+ headers["X-Browsy-Session"] = session;
83
+ }
84
+ if (body !== undefined) {
85
+ headers["Content-Type"] = "application/json";
86
+ }
87
+ const res = await fetch(`${this.baseUrl}${path}`, {
88
+ method,
89
+ headers,
90
+ body: body !== undefined ? JSON.stringify(body) : undefined,
91
+ });
92
+ const responseSession = res.headers.get("X-Browsy-Session") ?? session ?? "";
93
+ const text = await res.text();
94
+ let json;
95
+ try {
96
+ json = JSON.parse(text);
97
+ }
98
+ catch {
99
+ // Response is plain text, not JSON
100
+ }
101
+ return {
102
+ ok: res.ok,
103
+ status: res.status,
104
+ session: responseSession,
105
+ body: text,
106
+ json,
107
+ };
108
+ }
109
+ }
@@ -0,0 +1,6 @@
1
+ import type { BrowsyConfig, BrowsyConfigInput } from "./types.js";
2
+ export declare function defaultConfig(): BrowsyConfig;
3
+ /**
4
+ * Merge partial user input onto defaults, producing a complete BrowsyConfig.
5
+ */
6
+ export declare function parseConfig(input?: BrowsyConfigInput): BrowsyConfig;
package/dist/config.js ADDED
@@ -0,0 +1,28 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Defaults
3
+ // ---------------------------------------------------------------------------
4
+ export function defaultConfig() {
5
+ return {
6
+ port: 3847,
7
+ autoStart: true,
8
+ allowPrivateNetwork: false,
9
+ serverTimeout: 10_000,
10
+ };
11
+ }
12
+ // ---------------------------------------------------------------------------
13
+ // Config parsing
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Merge partial user input onto defaults, producing a complete BrowsyConfig.
17
+ */
18
+ export function parseConfig(input) {
19
+ const base = defaultConfig();
20
+ if (!input)
21
+ return base;
22
+ return {
23
+ port: input.port ?? base.port,
24
+ autoStart: input.autoStart ?? base.autoStart,
25
+ allowPrivateNetwork: input.allowPrivateNetwork ?? base.allowPrivateNetwork,
26
+ serverTimeout: input.serverTimeout ?? base.serverTimeout,
27
+ };
28
+ }
@@ -0,0 +1,27 @@
1
+ import type { BrowsyConfigInput, BrowsyServerInfo } from "./types.js";
2
+ import { BrowsyClient } from "./client.js";
3
+ import { ServerManager } from "./server-manager.js";
4
+ import { SessionManager } from "./session-manager.js";
5
+ import type { BrowsyConfig } from "./types.js";
6
+ /**
7
+ * Central facade holding config, client, server manager, and session manager.
8
+ * One instance per SDK lifetime.
9
+ */
10
+ export declare class BrowsyContext {
11
+ readonly config: BrowsyConfig;
12
+ readonly client: BrowsyClient;
13
+ readonly serverManager: ServerManager;
14
+ readonly sessionManager: SessionManager;
15
+ /** Agent ID to use when no agent context is available */
16
+ private defaultAgentId;
17
+ constructor(configInput?: BrowsyConfigInput);
18
+ /** Ensure the browsy server is running. */
19
+ ensureServer(): Promise<void>;
20
+ /** Get current server status. */
21
+ getStatus(): BrowsyServerInfo;
22
+ /**
23
+ * Execute a browsy tool call.
24
+ * Ensures server is running, manages sessions, calls the client, and returns results.
25
+ */
26
+ executeToolCall(method: string, params: Record<string, unknown>, agentId?: string): Promise<string>;
27
+ }
@@ -0,0 +1,100 @@
1
+ import { parseConfig } from "./config.js";
2
+ import { BrowsyClient } from "./client.js";
3
+ import { ServerManager } from "./server-manager.js";
4
+ import { SessionManager } from "./session-manager.js";
5
+ /**
6
+ * Central facade holding config, client, server manager, and session manager.
7
+ * One instance per SDK lifetime.
8
+ */
9
+ export class BrowsyContext {
10
+ config;
11
+ client;
12
+ serverManager;
13
+ sessionManager;
14
+ /** Agent ID to use when no agent context is available */
15
+ defaultAgentId = "__default__";
16
+ constructor(configInput) {
17
+ this.config = parseConfig(configInput);
18
+ this.client = new BrowsyClient(this.config.port);
19
+ this.serverManager = new ServerManager(this.config);
20
+ this.sessionManager = new SessionManager();
21
+ }
22
+ /** Ensure the browsy server is running. */
23
+ async ensureServer() {
24
+ if (!this.serverManager.isRunning()) {
25
+ await this.serverManager.start();
26
+ }
27
+ }
28
+ /** Get current server status. */
29
+ getStatus() {
30
+ return this.serverManager.getStatus();
31
+ }
32
+ /**
33
+ * Execute a browsy tool call.
34
+ * Ensures server is running, manages sessions, calls the client, and returns results.
35
+ */
36
+ async executeToolCall(method, params, agentId) {
37
+ await this.ensureServer();
38
+ const aid = agentId ?? this.defaultAgentId;
39
+ const session = this.sessionManager.getOrCreate(aid);
40
+ const token = session.token || undefined;
41
+ let response;
42
+ switch (method) {
43
+ case "browse":
44
+ response = await this.client.browse(params, token);
45
+ break;
46
+ case "click":
47
+ response = await this.client.click(params, token);
48
+ break;
49
+ case "typeText":
50
+ response = await this.client.typeText(params, token);
51
+ break;
52
+ case "check":
53
+ response = await this.client.check(params, token);
54
+ break;
55
+ case "uncheck":
56
+ response = await this.client.uncheck(params, token);
57
+ break;
58
+ case "select":
59
+ response = await this.client.select(params, token);
60
+ break;
61
+ case "search":
62
+ response = await this.client.search(params, token);
63
+ break;
64
+ case "login":
65
+ response = await this.client.login(params, token);
66
+ break;
67
+ case "enterCode":
68
+ response = await this.client.enterCode(params, token);
69
+ break;
70
+ case "find":
71
+ response = await this.client.find(params, token);
72
+ break;
73
+ case "pageInfo":
74
+ response = await this.client.pageInfo(token);
75
+ break;
76
+ case "tables":
77
+ response = await this.client.tables(token);
78
+ break;
79
+ case "getPage":
80
+ response = await this.client.getPage(params, token);
81
+ break;
82
+ case "back":
83
+ response = await this.client.back(token);
84
+ break;
85
+ default:
86
+ throw new Error(`Unknown browsy method: ${method}`);
87
+ }
88
+ // Update session token from response
89
+ if (response.session) {
90
+ this.sessionManager.update(aid, response.session);
91
+ }
92
+ // Return error message on failure, body on success
93
+ let result = response.body;
94
+ if (!response.ok) {
95
+ const errorJson = response.json;
96
+ result = errorJson?.error ?? response.body;
97
+ }
98
+ return result;
99
+ }
100
+ }
@@ -0,0 +1,8 @@
1
+ export { BrowsyClient } from "./client.js";
2
+ export { ServerManager } from "./server-manager.js";
3
+ export { SessionManager } from "./session-manager.js";
4
+ export { BrowsyContext } from "./context.js";
5
+ export { defaultConfig, parseConfig } from "./config.js";
6
+ export { isPortInUse, findBrowsyBinary } from "./process.js";
7
+ export type { BrowsyConfig, BrowsyConfigInput, BrowsyResponse, BrowsyServerStatus, BrowsyServerInfo, BrowsySession, } from "./types.js";
8
+ export { BrowseParams, ClickParams, TypeTextParams, CheckParams, UncheckParams, SelectParams, SearchParams, LoginParams, EnterCodeParams, FindParams, GetPageParams, PageInfoParams, TablesParams, BackParams, TOOL_DESCRIPTIONS, TOOL_SCHEMAS, } from "./schemas.js";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // Core SDK
2
+ export { BrowsyClient } from "./client.js";
3
+ export { ServerManager } from "./server-manager.js";
4
+ export { SessionManager } from "./session-manager.js";
5
+ export { BrowsyContext } from "./context.js";
6
+ export { defaultConfig, parseConfig } from "./config.js";
7
+ export { isPortInUse, findBrowsyBinary } from "./process.js";
8
+ // Schemas
9
+ export { BrowseParams, ClickParams, TypeTextParams, CheckParams, UncheckParams, SelectParams, SearchParams, LoginParams, EnterCodeParams, FindParams, GetPageParams, PageInfoParams, TablesParams, BackParams, TOOL_DESCRIPTIONS, TOOL_SCHEMAS, } from "./schemas.js";
@@ -0,0 +1,157 @@
1
+ import { BrowsyContext } from "./context.js";
2
+ import type { BrowsyConfigInput } from "./types.js";
3
+ /**
4
+ * Returns an array of 14 LangChain tool instances for all browsy operations.
5
+ * Lazily initializes a default BrowsyContext if none is provided.
6
+ */
7
+ export declare function getTools(ctx?: BrowsyContext | BrowsyConfigInput): import("@langchain/core/tools").DynamicStructuredTool<import("zod").ZodObject<{
8
+ url: import("zod").ZodString;
9
+ format: import("zod").ZodOptional<import("zod").ZodString>;
10
+ scope: import("zod").ZodOptional<import("zod").ZodString>;
11
+ }, "strip", import("zod").ZodTypeAny, {
12
+ url: string;
13
+ format?: string | undefined;
14
+ scope?: string | undefined;
15
+ }, {
16
+ url: string;
17
+ format?: string | undefined;
18
+ scope?: string | undefined;
19
+ }> | import("zod").ZodObject<{
20
+ id: import("zod").ZodNumber;
21
+ }, "strip", import("zod").ZodTypeAny, {
22
+ id: number;
23
+ }, {
24
+ id: number;
25
+ }> | import("zod").ZodObject<{
26
+ id: import("zod").ZodNumber;
27
+ text: import("zod").ZodString;
28
+ }, "strip", import("zod").ZodTypeAny, {
29
+ id: number;
30
+ text: string;
31
+ }, {
32
+ id: number;
33
+ text: string;
34
+ }> | import("zod").ZodObject<{
35
+ id: import("zod").ZodNumber;
36
+ }, "strip", import("zod").ZodTypeAny, {
37
+ id: number;
38
+ }, {
39
+ id: number;
40
+ }> | import("zod").ZodObject<{
41
+ id: import("zod").ZodNumber;
42
+ }, "strip", import("zod").ZodTypeAny, {
43
+ id: number;
44
+ }, {
45
+ id: number;
46
+ }> | import("zod").ZodObject<{
47
+ id: import("zod").ZodNumber;
48
+ value: import("zod").ZodString;
49
+ }, "strip", import("zod").ZodTypeAny, {
50
+ id: number;
51
+ value: string;
52
+ }, {
53
+ id: number;
54
+ value: string;
55
+ }> | import("zod").ZodObject<{
56
+ query: import("zod").ZodString;
57
+ engine: import("zod").ZodOptional<import("zod").ZodString>;
58
+ }, "strip", import("zod").ZodTypeAny, {
59
+ query: string;
60
+ engine?: string | undefined;
61
+ }, {
62
+ query: string;
63
+ engine?: string | undefined;
64
+ }> | import("zod").ZodObject<{
65
+ username: import("zod").ZodString;
66
+ password: import("zod").ZodString;
67
+ }, "strip", import("zod").ZodTypeAny, {
68
+ username: string;
69
+ password: string;
70
+ }, {
71
+ username: string;
72
+ password: string;
73
+ }> | import("zod").ZodObject<{
74
+ code: import("zod").ZodString;
75
+ }, "strip", import("zod").ZodTypeAny, {
76
+ code: string;
77
+ }, {
78
+ code: string;
79
+ }> | import("zod").ZodObject<{
80
+ text: import("zod").ZodOptional<import("zod").ZodString>;
81
+ role: import("zod").ZodOptional<import("zod").ZodString>;
82
+ }, "strip", import("zod").ZodTypeAny, {
83
+ text?: string | undefined;
84
+ role?: string | undefined;
85
+ }, {
86
+ text?: string | undefined;
87
+ role?: string | undefined;
88
+ }> | import("zod").ZodObject<{
89
+ format: import("zod").ZodOptional<import("zod").ZodString>;
90
+ scope: import("zod").ZodOptional<import("zod").ZodString>;
91
+ }, "strip", import("zod").ZodTypeAny, {
92
+ format?: string | undefined;
93
+ scope?: string | undefined;
94
+ }, {
95
+ format?: string | undefined;
96
+ scope?: string | undefined;
97
+ }> | import("zod").ZodObject<{}, "strip", import("zod").ZodTypeAny, {}, {}> | import("zod").ZodObject<{}, "strip", import("zod").ZodTypeAny, {}, {}> | import("zod").ZodObject<{}, "strip", import("zod").ZodTypeAny, {}, {}>, {
98
+ url: string;
99
+ format?: string | undefined;
100
+ scope?: string | undefined;
101
+ } | {
102
+ id: number;
103
+ } | {
104
+ id: number;
105
+ text: string;
106
+ } | {
107
+ id: number;
108
+ } | {
109
+ id: number;
110
+ } | {
111
+ id: number;
112
+ value: string;
113
+ } | {
114
+ query: string;
115
+ engine?: string | undefined;
116
+ } | {
117
+ username: string;
118
+ password: string;
119
+ } | {
120
+ code: string;
121
+ } | {
122
+ text?: string | undefined;
123
+ role?: string | undefined;
124
+ } | {
125
+ format?: string | undefined;
126
+ scope?: string | undefined;
127
+ } | {} | {} | {}, {
128
+ url: string;
129
+ format?: string | undefined;
130
+ scope?: string | undefined;
131
+ } | {
132
+ id: number;
133
+ } | {
134
+ id: number;
135
+ text: string;
136
+ } | {
137
+ id: number;
138
+ } | {
139
+ id: number;
140
+ } | {
141
+ id: number;
142
+ value: string;
143
+ } | {
144
+ query: string;
145
+ engine?: string | undefined;
146
+ } | {
147
+ username: string;
148
+ password: string;
149
+ } | {
150
+ code: string;
151
+ } | {
152
+ text?: string | undefined;
153
+ role?: string | undefined;
154
+ } | {
155
+ format?: string | undefined;
156
+ scope?: string | undefined;
157
+ } | {} | {} | {}, string>[];
@@ -0,0 +1,26 @@
1
+ import { tool } from "@langchain/core/tools";
2
+ import { BrowsyContext } from "./context.js";
3
+ import { TOOL_SCHEMAS, TOOL_DESCRIPTIONS } from "./schemas.js";
4
+ let defaultContext;
5
+ function getDefaultContext() {
6
+ if (!defaultContext) {
7
+ defaultContext = new BrowsyContext();
8
+ }
9
+ return defaultContext;
10
+ }
11
+ /**
12
+ * Returns an array of 14 LangChain tool instances for all browsy operations.
13
+ * Lazily initializes a default BrowsyContext if none is provided.
14
+ */
15
+ export function getTools(ctx) {
16
+ const context = ctx instanceof BrowsyContext
17
+ ? ctx
18
+ : ctx
19
+ ? new BrowsyContext(ctx)
20
+ : getDefaultContext();
21
+ return TOOL_SCHEMAS.map((entry) => tool(async (params) => context.executeToolCall(entry.method, params), {
22
+ name: entry.name,
23
+ description: TOOL_DESCRIPTIONS[entry.name],
24
+ schema: entry.schema,
25
+ }));
26
+ }
@@ -0,0 +1,23 @@
1
+ import { BrowsyContext } from "./context.js";
2
+ import type { BrowsyConfigInput } from "./types.js";
3
+ export interface ChatCompletionTool {
4
+ type: "function";
5
+ function: {
6
+ name: string;
7
+ description: string;
8
+ parameters: Record<string, unknown>;
9
+ strict?: boolean;
10
+ };
11
+ }
12
+ /**
13
+ * Returns OpenAI-format tool definitions for all 14 browsy tools.
14
+ */
15
+ export declare function getToolDefinitions(): ChatCompletionTool[];
16
+ /**
17
+ * Dispatches a tool call by name and returns the string result.
18
+ */
19
+ export declare function handleToolCall(name: string, args: Record<string, unknown>, ctx?: BrowsyContext | BrowsyConfigInput): Promise<string>;
20
+ /**
21
+ * Returns a bound handler function for dispatching tool calls.
22
+ */
23
+ export declare function createToolCallHandler(ctx?: BrowsyContext | BrowsyConfigInput): (name: string, args: Record<string, unknown>) => Promise<string>;
package/dist/openai.js ADDED
@@ -0,0 +1,58 @@
1
+ import { zodToJsonSchema } from "./zod-to-json-schema.js";
2
+ import { BrowsyContext } from "./context.js";
3
+ import { TOOL_SCHEMAS, TOOL_DESCRIPTIONS } from "./schemas.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Default context
6
+ // ---------------------------------------------------------------------------
7
+ let defaultContext;
8
+ function getDefaultContext() {
9
+ if (!defaultContext) {
10
+ defaultContext = new BrowsyContext();
11
+ }
12
+ return defaultContext;
13
+ }
14
+ // ---------------------------------------------------------------------------
15
+ // Public API
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Returns OpenAI-format tool definitions for all 14 browsy tools.
19
+ */
20
+ export function getToolDefinitions() {
21
+ return TOOL_SCHEMAS.map((entry) => ({
22
+ type: "function",
23
+ function: {
24
+ name: entry.name,
25
+ description: TOOL_DESCRIPTIONS[entry.name],
26
+ parameters: zodToJsonSchema(entry.schema),
27
+ strict: true,
28
+ },
29
+ }));
30
+ }
31
+ /**
32
+ * Dispatches a tool call by name and returns the string result.
33
+ */
34
+ export async function handleToolCall(name, args, ctx) {
35
+ const context = ctx instanceof BrowsyContext
36
+ ? ctx
37
+ : ctx
38
+ ? new BrowsyContext(ctx)
39
+ : getDefaultContext();
40
+ const entry = TOOL_SCHEMAS.find((t) => t.name === name);
41
+ if (!entry) {
42
+ throw new Error(`Unknown browsy tool: ${name}`);
43
+ }
44
+ return context.executeToolCall(entry.method, args);
45
+ }
46
+ /**
47
+ * Returns a bound handler function for dispatching tool calls.
48
+ */
49
+ export function createToolCallHandler(ctx) {
50
+ const context = ctx instanceof BrowsyContext
51
+ ? ctx
52
+ : ctx
53
+ ? new BrowsyContext(ctx)
54
+ : getDefaultContext();
55
+ return async (name, args) => {
56
+ return handleToolCall(name, args, context);
57
+ };
58
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Check if a port is currently in use.
3
+ */
4
+ export declare function isPortInUse(port: number): Promise<boolean>;
5
+ /**
6
+ * Find the browsy binary in PATH or common locations.
7
+ * Returns the path to the binary, or null if not found.
8
+ *
9
+ * Checks in order:
10
+ * 1. BROWSY_BIN environment variable (explicit override)
11
+ * 2. "browsy" in system PATH
12
+ */
13
+ export declare function findBrowsyBinary(): string | null;