agentic-browser 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,374 @@
1
+ import { r as createCliRuntime } from "../runtime-C-oYEtN0.mjs";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import crypto from "node:crypto";
5
+ import { Command } from "commander";
6
+
7
+ //#region src/cli/commands/agent.ts
8
+ const STATE_FILE_NAME = "agent-state.json";
9
+ function stateFilePath(runtime) {
10
+ return path.join(runtime.context.config.logDir, STATE_FILE_NAME);
11
+ }
12
+ function loadState(runtime) {
13
+ const filePath = stateFilePath(runtime);
14
+ if (!fs.existsSync(filePath)) return { sessionId: null };
15
+ try {
16
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
17
+ } catch {
18
+ return { sessionId: null };
19
+ }
20
+ }
21
+ function saveState(runtime, state) {
22
+ const dir = path.dirname(stateFilePath(runtime));
23
+ fs.mkdirSync(dir, { recursive: true });
24
+ fs.writeFileSync(stateFilePath(runtime), JSON.stringify(state, null, 2));
25
+ }
26
+ function requireSessionId(runtime) {
27
+ const state = loadState(runtime);
28
+ if (!state.sessionId) throw new Error("No active agent session. Run: agentic-browser agent start");
29
+ return state.sessionId;
30
+ }
31
+ function nextCommandId() {
32
+ return `cmd-${Date.now()}-${crypto.randomBytes(3).toString("hex")}`;
33
+ }
34
+ async function ensureReady(runtime, sessionId) {
35
+ if (runtime.api.getSession(sessionId).status === "ready") return;
36
+ const restarted = await runtime.api.restartSession(sessionId);
37
+ if (restarted.status !== "ready") throw new Error(`Session not ready after restart: ${restarted.status}`);
38
+ }
39
+ async function agentStart(runtime) {
40
+ const session = await runtime.api.createSession({ browser: "chrome" });
41
+ saveState(runtime, { sessionId: session.sessionId });
42
+ return {
43
+ ok: true,
44
+ action: "start",
45
+ sessionId: session.sessionId
46
+ };
47
+ }
48
+ async function agentStatus(runtime) {
49
+ const sessionId = requireSessionId(runtime);
50
+ return {
51
+ ok: true,
52
+ action: "status",
53
+ ...runtime.api.getSession(sessionId)
54
+ };
55
+ }
56
+ async function agentStop(runtime) {
57
+ const sessionId = requireSessionId(runtime);
58
+ await runtime.api.terminateSession(sessionId);
59
+ saveState(runtime, { sessionId: null });
60
+ return {
61
+ ok: true,
62
+ action: "stop",
63
+ sessionId
64
+ };
65
+ }
66
+ async function agentRun(runtime, input) {
67
+ const sessionId = requireSessionId(runtime);
68
+ await ensureReady(runtime, sessionId);
69
+ const attempt = () => runtime.api.executeCommand(sessionId, {
70
+ commandId: nextCommandId(),
71
+ type: input.type,
72
+ payload: input.payload
73
+ });
74
+ try {
75
+ const first = await attempt();
76
+ if (first.resultStatus === "failed") throw new Error(first.resultMessage ?? "Command failed");
77
+ return {
78
+ ok: true,
79
+ action: "run",
80
+ ...first
81
+ };
82
+ } catch {
83
+ await runtime.api.restartSession(sessionId);
84
+ const second = await attempt();
85
+ if (second.resultStatus === "failed") throw new Error(second.resultMessage ?? "Command failed after retry");
86
+ return {
87
+ ok: true,
88
+ action: "run",
89
+ ...second
90
+ };
91
+ }
92
+ }
93
+ async function agentContent(runtime, input) {
94
+ const sessionId = requireSessionId(runtime);
95
+ await ensureReady(runtime, sessionId);
96
+ return {
97
+ ok: true,
98
+ action: "content",
99
+ ...await runtime.api.getContent(sessionId, {
100
+ mode: input.mode,
101
+ selector: input.selector
102
+ })
103
+ };
104
+ }
105
+ async function agentElements(runtime, input) {
106
+ const sessionId = requireSessionId(runtime);
107
+ await ensureReady(runtime, sessionId);
108
+ return {
109
+ ok: true,
110
+ action: "elements",
111
+ ...await runtime.api.getInteractiveElements(sessionId, {
112
+ roles: input.roles,
113
+ visibleOnly: input.visibleOnly,
114
+ limit: input.limit,
115
+ selector: input.selector
116
+ })
117
+ };
118
+ }
119
+ async function agentMemorySearch(runtime, input) {
120
+ return {
121
+ ok: true,
122
+ action: "memory-search",
123
+ ...runtime.api.searchMemory(input)
124
+ };
125
+ }
126
+ async function agentCleanup(runtime, input) {
127
+ return {
128
+ ok: true,
129
+ action: "cleanup",
130
+ ...runtime.api.cleanupSessions(input)
131
+ };
132
+ }
133
+
134
+ //#endregion
135
+ //#region src/cli/output/result-formatter.ts
136
+ function formatResult(resultStatus, message) {
137
+ return {
138
+ resultStatus,
139
+ message
140
+ };
141
+ }
142
+
143
+ //#endregion
144
+ //#region src/cli/commands/session-start.ts
145
+ async function runSessionStart(runtime, input) {
146
+ const session = await runtime.api.createSession(input);
147
+ return {
148
+ ...formatResult("success", "Session started"),
149
+ sessionId: session.sessionId
150
+ };
151
+ }
152
+
153
+ //#endregion
154
+ //#region src/cli/commands/session-status.ts
155
+ async function runSessionStatus(runtime, input) {
156
+ return runtime.api.getSession(input.sessionId);
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/cli/commands/session-stop.ts
161
+ async function runSessionStop(runtime, input) {
162
+ await runtime.api.terminateSession(input.sessionId);
163
+ return { terminated: true };
164
+ }
165
+
166
+ //#endregion
167
+ //#region src/cli/commands/session-cleanup.ts
168
+ async function runSessionCleanup(runtime, input) {
169
+ return runtime.api.cleanupSessions(input);
170
+ }
171
+
172
+ //#endregion
173
+ //#region src/cli/commands/session-restart.ts
174
+ async function runSessionRestart(runtime, input) {
175
+ return await runtime.api.restartSession(input.sessionId);
176
+ }
177
+
178
+ //#endregion
179
+ //#region src/cli/commands/session-auth.ts
180
+ async function runSessionAuth(runtime, input) {
181
+ const token = runtime.api.rotateSessionToken(input.sessionId);
182
+ return {
183
+ token,
184
+ valid: runtime.context.tokenService.validate(input.sessionId, token)
185
+ };
186
+ }
187
+
188
+ //#endregion
189
+ //#region src/cli/commands/command-run.ts
190
+ async function runCommand(runtime, input) {
191
+ if (!input.sessionId) throw new Error("No active session. Start or restart a session first.");
192
+ try {
193
+ return await runtime.api.executeCommand(input.sessionId, {
194
+ commandId: input.commandId,
195
+ type: input.type,
196
+ payload: input.payload
197
+ });
198
+ } catch (error) {
199
+ throw new Error(`Command rejected: ${error.message}`);
200
+ }
201
+ }
202
+
203
+ //#endregion
204
+ //#region src/cli/commands/page-content.ts
205
+ async function runPageContent(runtime, input) {
206
+ return await runtime.api.getContent(input.sessionId, {
207
+ mode: input.mode,
208
+ selector: input.selector
209
+ });
210
+ }
211
+
212
+ //#endregion
213
+ //#region src/cli/commands/memory-search.ts
214
+ async function runMemorySearch(runtime, input) {
215
+ if (!input.taskIntent || !input.taskIntent.trim()) throw new Error("taskIntent is required");
216
+ return runtime.api.searchMemory(input);
217
+ }
218
+
219
+ //#endregion
220
+ //#region src/cli/commands/memory-inspect.ts
221
+ async function runMemoryInspect(runtime, input) {
222
+ if (!input.insightId || !input.insightId.trim()) throw new Error("insightId is required");
223
+ return runtime.api.inspectMemory(input.insightId);
224
+ }
225
+
226
+ //#endregion
227
+ //#region src/cli/commands/memory-verify.ts
228
+ async function runMemoryVerify(runtime, input) {
229
+ if (!input.insightId || !input.insightId.trim()) throw new Error("insightId is required");
230
+ return runtime.api.verifyMemory(input.insightId);
231
+ }
232
+
233
+ //#endregion
234
+ //#region src/cli/commands/memory-stats.ts
235
+ async function runMemoryStats(runtime) {
236
+ return runtime.api.memoryStats();
237
+ }
238
+
239
+ //#endregion
240
+ //#region src/cli/index.ts
241
+ async function main() {
242
+ const runtime = createCliRuntime();
243
+ const program = new Command();
244
+ program.name("agentic-browser").description("Agentic browser CLI");
245
+ program.command("session:start").action(async () => {
246
+ const result = await runSessionStart(runtime, { browser: "chrome" });
247
+ console.log(JSON.stringify(result));
248
+ });
249
+ program.command("session:status").argument("<sessionId>").action(async (sessionId) => {
250
+ const result = await runSessionStatus(runtime, { sessionId });
251
+ console.log(JSON.stringify(result));
252
+ });
253
+ program.command("session:stop").argument("<sessionId>").action(async (sessionId) => {
254
+ const result = await runSessionStop(runtime, { sessionId });
255
+ console.log(JSON.stringify(result));
256
+ });
257
+ program.command("session:cleanup").option("--max-age-days <days>", "remove terminated sessions older than N days", "7").option("--dry-run", "show what would be removed without deleting").action(async (options) => {
258
+ const result = await runSessionCleanup(runtime, {
259
+ maxAgeDays: Number.parseFloat(options.maxAgeDays),
260
+ dryRun: Boolean(options.dryRun)
261
+ });
262
+ console.log(JSON.stringify(result));
263
+ });
264
+ program.command("session:restart").argument("<sessionId>").action(async (sessionId) => {
265
+ const result = await runSessionRestart(runtime, { sessionId });
266
+ console.log(JSON.stringify(result));
267
+ });
268
+ program.command("session:auth").argument("<sessionId>").action(async (sessionId) => {
269
+ const result = await runSessionAuth(runtime, { sessionId });
270
+ console.log(JSON.stringify(result));
271
+ });
272
+ program.command("command:run").argument("<sessionId>").argument("<commandId>").argument("<type>").argument("<payloadJson>").action(async (sessionId, commandId, type, payloadJson) => {
273
+ const result = await runCommand(runtime, {
274
+ sessionId,
275
+ commandId,
276
+ type,
277
+ payload: JSON.parse(payloadJson)
278
+ });
279
+ console.log(JSON.stringify(result));
280
+ });
281
+ program.command("page:content").argument("<sessionId>").option("--mode <mode>", "title|text|html", "text").option("--selector <selector>", "optional CSS selector").action(async (sessionId, options) => {
282
+ const result = await runPageContent(runtime, {
283
+ sessionId,
284
+ mode: options.mode,
285
+ selector: options.selector
286
+ });
287
+ console.log(JSON.stringify(result));
288
+ });
289
+ program.command("memory:search").argument("<taskIntent>").option("--domain <domain>", "website domain filter").option("--limit <limit>", "max results", "10").action(async (taskIntent, options) => {
290
+ const result = await runMemorySearch(runtime, {
291
+ taskIntent,
292
+ siteDomain: options.domain,
293
+ limit: Number.parseInt(options.limit, 10)
294
+ });
295
+ console.log(JSON.stringify(result));
296
+ });
297
+ program.command("memory:inspect").argument("<insightId>").action(async (insightId) => {
298
+ const result = await runMemoryInspect(runtime, { insightId });
299
+ console.log(JSON.stringify(result));
300
+ });
301
+ program.command("memory:verify").argument("<insightId>").action(async (insightId) => {
302
+ const result = await runMemoryVerify(runtime, { insightId });
303
+ console.log(JSON.stringify(result));
304
+ });
305
+ program.command("memory:stats").action(async () => {
306
+ const result = await runMemoryStats(runtime);
307
+ console.log(JSON.stringify(result));
308
+ });
309
+ const agent = program.command("agent").description("Stateful agent wrapper with session persistence and auto-retry");
310
+ agent.command("start").action(async () => {
311
+ const result = await agentStart(runtime);
312
+ console.log(JSON.stringify(result));
313
+ });
314
+ agent.command("status").action(async () => {
315
+ const result = await agentStatus(runtime);
316
+ console.log(JSON.stringify(result));
317
+ });
318
+ agent.command("stop").action(async () => {
319
+ const result = await agentStop(runtime);
320
+ console.log(JSON.stringify(result));
321
+ });
322
+ agent.command("run").argument("<type>", "navigate|interact|restart|terminate").argument("<payloadJson>", "JSON payload").action(async (type, payloadJson) => {
323
+ const result = await agentRun(runtime, {
324
+ type,
325
+ payload: JSON.parse(payloadJson)
326
+ });
327
+ console.log(JSON.stringify(result));
328
+ });
329
+ agent.command("content").option("--mode <mode>", "title|text|html", "text").option("--selector <selector>", "optional CSS selector").action(async (options) => {
330
+ const result = await agentContent(runtime, options);
331
+ console.log(JSON.stringify(result));
332
+ });
333
+ agent.command("elements").description("List interactive elements on the current page").option("--roles <roles>", "comma-separated roles filter", (v) => v.split(",")).option("--visible-only", "only visible elements", true).option("--no-visible-only", "include hidden elements").option("--limit <n>", "max elements", "50").option("--selector <selector>", "scope to CSS selector subtree").action(async (options) => {
334
+ const result = await agentElements(runtime, {
335
+ roles: options.roles,
336
+ visibleOnly: options.visibleOnly,
337
+ limit: Number.parseInt(options.limit, 10),
338
+ selector: options.selector
339
+ });
340
+ console.log(JSON.stringify(result));
341
+ });
342
+ agent.command("memory-search").argument("<taskIntent>").option("--domain <domain>", "website domain filter").option("--limit <limit>", "max results", "5").action(async (taskIntent, options) => {
343
+ const result = await agentMemorySearch(runtime, {
344
+ taskIntent,
345
+ siteDomain: options.domain,
346
+ limit: Number.parseInt(options.limit, 10)
347
+ });
348
+ console.log(JSON.stringify(result));
349
+ });
350
+ agent.command("cleanup").option("--max-age-days <days>", "remove sessions older than N days", "7").option("--dry-run", "show what would be removed").action(async (options) => {
351
+ const result = await agentCleanup(runtime, {
352
+ maxAgeDays: Number.parseFloat(options.maxAgeDays),
353
+ dryRun: Boolean(options.dryRun)
354
+ });
355
+ console.log(JSON.stringify(result));
356
+ });
357
+ program.command("mcp").description("Start the MCP server (stdio transport)").action(async () => {
358
+ const { main: startMcpServer } = await import("../mcp/index.mjs");
359
+ await startMcpServer();
360
+ });
361
+ program.command("setup").description("Configure agentic-browser as MCP server for your AI tool").action(async () => {
362
+ const { runSetup } = await import("../setup-CULSgM_M.mjs");
363
+ await runSetup();
364
+ });
365
+ await program.parseAsync(process.argv);
366
+ }
367
+ main().catch((error) => {
368
+ const message = error instanceof Error ? error.message : String(error);
369
+ console.error(JSON.stringify({ error: message }));
370
+ process.exit(1);
371
+ });
372
+
373
+ //#endregion
374
+ export { };
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-C-oYEtN0.mjs";
2
+
3
+ export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
@@ -0,0 +1,170 @@
1
+ import { n as createAgenticBrowserCore } from "../runtime-C-oYEtN0.mjs";
2
+ import { z } from "zod";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ //#region src/mcp/index.ts
7
+ let core;
8
+ let activeSessionId;
9
+ function getCore() {
10
+ if (!core) core = createAgenticBrowserCore();
11
+ return core;
12
+ }
13
+ function genId(prefix) {
14
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
15
+ }
16
+ const server = new McpServer({
17
+ name: "agentic-browser",
18
+ version: "0.1.0"
19
+ });
20
+ server.tool("browser_start_session", "Start a Chrome browser session for web automation. Call this first before using any other browser tool. Returns a sessionId you'll need for all subsequent calls.", {}, async () => {
21
+ const session = await getCore().startSession();
22
+ activeSessionId = session.sessionId;
23
+ return { content: [{
24
+ type: "text",
25
+ text: JSON.stringify(session)
26
+ }] };
27
+ });
28
+ server.tool("browser_navigate", "Navigate the browser to a URL. The browser must have an active session.", {
29
+ url: z.string().describe("The URL to navigate to"),
30
+ sessionId: z.string().optional().describe("Session ID (uses active session if omitted)")
31
+ }, async ({ url, sessionId }) => {
32
+ const sid = sessionId ?? activeSessionId;
33
+ if (!sid) throw new Error("No active session. Call browser_start_session first.");
34
+ const result = await getCore().runCommand({
35
+ sessionId: sid,
36
+ commandId: genId("nav"),
37
+ type: "navigate",
38
+ payload: { url }
39
+ });
40
+ return { content: [{
41
+ type: "text",
42
+ text: JSON.stringify(result)
43
+ }] };
44
+ });
45
+ server.tool("browser_interact", "Interact with a page element. Actions: \"click\" (click element), \"type\" (type text into input), \"press\" (press a keyboard key like Enter), \"waitFor\" (wait for element to appear).", {
46
+ action: z.enum([
47
+ "click",
48
+ "type",
49
+ "press",
50
+ "waitFor"
51
+ ]).describe("The interaction type"),
52
+ selector: z.string().optional().describe("CSS selector for the target element"),
53
+ text: z.string().optional().describe("Text to type (required for \"type\" action)"),
54
+ key: z.string().optional().describe("Key to press (required for \"press\" action, e.g. \"Enter\", \"Tab\")"),
55
+ timeoutMs: z.number().optional().describe("Timeout in milliseconds (for \"waitFor\" action, default 4000)"),
56
+ sessionId: z.string().optional().describe("Session ID (uses active session if omitted)")
57
+ }, async ({ action, selector, text, key, timeoutMs, sessionId }) => {
58
+ const sid = sessionId ?? activeSessionId;
59
+ if (!sid) throw new Error("No active session. Call browser_start_session first.");
60
+ const payload = { action };
61
+ if (selector) payload.selector = selector;
62
+ if (text) payload.text = text;
63
+ if (key) payload.key = key;
64
+ if (timeoutMs) payload.timeoutMs = timeoutMs;
65
+ const result = await getCore().runCommand({
66
+ sessionId: sid,
67
+ commandId: genId("int"),
68
+ type: "interact",
69
+ payload
70
+ });
71
+ return { content: [{
72
+ type: "text",
73
+ text: JSON.stringify(result)
74
+ }] };
75
+ });
76
+ server.tool("browser_get_content", "Get the current page content. Modes: \"title\" (page title only), \"text\" (readable text content), \"html\" (raw HTML). Use selector to scope to a specific element.", {
77
+ mode: z.enum([
78
+ "title",
79
+ "text",
80
+ "html"
81
+ ]).default("text").describe("Content extraction mode"),
82
+ selector: z.string().optional().describe("CSS selector to scope content (e.g. \"main\", \"#content\")"),
83
+ sessionId: z.string().optional().describe("Session ID (uses active session if omitted)")
84
+ }, async ({ mode, selector, sessionId }) => {
85
+ const sid = sessionId ?? activeSessionId;
86
+ if (!sid) throw new Error("No active session. Call browser_start_session first.");
87
+ const result = await getCore().getPageContent({
88
+ sessionId: sid,
89
+ mode,
90
+ selector
91
+ });
92
+ return { content: [{
93
+ type: "text",
94
+ text: JSON.stringify(result)
95
+ }] };
96
+ });
97
+ server.tool("browser_get_elements", "Discover all interactive elements on the current page (buttons, links, inputs, etc.). Returns CSS selectors you can use with browser_interact. Call this to understand what's on the page before interacting.", {
98
+ roles: z.array(z.enum([
99
+ "link",
100
+ "button",
101
+ "input",
102
+ "select",
103
+ "textarea",
104
+ "checkbox",
105
+ "radio",
106
+ "contenteditable",
107
+ "custom"
108
+ ])).optional().describe("Filter by element roles (omit for all)"),
109
+ visibleOnly: z.boolean().default(true).describe("Only return visible elements"),
110
+ limit: z.number().default(50).describe("Maximum number of elements to return"),
111
+ selector: z.string().optional().describe("CSS selector to scope element discovery to a subtree"),
112
+ sessionId: z.string().optional().describe("Session ID (uses active session if omitted)")
113
+ }, async ({ roles, visibleOnly, limit, selector, sessionId }) => {
114
+ const sid = sessionId ?? activeSessionId;
115
+ if (!sid) throw new Error("No active session. Call browser_start_session first.");
116
+ const result = await getCore().getInteractiveElements({
117
+ sessionId: sid,
118
+ roles,
119
+ visibleOnly,
120
+ limit,
121
+ selector
122
+ });
123
+ return { content: [{
124
+ type: "text",
125
+ text: JSON.stringify(result)
126
+ }] };
127
+ });
128
+ server.tool("browser_search_memory", "Search task memory for previously learned selectors and interaction patterns. Use this before interacting with a known site to reuse proven selectors instead of rediscovering them.", {
129
+ taskIntent: z.string().describe("What you want to do, e.g. \"login:github.com\" or \"search:amazon.de\""),
130
+ siteDomain: z.string().optional().describe("Domain to scope the search"),
131
+ limit: z.number().default(5).describe("Maximum number of results")
132
+ }, async ({ taskIntent, siteDomain, limit }) => {
133
+ const result = getCore().searchMemory({
134
+ taskIntent,
135
+ siteDomain,
136
+ limit
137
+ });
138
+ return { content: [{
139
+ type: "text",
140
+ text: JSON.stringify(result)
141
+ }] };
142
+ });
143
+ server.tool("browser_stop_session", "Stop the browser session and terminate Chrome. Call this when you're done with browser automation.", { sessionId: z.string().optional().describe("Session ID (uses active session if omitted)") }, async ({ sessionId }) => {
144
+ const sid = sessionId ?? activeSessionId;
145
+ if (!sid) throw new Error("No active session.");
146
+ await getCore().stopSession(sid);
147
+ if (activeSessionId === sid) activeSessionId = void 0;
148
+ return { content: [{
149
+ type: "text",
150
+ text: JSON.stringify({
151
+ ok: true,
152
+ stopped: sid
153
+ })
154
+ }] };
155
+ });
156
+ async function main() {
157
+ const transport = new StdioServerTransport();
158
+ transport.onclose = async () => {
159
+ if (activeSessionId) {
160
+ try {
161
+ await getCore().stopSession(activeSessionId);
162
+ } catch {}
163
+ activeSessionId = void 0;
164
+ }
165
+ };
166
+ await server.connect(transport);
167
+ }
168
+
169
+ //#endregion
170
+ export { main };