@routecraft/os 0.6.0-canary-20260612184105

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/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # @routecraft/os
2
+
3
+ System-native host capabilities for Routecraft: drive the host machine from route pipelines. This is the home for capabilities that are neither standard protocols nor vendor products, things that touch the host, carry heavy or native peers, and have a security surface.
4
+
5
+ ## Available now
6
+
7
+ - `agentBrowser()` -- browser automation via [agent-browser](https://github.com/nichochar/agent-browser). Migrated here from the former `@routecraft/browser` package.
8
+
9
+ ## Planned
10
+
11
+ - `shell()` -- run shell commands, sandboxed by default, sharing only the environment variables the route node declares it needs.
12
+ - `sandbox()` -- sandboxed execution as a first-class concept.
13
+ - Host primitives such as clipboard, notifications, filesystem-watch, and process management.
14
+
15
+ See [`.standards/package-boundaries.md`](https://github.com/routecraftjs/routecraft/blob/main/.standards/package-boundaries.md) for why these live together and the secure-by-default contract.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # Bun (recommended)
21
+ bun add @routecraft/os agent-browser
22
+
23
+ # npm / pnpm / yarn
24
+ npm install @routecraft/os agent-browser
25
+ pnpm add @routecraft/os agent-browser
26
+ yarn add @routecraft/os agent-browser
27
+ ```
28
+
29
+ `agent-browser` is an optional peer dependency. The `agentBrowser()` adapter loads it lazily, so it is optional at install time but required at runtime for that adapter.
30
+
31
+ ## `agentBrowser(command, options?)`
32
+
33
+ Creates a `Destination` adapter. Use it with `.to()`, `.enrich()`, or `.tap()`.
34
+
35
+ ```typescript
36
+ import { craft, simple, ContextBuilder } from '@routecraft/routecraft';
37
+ import { agentBrowser } from '@routecraft/os';
38
+
39
+ const scrape = craft()
40
+ .id('scrape-page')
41
+ .from(simple({ url: 'https://example.com' }))
42
+ .to(agentBrowser('open', { url: (ex) => ex.body.url }))
43
+ .to(agentBrowser('get', { info: 'text', selector: 'h1' }));
44
+
45
+ const ctx = new ContextBuilder().routes([scrape]).build();
46
+ await ctx.start();
47
+ ```
48
+
49
+ ### Commands
50
+
51
+ | Command | Required Options | Description |
52
+ |---------|-----------------|-------------|
53
+ | `open` | `url` | Navigate to a URL |
54
+ | `click` | `selector` | Click an element |
55
+ | `dblclick` | `selector` | Double-click an element |
56
+ | `fill` | `selector`, `value` | Fill an input field |
57
+ | `type` | `selector`, `value` | Type text into an element |
58
+ | `press` | `key` | Press a keyboard key |
59
+ | `hover` | `selector` | Hover over an element |
60
+ | `focus` | `selector` | Focus an element |
61
+ | `select` | `selector`, `value` | Select a dropdown option |
62
+ | `check` | `selector` | Check a checkbox |
63
+ | `uncheck` | `selector` | Uncheck a checkbox |
64
+ | `scroll` | `direction` | Scroll the page (`up`, `down`, `left`, `right`) |
65
+ | `snapshot` | -- | Get an accessibility snapshot |
66
+ | `screenshot` | -- | Take a screenshot |
67
+ | `eval` | `js` | Evaluate JavaScript in the page |
68
+ | `get` | `info` | Get page data (`text`, `html`, `value`, `title`, `url`, `count`, `attr`, `box`, `styles`) |
69
+ | `wait` | -- | Wait for a selector or timeout |
70
+ | `close` | -- | Close the browser session |
71
+ | `back` | -- | Navigate back |
72
+ | `forward` | -- | Navigate forward |
73
+ | `reload` | -- | Reload the page |
74
+ | `tab` | -- | Manage tabs (`new`, `close`, `list`, switch by index) |
75
+
76
+ ### Base options
77
+
78
+ Every command also accepts these shared options:
79
+
80
+ | Option | Type | Description |
81
+ |--------|------|-------------|
82
+ | `session` | `string \| (ex) => string` | Override the auto-derived session ID |
83
+ | `headed` | `boolean` | Run the browser in headed mode (show window) |
84
+ | `json` | `boolean` | Parse output into `result.parsed` |
85
+ | `args` | `string[]` | Extra CLI flags (escape hatch) |
86
+
87
+ ### Resolvable options
88
+
89
+ String options like `url`, `selector`, and `value` accept either a static value or a function that receives the exchange:
90
+
91
+ ```typescript
92
+ // Static
93
+ agentBrowser('open', { url: 'https://example.com' })
94
+
95
+ // Dynamic from exchange body
96
+ agentBrowser('open', { url: (ex) => ex.body.targetUrl })
97
+ ```
98
+
99
+ ### Session management
100
+
101
+ Sessions are derived from the exchange ID by default, so each exchange gets an isolated browser. Override with the `session` option to share a browser across exchanges:
102
+
103
+ ```typescript
104
+ agentBrowser('open', { url: 'https://example.com', session: 'shared' })
105
+ ```
106
+
107
+ ### Result shape
108
+
109
+ ```typescript
110
+ interface AgentBrowserResult {
111
+ stdout: string; // Text output from the command
112
+ parsed?: unknown; // Parsed JSON (when json: true)
113
+ exitCode: number; // 0 on success, 1 on failure
114
+ }
115
+ ```
116
+
117
+ ## Migrating from `@routecraft/browser`
118
+
119
+ `@routecraft/browser` has been folded into `@routecraft/os`. Update the import:
120
+
121
+ ```diff
122
+ - import { agentBrowser } from '@routecraft/browser';
123
+ + import { agentBrowser } from '@routecraft/os';
124
+ ```
125
+
126
+ The factory, options, and result shape are unchanged.
127
+
128
+ ## License
129
+
130
+ Apache-2.0
131
+
132
+ ## Links
133
+
134
+ - [Documentation](https://routecraft.dev)
135
+ - [GitHub Repository](https://github.com/routecraftjs/routecraft)
136
+ - [Issue Tracker](https://github.com/routecraftjs/routecraft/issues)
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var m=null;async function b(){if(m)return m;try{let[{BrowserManager:e},{executeCommand:s}]=await Promise.all([import('agent-browser/dist/browser.js'),import('agent-browser/dist/actions.js')]);return m={BrowserManager:e,executeCommand:s},m}catch{throw new Error('@routecraft/os: the "agent-browser" package is required for this adapter. Install it with: npm install agent-browser')}}function w(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function y(e,s){if(e!==void 0)return typeof e=="function"?e(s):e}function o(e,s,t){let n=e[s];if(n==null)throw new Error(`agentBrowser adapter: required option "${t}" was missing`);return String(n)}var p=new Map,f=new Map;function k(e,s,t){let n=[],r={id:e};switch(s){case "open":{let a=o(t,"url","open");n.push({...r,action:"navigate",url:a});break}case "click":n.push({...r,action:"click",selector:o(t,"selector","click"),newTab:t.newTab??false});break;case "dblclick":n.push({...r,action:"dblclick",selector:o(t,"selector","dblclick")});break;case "fill":n.push({...r,action:"fill",selector:o(t,"selector","fill"),value:o(t,"value","fill")});break;case "type":n.push({...r,action:"type",selector:o(t,"selector","type"),text:o(t,"value","type")});break;case "press":n.push({...r,action:"press",key:o(t,"key","press")});break;case "hover":n.push({...r,action:"hover",selector:o(t,"selector","hover")});break;case "focus":n.push({...r,action:"focus",selector:o(t,"selector","focus")});break;case "select":n.push({...r,action:"select",selector:o(t,"selector","select"),values:o(t,"value","select")});break;case "check":n.push({...r,action:"check",selector:o(t,"selector","check")});break;case "uncheck":n.push({...r,action:"uncheck",selector:o(t,"selector","uncheck")});break;case "scroll":n.push({...r,action:"scroll",direction:o(t,"direction","scroll"),amount:t.pixels??100});break;case "snapshot":n.push({...r,action:"snapshot",interactive:t.interactive??false});break;case "screenshot":n.push({...r,action:"screenshot",path:t.path??null,fullPage:t.full??false,annotate:t.annotate??false});break;case "eval":n.push({...r,action:"evaluate",script:o(t,"js","eval")});break;case "get":{let a=o(t,"info","get"),c={text:"gettext",html:"innerhtml",value:"inputvalue",title:"title",url:"url",count:"count",attr:"getattribute",box:"boundingbox",styles:"styles"}[a];a==="title"||a==="url"?n.push({...r,action:c}):a==="attr"?n.push({...r,action:"getattribute",selector:o(t,"selector","get.selector"),attribute:o(t,"attr","get.attr")}):n.push({...r,action:c,selector:o(t,"selector",`get.${a}`)});break}case "wait":n.push({...r,action:"wait",selector:t.selector!=null?String(t.selector):void 0,timeout:t.ms!=null?Number(t.ms):void 0});break;case "close":n.push({...r,action:"close"});break;case "back":n.push({...r,action:"back"});break;case "forward":n.push({...r,action:"forward"});break;case "reload":n.push({...r,action:"reload"});break;case "tab":{let a=t.action;a==="list"?n.push({...r,action:"tab_list"}):a==="new"?n.push({...r,action:"tab_new",url:t.url!=null?String(t.url):void 0}):a==="close"?n.push({...r,action:"tab_close",index:t.index!=null?Number(t.index):0}):t.index!=null?n.push({...r,action:"tab_switch",index:Number(t.index)}):n.push({...r,action:"tab_list"});break}}return n}async function B(e,s){let t=p.get(e);if(t)return t;let n=f.get(e);if(n)return n;let r=(async()=>{let{BrowserManager:a,executeCommand:l}=await b(),c=new a;return await l({id:e,action:"launch",headless:!s},c),p.set(e,c),c})();f.set(e,r);try{return await r}finally{f.delete(e);}}async function x(){let{executeCommand:e}=await b();return e}function C(e){return e.snapshot!=null?String(e.snapshot):e.text!=null?String(e.text):e.html!=null?String(e.html):e.value!=null?String(e.value):e.title!=null?String(e.title):e.url!=null?String(e.url):e.result!==void 0?JSON.stringify(e.result):e.count!=null?String(e.count):e.attribute!=null&&e.value!=null?String(e.value):e.box!=null?JSON.stringify(e.box):e.tabs!=null?JSON.stringify(e.tabs):JSON.stringify(e)}function A(e){p.delete(e);}var d=class{constructor(s,t={}){this.command=s;this.options=t;}command;options;adapterId="routecraft.adapter.agent-browser";async send(s){let t=y(this.options.session,s)??w(s.id),n=this.options.headed??false,r={},a=this.options;for(let i of Object.keys(a)){if(i==="session"||i==="headed"||i==="json"||i==="args")continue;let u=a[i];typeof u=="function"?r[i]=u(s):r[i]=u;}let l=k(s.id,this.command,r);if(l.length===0)return {stdout:`agent-browser: no command could be built for action "${this.command}"`,exitCode:1};let c=await B(t,n),v=await x();try{let i={};for(let M of l){let g=await v(M,c);if(!g.success)return {stdout:g.error??"Unknown error",exitCode:1};g.data&&(i=g.data);}this.command==="close"&&typeof c.close=="function"&&await c.close().catch(()=>{});let h={stdout:C(i),exitCode:0};return this.options.json&&(h.parsed=i),h}catch(i){return {stdout:i instanceof Error?i.message:String(i),exitCode:1}}finally{this.command==="close"&&A(t);}}};function S(e,s){return new d(e,s??{})}exports.agentBrowser=S;//# sourceMappingURL=index.cjs.map
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/agent-browser/shared.ts","../src/adapters/agent-browser/destination.ts","../src/adapters/agent-browser/index.ts"],"names":["cached","loadAgentBrowser","BrowserManager","executeCommand","sanitizeSessionId","id","resolve","val","exchange","requireOpt","opts","key","label","v","sessionManagers","sessionLaunches","buildLibraryCommand","command","cmds","base","url","info","action","getOrCreateManager","sessionId","headed","existing","inFlight","launch","manager","loadExecuteCommand","dataToStdout","data","deleteSessionManager","AgentBrowserDestinationAdapter","options","session","resolved","raw","lastData","cmd","res","result","err","agentBrowser"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Exchange } from \"@routecraft/routecraft\";\nimport type { AgentBrowserCommand, Resolvable } from \"./types.ts\";\n\n// agent-browser types resolved lazily so the peer dep stays optional.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype BrowserManager = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExecuteCommandFn = (cmd: any, mgr: any) => Promise<any>;\n\ninterface AgentBrowserLib {\n BrowserManager: new () => BrowserManager;\n executeCommand: ExecuteCommandFn;\n}\n\nlet cached: AgentBrowserLib | null = null;\n\nasync function loadAgentBrowser(): Promise<AgentBrowserLib> {\n if (cached) return cached;\n try {\n const [{ BrowserManager }, { executeCommand }] = await Promise.all([\n import(\"agent-browser/dist/browser.js\"),\n import(\"agent-browser/dist/actions.js\"),\n ]);\n cached = { BrowserManager, executeCommand };\n return cached;\n } catch {\n throw new Error(\n '@routecraft/os: the \"agent-browser\" package is required for this adapter. Install it with: npm install agent-browser',\n );\n }\n}\n\n/**\n * Sanitize exchange id to agent-browser session name: alphanumeric, hyphen, underscore only.\n * @internal\n */\nexport function sanitizeSessionId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\ntype ResolvedCommandOptions = Record<string, unknown>;\n\nexport function resolve<T, V>(\n val: Resolvable<T, V> | undefined,\n exchange: Exchange<T>,\n): V | undefined {\n if (val === undefined) return undefined;\n if (typeof val === \"function\")\n return (val as (e: Exchange<T>) => V)(exchange);\n return val as V;\n}\n\nfunction requireOpt(\n opts: ResolvedCommandOptions,\n key: string,\n label: string,\n): string {\n const v = opts[key];\n if (v === undefined || v === null)\n throw new Error(\n `agentBrowser adapter: required option \"${label}\" was missing`,\n );\n return String(v);\n}\n\n/** Agent-browser protocol command shape (internal API). */\ninterface AgentBrowserProtocolCommand {\n id: string;\n action: string;\n [key: string]: unknown;\n}\n\n/** Session store: one BrowserManager per session id for split/aggregate isolation. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst sessionManagers = new Map<string, any>();\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst sessionLaunches = new Map<string, Promise<any>>();\n\n/**\n * Build agent-browser library command object from our (command, opts).\n */\nexport function buildLibraryCommand(\n id: string,\n command: AgentBrowserCommand,\n opts: ResolvedCommandOptions,\n): AgentBrowserProtocolCommand[] {\n const cmds: AgentBrowserProtocolCommand[] = [];\n const base = { id };\n\n switch (command) {\n case \"open\": {\n const url = requireOpt(opts, \"url\", \"open\");\n cmds.push({ ...base, action: \"navigate\", url });\n break;\n }\n case \"click\":\n cmds.push({\n ...base,\n action: \"click\",\n selector: requireOpt(opts, \"selector\", \"click\"),\n newTab: opts[\"newTab\"] ?? false,\n });\n break;\n case \"dblclick\":\n cmds.push({\n ...base,\n action: \"dblclick\",\n selector: requireOpt(opts, \"selector\", \"dblclick\"),\n });\n break;\n case \"fill\":\n cmds.push({\n ...base,\n action: \"fill\",\n selector: requireOpt(opts, \"selector\", \"fill\"),\n value: requireOpt(opts, \"value\", \"fill\"),\n });\n break;\n case \"type\":\n cmds.push({\n ...base,\n action: \"type\",\n selector: requireOpt(opts, \"selector\", \"type\"),\n text: requireOpt(opts, \"value\", \"type\"),\n });\n break;\n case \"press\":\n cmds.push({\n ...base,\n action: \"press\",\n key: requireOpt(opts, \"key\", \"press\"),\n });\n break;\n case \"hover\":\n cmds.push({\n ...base,\n action: \"hover\",\n selector: requireOpt(opts, \"selector\", \"hover\"),\n });\n break;\n case \"focus\":\n cmds.push({\n ...base,\n action: \"focus\",\n selector: requireOpt(opts, \"selector\", \"focus\"),\n });\n break;\n case \"select\":\n cmds.push({\n ...base,\n action: \"select\",\n selector: requireOpt(opts, \"selector\", \"select\"),\n values: requireOpt(opts, \"value\", \"select\"),\n });\n break;\n case \"check\":\n cmds.push({\n ...base,\n action: \"check\",\n selector: requireOpt(opts, \"selector\", \"check\"),\n });\n break;\n case \"uncheck\":\n cmds.push({\n ...base,\n action: \"uncheck\",\n selector: requireOpt(opts, \"selector\", \"uncheck\"),\n });\n break;\n case \"scroll\":\n cmds.push({\n ...base,\n action: \"scroll\",\n direction: requireOpt(opts, \"direction\", \"scroll\"),\n amount: opts[\"pixels\"] ?? 100,\n });\n break;\n case \"snapshot\":\n cmds.push({\n ...base,\n action: \"snapshot\",\n interactive: opts[\"interactive\"] ?? false,\n });\n break;\n case \"screenshot\":\n cmds.push({\n ...base,\n action: \"screenshot\",\n path: opts[\"path\"] ?? null,\n fullPage: opts[\"full\"] ?? false,\n annotate: opts[\"annotate\"] ?? false,\n });\n break;\n case \"eval\":\n cmds.push({\n ...base,\n action: \"evaluate\",\n script: requireOpt(opts, \"js\", \"eval\"),\n });\n break;\n case \"get\": {\n const info = requireOpt(opts, \"info\", \"get\");\n const actionMap: Record<string, string> = {\n text: \"gettext\",\n html: \"innerhtml\",\n value: \"inputvalue\",\n title: \"title\",\n url: \"url\",\n count: \"count\",\n attr: \"getattribute\",\n box: \"boundingbox\",\n styles: \"styles\",\n };\n const action = actionMap[info];\n if (info === \"title\" || info === \"url\") {\n cmds.push({ ...base, action });\n } else if (info === \"attr\") {\n cmds.push({\n ...base,\n action: \"getattribute\",\n selector: requireOpt(opts, \"selector\", \"get.selector\"),\n attribute: requireOpt(opts, \"attr\", \"get.attr\"),\n });\n } else {\n cmds.push({\n ...base,\n action,\n selector: requireOpt(opts, \"selector\", `get.${info}`),\n });\n }\n break;\n }\n case \"wait\":\n cmds.push({\n ...base,\n action: \"wait\",\n selector:\n opts[\"selector\"] != null ? String(opts[\"selector\"]) : undefined,\n timeout: opts[\"ms\"] != null ? Number(opts[\"ms\"]) : undefined,\n });\n break;\n case \"close\":\n cmds.push({ ...base, action: \"close\" });\n break;\n case \"back\":\n cmds.push({ ...base, action: \"back\" });\n break;\n case \"forward\":\n cmds.push({ ...base, action: \"forward\" });\n break;\n case \"reload\":\n cmds.push({ ...base, action: \"reload\" });\n break;\n case \"tab\": {\n const action = opts[\"action\"];\n if (action === \"list\") cmds.push({ ...base, action: \"tab_list\" });\n else if (action === \"new\")\n cmds.push({\n ...base,\n action: \"tab_new\",\n url: opts[\"url\"] != null ? String(opts[\"url\"]) : undefined,\n });\n else if (action === \"close\")\n cmds.push({\n ...base,\n action: \"tab_close\",\n index: opts[\"index\"] != null ? Number(opts[\"index\"]) : 0,\n });\n else if (opts[\"index\"] != null)\n cmds.push({\n ...base,\n action: \"tab_switch\",\n index: Number(opts[\"index\"]),\n });\n else cmds.push({ ...base, action: \"tab_list\" });\n break;\n }\n default:\n break;\n }\n return cmds;\n}\n\n/** Get or create BrowserManager for session; launch if needed. */\nexport async function getOrCreateManager(\n sessionId: string,\n headed: boolean,\n): Promise<BrowserManager> {\n const existing = sessionManagers.get(sessionId);\n if (existing) return existing;\n\n // Deduplicate concurrent launches for the same session\n const inFlight = sessionLaunches.get(sessionId);\n if (inFlight) return inFlight;\n\n // Set the in-flight promise synchronously before any await to prevent races\n const launch = (async () => {\n const { BrowserManager, executeCommand } = await loadAgentBrowser();\n const manager = new BrowserManager();\n await executeCommand(\n {\n id: sessionId,\n action: \"launch\",\n headless: !headed,\n },\n manager,\n );\n sessionManagers.set(sessionId, manager);\n return manager;\n })();\n\n sessionLaunches.set(sessionId, launch);\n try {\n return await launch;\n } finally {\n sessionLaunches.delete(sessionId);\n }\n}\n\n/** Lazy-load executeCommand for use in destination adapter. */\nexport async function loadExecuteCommand(): Promise<ExecuteCommandFn> {\n const { executeCommand } = await loadAgentBrowser();\n return executeCommand;\n}\n\n/** Map agent-browser response data to stdout string for AgentBrowserResult. */\nexport function dataToStdout(data: Record<string, unknown>): string {\n if (data[\"snapshot\"] != null) return String(data[\"snapshot\"]);\n if (data[\"text\"] != null) return String(data[\"text\"]);\n if (data[\"html\"] != null) return String(data[\"html\"]);\n if (data[\"value\"] != null) return String(data[\"value\"]);\n if (data[\"title\"] != null) return String(data[\"title\"]);\n if (data[\"url\"] != null) return String(data[\"url\"]);\n if (data[\"result\"] !== undefined) return JSON.stringify(data[\"result\"]);\n if (data[\"count\"] != null) return String(data[\"count\"]);\n if (data[\"attribute\"] != null && data[\"value\"] != null)\n return String(data[\"value\"]);\n if (data[\"box\"] != null) return JSON.stringify(data[\"box\"]);\n if (data[\"tabs\"] != null) return JSON.stringify(data[\"tabs\"]);\n return JSON.stringify(data);\n}\n\n/** Delete a session manager (called on close). */\nexport function deleteSessionManager(sessionId: string): void {\n sessionManagers.delete(sessionId);\n}\n","import type { Destination, Exchange } from \"@routecraft/routecraft\";\nimport type {\n AgentBrowserCommand,\n AgentBrowserResult,\n AgentBrowserOptionsMerged,\n} from \"./types.ts\";\nimport {\n resolve,\n sanitizeSessionId,\n buildLibraryCommand,\n getOrCreateManager,\n dataToStdout,\n deleteSessionManager,\n loadExecuteCommand,\n} from \"./shared.ts\";\n\n/**\n * AgentBrowserDestinationAdapter implements the Destination interface for browser automation.\n * Uses the agent-browser library to execute commands against a browser session.\n */\nexport class AgentBrowserDestinationAdapter<\n T = unknown,\n C extends AgentBrowserCommand = AgentBrowserCommand,\n> implements Destination<T, AgentBrowserResult> {\n readonly adapterId = \"routecraft.adapter.agent-browser\";\n\n constructor(\n private readonly command: C,\n private readonly options: AgentBrowserOptionsMerged<\n T,\n C\n > = {} as AgentBrowserOptionsMerged<T, C>,\n ) {}\n\n async send(exchange: Exchange<T>): Promise<AgentBrowserResult> {\n const session =\n resolve(this.options.session, exchange) ?? sanitizeSessionId(exchange.id);\n const headed = this.options.headed ?? false;\n\n const resolved: Record<string, unknown> = {};\n const raw = this.options as Record<string, unknown>;\n for (const key of Object.keys(raw)) {\n if (\n key === \"session\" ||\n key === \"headed\" ||\n key === \"json\" ||\n key === \"args\"\n )\n continue;\n const v = raw[key];\n if (typeof v === \"function\")\n resolved[key] = (v as (e: Exchange<T>) => unknown)(exchange);\n else resolved[key] = v;\n }\n\n const cmds = buildLibraryCommand(exchange.id, this.command, resolved);\n if (cmds.length === 0) {\n return {\n stdout: `agent-browser: no command could be built for action \"${this.command}\"`,\n exitCode: 1,\n };\n }\n\n const manager = await getOrCreateManager(session, headed);\n const executeCommand = await loadExecuteCommand();\n\n try {\n let lastData: Record<string, unknown> = {};\n for (const cmd of cmds) {\n const response = await executeCommand(cmd, manager);\n const res = response as {\n success: boolean;\n data?: Record<string, unknown>;\n error?: string;\n };\n if (!res.success) {\n return {\n stdout: res.error ?? \"Unknown error\",\n exitCode: 1,\n };\n }\n if (res.data) lastData = res.data;\n }\n\n if (this.command === \"close\" && typeof manager.close === \"function\") {\n await manager.close().catch(() => {});\n }\n const stdout = dataToStdout(lastData);\n const result: AgentBrowserResult = { stdout, exitCode: 0 };\n if (this.options.json) {\n result.parsed = lastData;\n }\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { stdout: message, exitCode: 1 };\n } finally {\n if (this.command === \"close\") {\n deleteSessionManager(session);\n }\n }\n }\n}\n","import type { Destination } from \"@routecraft/routecraft\";\nimport type {\n AgentBrowserCommand,\n AgentBrowserBaseOptions,\n AgentBrowserCommandMap,\n AgentBrowserResult,\n AgentBrowserOptionsMerged,\n} from \"./types.ts\";\nimport { AgentBrowserDestinationAdapter } from \"./destination.ts\";\n\n/**\n * Creates a browser destination adapter using the agent-browser library.\n * Session is derived from exchange.id so split/aggregate get isolated sessions.\n * Use with `.to()`, `.enrich()`, or `.tap()`. Requires `agent-browser` as a dependency.\n *\n * @param command - Agent-browser command (e.g. `open`, `click`, `snapshot`, `get`)\n * @param options - Command-specific options plus base options (session, headed, timeout, json)\n * @returns A Destination that runs the command and returns `{ stdout, parsed?, exitCode }`\n *\n * @example\n * ```typescript\n * .to(agentBrowser('open', { url: (ex) => ex.body.url }))\n * .tap(agentBrowser('snapshot', { json: true }))\n * .enrich(agentBrowser('get', { info: 'text', selector: 'h1' }), only((r) => r.stdout, 'title'))\n * ```\n */\nexport function agentBrowser<\n T = unknown,\n C extends AgentBrowserCommand = AgentBrowserCommand,\n>(\n command: C,\n options?: AgentBrowserCommandMap<T>[C] & AgentBrowserBaseOptions<T>,\n): Destination<T, AgentBrowserResult> {\n return new AgentBrowserDestinationAdapter<T, C>(\n command,\n (options ?? {}) as AgentBrowserOptionsMerged<T, C>,\n );\n}\n\n// Re-export types for public API\nexport type {\n AgentBrowserBaseOptions,\n AgentBrowserCommandMap,\n AgentBrowserCommand,\n AgentBrowserResult,\n} from \"./types.ts\";\n\n// Re-export sanitizeSessionId for testing\nexport { sanitizeSessionId } from \"./shared.ts\";\n"]}
@@ -0,0 +1,118 @@
1
+ import { Exchange, Destination } from '@routecraft/routecraft';
2
+
3
+ /** Option value that can be static or resolved from the exchange. */
4
+ type Resolvable<T, V> = V | ((exchange: Exchange<T>) => V);
5
+ /**
6
+ * Shared options available on every command.
7
+ */
8
+ interface AgentBrowserBaseOptions<T = unknown> {
9
+ /** Override auto-session derived from exchange.id */
10
+ session?: Resolvable<T, string>;
11
+ /** Run browser in headed mode (show window). */
12
+ headed?: boolean;
13
+ /** Request JSON output and parse into result.parsed. */
14
+ json?: boolean;
15
+ /** Escape hatch: extra CLI flags (ignored in library mode). */
16
+ args?: string[];
17
+ }
18
+ /**
19
+ * Command-specific options for autocomplete.
20
+ */
21
+ interface AgentBrowserCommandMap<T = unknown> {
22
+ open: {
23
+ url: Resolvable<T, string>;
24
+ };
25
+ click: {
26
+ selector: Resolvable<T, string>;
27
+ newTab?: boolean;
28
+ };
29
+ dblclick: {
30
+ selector: Resolvable<T, string>;
31
+ };
32
+ fill: {
33
+ selector: Resolvable<T, string>;
34
+ value: Resolvable<T, string>;
35
+ };
36
+ type: {
37
+ selector: Resolvable<T, string>;
38
+ value: Resolvable<T, string>;
39
+ };
40
+ press: {
41
+ key: string;
42
+ };
43
+ hover: {
44
+ selector: Resolvable<T, string>;
45
+ };
46
+ focus: {
47
+ selector: Resolvable<T, string>;
48
+ };
49
+ select: {
50
+ selector: Resolvable<T, string>;
51
+ value: Resolvable<T, string>;
52
+ };
53
+ check: {
54
+ selector: Resolvable<T, string>;
55
+ };
56
+ uncheck: {
57
+ selector: Resolvable<T, string>;
58
+ };
59
+ scroll: {
60
+ direction: "up" | "down" | "left" | "right";
61
+ pixels?: number;
62
+ };
63
+ snapshot: {
64
+ interactive?: boolean;
65
+ };
66
+ screenshot: {
67
+ path?: string;
68
+ full?: boolean;
69
+ annotate?: boolean;
70
+ };
71
+ eval: {
72
+ js: Resolvable<T, string>;
73
+ };
74
+ get: {
75
+ info: "text" | "html" | "value" | "title" | "url" | "count" | "attr" | "box" | "styles";
76
+ selector?: Resolvable<T, string>;
77
+ attr?: string;
78
+ };
79
+ wait: {
80
+ selector?: Resolvable<T, string>;
81
+ ms?: number;
82
+ };
83
+ close: Record<string, never>;
84
+ back: Record<string, never>;
85
+ forward: Record<string, never>;
86
+ reload: Record<string, never>;
87
+ tab: {
88
+ action?: "new" | "close" | "list";
89
+ index?: number;
90
+ url?: string;
91
+ };
92
+ }
93
+ type AgentBrowserCommand = keyof AgentBrowserCommandMap;
94
+ interface AgentBrowserResult {
95
+ stdout: string;
96
+ parsed?: unknown;
97
+ exitCode: number;
98
+ }
99
+
100
+ /**
101
+ * Creates a browser destination adapter using the agent-browser library.
102
+ * Session is derived from exchange.id so split/aggregate get isolated sessions.
103
+ * Use with `.to()`, `.enrich()`, or `.tap()`. Requires `agent-browser` as a dependency.
104
+ *
105
+ * @param command - Agent-browser command (e.g. `open`, `click`, `snapshot`, `get`)
106
+ * @param options - Command-specific options plus base options (session, headed, timeout, json)
107
+ * @returns A Destination that runs the command and returns `{ stdout, parsed?, exitCode }`
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * .to(agentBrowser('open', { url: (ex) => ex.body.url }))
112
+ * .tap(agentBrowser('snapshot', { json: true }))
113
+ * .enrich(agentBrowser('get', { info: 'text', selector: 'h1' }), only((r) => r.stdout, 'title'))
114
+ * ```
115
+ */
116
+ declare function agentBrowser<T = unknown, C extends AgentBrowserCommand = AgentBrowserCommand>(command: C, options?: AgentBrowserCommandMap<T>[C] & AgentBrowserBaseOptions<T>): Destination<T, AgentBrowserResult>;
117
+
118
+ export { type AgentBrowserBaseOptions, type AgentBrowserCommand, type AgentBrowserCommandMap, type AgentBrowserResult, agentBrowser };
@@ -0,0 +1,118 @@
1
+ import { Exchange, Destination } from '@routecraft/routecraft';
2
+
3
+ /** Option value that can be static or resolved from the exchange. */
4
+ type Resolvable<T, V> = V | ((exchange: Exchange<T>) => V);
5
+ /**
6
+ * Shared options available on every command.
7
+ */
8
+ interface AgentBrowserBaseOptions<T = unknown> {
9
+ /** Override auto-session derived from exchange.id */
10
+ session?: Resolvable<T, string>;
11
+ /** Run browser in headed mode (show window). */
12
+ headed?: boolean;
13
+ /** Request JSON output and parse into result.parsed. */
14
+ json?: boolean;
15
+ /** Escape hatch: extra CLI flags (ignored in library mode). */
16
+ args?: string[];
17
+ }
18
+ /**
19
+ * Command-specific options for autocomplete.
20
+ */
21
+ interface AgentBrowserCommandMap<T = unknown> {
22
+ open: {
23
+ url: Resolvable<T, string>;
24
+ };
25
+ click: {
26
+ selector: Resolvable<T, string>;
27
+ newTab?: boolean;
28
+ };
29
+ dblclick: {
30
+ selector: Resolvable<T, string>;
31
+ };
32
+ fill: {
33
+ selector: Resolvable<T, string>;
34
+ value: Resolvable<T, string>;
35
+ };
36
+ type: {
37
+ selector: Resolvable<T, string>;
38
+ value: Resolvable<T, string>;
39
+ };
40
+ press: {
41
+ key: string;
42
+ };
43
+ hover: {
44
+ selector: Resolvable<T, string>;
45
+ };
46
+ focus: {
47
+ selector: Resolvable<T, string>;
48
+ };
49
+ select: {
50
+ selector: Resolvable<T, string>;
51
+ value: Resolvable<T, string>;
52
+ };
53
+ check: {
54
+ selector: Resolvable<T, string>;
55
+ };
56
+ uncheck: {
57
+ selector: Resolvable<T, string>;
58
+ };
59
+ scroll: {
60
+ direction: "up" | "down" | "left" | "right";
61
+ pixels?: number;
62
+ };
63
+ snapshot: {
64
+ interactive?: boolean;
65
+ };
66
+ screenshot: {
67
+ path?: string;
68
+ full?: boolean;
69
+ annotate?: boolean;
70
+ };
71
+ eval: {
72
+ js: Resolvable<T, string>;
73
+ };
74
+ get: {
75
+ info: "text" | "html" | "value" | "title" | "url" | "count" | "attr" | "box" | "styles";
76
+ selector?: Resolvable<T, string>;
77
+ attr?: string;
78
+ };
79
+ wait: {
80
+ selector?: Resolvable<T, string>;
81
+ ms?: number;
82
+ };
83
+ close: Record<string, never>;
84
+ back: Record<string, never>;
85
+ forward: Record<string, never>;
86
+ reload: Record<string, never>;
87
+ tab: {
88
+ action?: "new" | "close" | "list";
89
+ index?: number;
90
+ url?: string;
91
+ };
92
+ }
93
+ type AgentBrowserCommand = keyof AgentBrowserCommandMap;
94
+ interface AgentBrowserResult {
95
+ stdout: string;
96
+ parsed?: unknown;
97
+ exitCode: number;
98
+ }
99
+
100
+ /**
101
+ * Creates a browser destination adapter using the agent-browser library.
102
+ * Session is derived from exchange.id so split/aggregate get isolated sessions.
103
+ * Use with `.to()`, `.enrich()`, or `.tap()`. Requires `agent-browser` as a dependency.
104
+ *
105
+ * @param command - Agent-browser command (e.g. `open`, `click`, `snapshot`, `get`)
106
+ * @param options - Command-specific options plus base options (session, headed, timeout, json)
107
+ * @returns A Destination that runs the command and returns `{ stdout, parsed?, exitCode }`
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * .to(agentBrowser('open', { url: (ex) => ex.body.url }))
112
+ * .tap(agentBrowser('snapshot', { json: true }))
113
+ * .enrich(agentBrowser('get', { info: 'text', selector: 'h1' }), only((r) => r.stdout, 'title'))
114
+ * ```
115
+ */
116
+ declare function agentBrowser<T = unknown, C extends AgentBrowserCommand = AgentBrowserCommand>(command: C, options?: AgentBrowserCommandMap<T>[C] & AgentBrowserBaseOptions<T>): Destination<T, AgentBrowserResult>;
117
+
118
+ export { type AgentBrowserBaseOptions, type AgentBrowserCommand, type AgentBrowserCommandMap, type AgentBrowserResult, agentBrowser };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var m=null;async function b(){if(m)return m;try{let[{BrowserManager:e},{executeCommand:s}]=await Promise.all([import('agent-browser/dist/browser.js'),import('agent-browser/dist/actions.js')]);return m={BrowserManager:e,executeCommand:s},m}catch{throw new Error('@routecraft/os: the "agent-browser" package is required for this adapter. Install it with: npm install agent-browser')}}function w(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function y(e,s){if(e!==void 0)return typeof e=="function"?e(s):e}function o(e,s,t){let n=e[s];if(n==null)throw new Error(`agentBrowser adapter: required option "${t}" was missing`);return String(n)}var p=new Map,f=new Map;function k(e,s,t){let n=[],r={id:e};switch(s){case "open":{let a=o(t,"url","open");n.push({...r,action:"navigate",url:a});break}case "click":n.push({...r,action:"click",selector:o(t,"selector","click"),newTab:t.newTab??false});break;case "dblclick":n.push({...r,action:"dblclick",selector:o(t,"selector","dblclick")});break;case "fill":n.push({...r,action:"fill",selector:o(t,"selector","fill"),value:o(t,"value","fill")});break;case "type":n.push({...r,action:"type",selector:o(t,"selector","type"),text:o(t,"value","type")});break;case "press":n.push({...r,action:"press",key:o(t,"key","press")});break;case "hover":n.push({...r,action:"hover",selector:o(t,"selector","hover")});break;case "focus":n.push({...r,action:"focus",selector:o(t,"selector","focus")});break;case "select":n.push({...r,action:"select",selector:o(t,"selector","select"),values:o(t,"value","select")});break;case "check":n.push({...r,action:"check",selector:o(t,"selector","check")});break;case "uncheck":n.push({...r,action:"uncheck",selector:o(t,"selector","uncheck")});break;case "scroll":n.push({...r,action:"scroll",direction:o(t,"direction","scroll"),amount:t.pixels??100});break;case "snapshot":n.push({...r,action:"snapshot",interactive:t.interactive??false});break;case "screenshot":n.push({...r,action:"screenshot",path:t.path??null,fullPage:t.full??false,annotate:t.annotate??false});break;case "eval":n.push({...r,action:"evaluate",script:o(t,"js","eval")});break;case "get":{let a=o(t,"info","get"),c={text:"gettext",html:"innerhtml",value:"inputvalue",title:"title",url:"url",count:"count",attr:"getattribute",box:"boundingbox",styles:"styles"}[a];a==="title"||a==="url"?n.push({...r,action:c}):a==="attr"?n.push({...r,action:"getattribute",selector:o(t,"selector","get.selector"),attribute:o(t,"attr","get.attr")}):n.push({...r,action:c,selector:o(t,"selector",`get.${a}`)});break}case "wait":n.push({...r,action:"wait",selector:t.selector!=null?String(t.selector):void 0,timeout:t.ms!=null?Number(t.ms):void 0});break;case "close":n.push({...r,action:"close"});break;case "back":n.push({...r,action:"back"});break;case "forward":n.push({...r,action:"forward"});break;case "reload":n.push({...r,action:"reload"});break;case "tab":{let a=t.action;a==="list"?n.push({...r,action:"tab_list"}):a==="new"?n.push({...r,action:"tab_new",url:t.url!=null?String(t.url):void 0}):a==="close"?n.push({...r,action:"tab_close",index:t.index!=null?Number(t.index):0}):t.index!=null?n.push({...r,action:"tab_switch",index:Number(t.index)}):n.push({...r,action:"tab_list"});break}}return n}async function B(e,s){let t=p.get(e);if(t)return t;let n=f.get(e);if(n)return n;let r=(async()=>{let{BrowserManager:a,executeCommand:l}=await b(),c=new a;return await l({id:e,action:"launch",headless:!s},c),p.set(e,c),c})();f.set(e,r);try{return await r}finally{f.delete(e);}}async function x(){let{executeCommand:e}=await b();return e}function C(e){return e.snapshot!=null?String(e.snapshot):e.text!=null?String(e.text):e.html!=null?String(e.html):e.value!=null?String(e.value):e.title!=null?String(e.title):e.url!=null?String(e.url):e.result!==void 0?JSON.stringify(e.result):e.count!=null?String(e.count):e.attribute!=null&&e.value!=null?String(e.value):e.box!=null?JSON.stringify(e.box):e.tabs!=null?JSON.stringify(e.tabs):JSON.stringify(e)}function A(e){p.delete(e);}var d=class{constructor(s,t={}){this.command=s;this.options=t;}command;options;adapterId="routecraft.adapter.agent-browser";async send(s){let t=y(this.options.session,s)??w(s.id),n=this.options.headed??false,r={},a=this.options;for(let i of Object.keys(a)){if(i==="session"||i==="headed"||i==="json"||i==="args")continue;let u=a[i];typeof u=="function"?r[i]=u(s):r[i]=u;}let l=k(s.id,this.command,r);if(l.length===0)return {stdout:`agent-browser: no command could be built for action "${this.command}"`,exitCode:1};let c=await B(t,n),v=await x();try{let i={};for(let M of l){let g=await v(M,c);if(!g.success)return {stdout:g.error??"Unknown error",exitCode:1};g.data&&(i=g.data);}this.command==="close"&&typeof c.close=="function"&&await c.close().catch(()=>{});let h={stdout:C(i),exitCode:0};return this.options.json&&(h.parsed=i),h}catch(i){return {stdout:i instanceof Error?i.message:String(i),exitCode:1}}finally{this.command==="close"&&A(t);}}};function S(e,s){return new d(e,s??{})}export{S as agentBrowser};//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/agent-browser/shared.ts","../src/adapters/agent-browser/destination.ts","../src/adapters/agent-browser/index.ts"],"names":["cached","loadAgentBrowser","BrowserManager","executeCommand","sanitizeSessionId","id","resolve","val","exchange","requireOpt","opts","key","label","v","sessionManagers","sessionLaunches","buildLibraryCommand","command","cmds","base","url","info","action","getOrCreateManager","sessionId","headed","existing","inFlight","launch","manager","loadExecuteCommand","dataToStdout","data","deleteSessionManager","AgentBrowserDestinationAdapter","options","session","resolved","raw","lastData","cmd","res","result","err","agentBrowser"],"mappings":"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","file":"index.js","sourcesContent":["import type { Exchange } from \"@routecraft/routecraft\";\nimport type { AgentBrowserCommand, Resolvable } from \"./types.ts\";\n\n// agent-browser types resolved lazily so the peer dep stays optional.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype BrowserManager = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExecuteCommandFn = (cmd: any, mgr: any) => Promise<any>;\n\ninterface AgentBrowserLib {\n BrowserManager: new () => BrowserManager;\n executeCommand: ExecuteCommandFn;\n}\n\nlet cached: AgentBrowserLib | null = null;\n\nasync function loadAgentBrowser(): Promise<AgentBrowserLib> {\n if (cached) return cached;\n try {\n const [{ BrowserManager }, { executeCommand }] = await Promise.all([\n import(\"agent-browser/dist/browser.js\"),\n import(\"agent-browser/dist/actions.js\"),\n ]);\n cached = { BrowserManager, executeCommand };\n return cached;\n } catch {\n throw new Error(\n '@routecraft/os: the \"agent-browser\" package is required for this adapter. Install it with: npm install agent-browser',\n );\n }\n}\n\n/**\n * Sanitize exchange id to agent-browser session name: alphanumeric, hyphen, underscore only.\n * @internal\n */\nexport function sanitizeSessionId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\ntype ResolvedCommandOptions = Record<string, unknown>;\n\nexport function resolve<T, V>(\n val: Resolvable<T, V> | undefined,\n exchange: Exchange<T>,\n): V | undefined {\n if (val === undefined) return undefined;\n if (typeof val === \"function\")\n return (val as (e: Exchange<T>) => V)(exchange);\n return val as V;\n}\n\nfunction requireOpt(\n opts: ResolvedCommandOptions,\n key: string,\n label: string,\n): string {\n const v = opts[key];\n if (v === undefined || v === null)\n throw new Error(\n `agentBrowser adapter: required option \"${label}\" was missing`,\n );\n return String(v);\n}\n\n/** Agent-browser protocol command shape (internal API). */\ninterface AgentBrowserProtocolCommand {\n id: string;\n action: string;\n [key: string]: unknown;\n}\n\n/** Session store: one BrowserManager per session id for split/aggregate isolation. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst sessionManagers = new Map<string, any>();\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst sessionLaunches = new Map<string, Promise<any>>();\n\n/**\n * Build agent-browser library command object from our (command, opts).\n */\nexport function buildLibraryCommand(\n id: string,\n command: AgentBrowserCommand,\n opts: ResolvedCommandOptions,\n): AgentBrowserProtocolCommand[] {\n const cmds: AgentBrowserProtocolCommand[] = [];\n const base = { id };\n\n switch (command) {\n case \"open\": {\n const url = requireOpt(opts, \"url\", \"open\");\n cmds.push({ ...base, action: \"navigate\", url });\n break;\n }\n case \"click\":\n cmds.push({\n ...base,\n action: \"click\",\n selector: requireOpt(opts, \"selector\", \"click\"),\n newTab: opts[\"newTab\"] ?? false,\n });\n break;\n case \"dblclick\":\n cmds.push({\n ...base,\n action: \"dblclick\",\n selector: requireOpt(opts, \"selector\", \"dblclick\"),\n });\n break;\n case \"fill\":\n cmds.push({\n ...base,\n action: \"fill\",\n selector: requireOpt(opts, \"selector\", \"fill\"),\n value: requireOpt(opts, \"value\", \"fill\"),\n });\n break;\n case \"type\":\n cmds.push({\n ...base,\n action: \"type\",\n selector: requireOpt(opts, \"selector\", \"type\"),\n text: requireOpt(opts, \"value\", \"type\"),\n });\n break;\n case \"press\":\n cmds.push({\n ...base,\n action: \"press\",\n key: requireOpt(opts, \"key\", \"press\"),\n });\n break;\n case \"hover\":\n cmds.push({\n ...base,\n action: \"hover\",\n selector: requireOpt(opts, \"selector\", \"hover\"),\n });\n break;\n case \"focus\":\n cmds.push({\n ...base,\n action: \"focus\",\n selector: requireOpt(opts, \"selector\", \"focus\"),\n });\n break;\n case \"select\":\n cmds.push({\n ...base,\n action: \"select\",\n selector: requireOpt(opts, \"selector\", \"select\"),\n values: requireOpt(opts, \"value\", \"select\"),\n });\n break;\n case \"check\":\n cmds.push({\n ...base,\n action: \"check\",\n selector: requireOpt(opts, \"selector\", \"check\"),\n });\n break;\n case \"uncheck\":\n cmds.push({\n ...base,\n action: \"uncheck\",\n selector: requireOpt(opts, \"selector\", \"uncheck\"),\n });\n break;\n case \"scroll\":\n cmds.push({\n ...base,\n action: \"scroll\",\n direction: requireOpt(opts, \"direction\", \"scroll\"),\n amount: opts[\"pixels\"] ?? 100,\n });\n break;\n case \"snapshot\":\n cmds.push({\n ...base,\n action: \"snapshot\",\n interactive: opts[\"interactive\"] ?? false,\n });\n break;\n case \"screenshot\":\n cmds.push({\n ...base,\n action: \"screenshot\",\n path: opts[\"path\"] ?? null,\n fullPage: opts[\"full\"] ?? false,\n annotate: opts[\"annotate\"] ?? false,\n });\n break;\n case \"eval\":\n cmds.push({\n ...base,\n action: \"evaluate\",\n script: requireOpt(opts, \"js\", \"eval\"),\n });\n break;\n case \"get\": {\n const info = requireOpt(opts, \"info\", \"get\");\n const actionMap: Record<string, string> = {\n text: \"gettext\",\n html: \"innerhtml\",\n value: \"inputvalue\",\n title: \"title\",\n url: \"url\",\n count: \"count\",\n attr: \"getattribute\",\n box: \"boundingbox\",\n styles: \"styles\",\n };\n const action = actionMap[info];\n if (info === \"title\" || info === \"url\") {\n cmds.push({ ...base, action });\n } else if (info === \"attr\") {\n cmds.push({\n ...base,\n action: \"getattribute\",\n selector: requireOpt(opts, \"selector\", \"get.selector\"),\n attribute: requireOpt(opts, \"attr\", \"get.attr\"),\n });\n } else {\n cmds.push({\n ...base,\n action,\n selector: requireOpt(opts, \"selector\", `get.${info}`),\n });\n }\n break;\n }\n case \"wait\":\n cmds.push({\n ...base,\n action: \"wait\",\n selector:\n opts[\"selector\"] != null ? String(opts[\"selector\"]) : undefined,\n timeout: opts[\"ms\"] != null ? Number(opts[\"ms\"]) : undefined,\n });\n break;\n case \"close\":\n cmds.push({ ...base, action: \"close\" });\n break;\n case \"back\":\n cmds.push({ ...base, action: \"back\" });\n break;\n case \"forward\":\n cmds.push({ ...base, action: \"forward\" });\n break;\n case \"reload\":\n cmds.push({ ...base, action: \"reload\" });\n break;\n case \"tab\": {\n const action = opts[\"action\"];\n if (action === \"list\") cmds.push({ ...base, action: \"tab_list\" });\n else if (action === \"new\")\n cmds.push({\n ...base,\n action: \"tab_new\",\n url: opts[\"url\"] != null ? String(opts[\"url\"]) : undefined,\n });\n else if (action === \"close\")\n cmds.push({\n ...base,\n action: \"tab_close\",\n index: opts[\"index\"] != null ? Number(opts[\"index\"]) : 0,\n });\n else if (opts[\"index\"] != null)\n cmds.push({\n ...base,\n action: \"tab_switch\",\n index: Number(opts[\"index\"]),\n });\n else cmds.push({ ...base, action: \"tab_list\" });\n break;\n }\n default:\n break;\n }\n return cmds;\n}\n\n/** Get or create BrowserManager for session; launch if needed. */\nexport async function getOrCreateManager(\n sessionId: string,\n headed: boolean,\n): Promise<BrowserManager> {\n const existing = sessionManagers.get(sessionId);\n if (existing) return existing;\n\n // Deduplicate concurrent launches for the same session\n const inFlight = sessionLaunches.get(sessionId);\n if (inFlight) return inFlight;\n\n // Set the in-flight promise synchronously before any await to prevent races\n const launch = (async () => {\n const { BrowserManager, executeCommand } = await loadAgentBrowser();\n const manager = new BrowserManager();\n await executeCommand(\n {\n id: sessionId,\n action: \"launch\",\n headless: !headed,\n },\n manager,\n );\n sessionManagers.set(sessionId, manager);\n return manager;\n })();\n\n sessionLaunches.set(sessionId, launch);\n try {\n return await launch;\n } finally {\n sessionLaunches.delete(sessionId);\n }\n}\n\n/** Lazy-load executeCommand for use in destination adapter. */\nexport async function loadExecuteCommand(): Promise<ExecuteCommandFn> {\n const { executeCommand } = await loadAgentBrowser();\n return executeCommand;\n}\n\n/** Map agent-browser response data to stdout string for AgentBrowserResult. */\nexport function dataToStdout(data: Record<string, unknown>): string {\n if (data[\"snapshot\"] != null) return String(data[\"snapshot\"]);\n if (data[\"text\"] != null) return String(data[\"text\"]);\n if (data[\"html\"] != null) return String(data[\"html\"]);\n if (data[\"value\"] != null) return String(data[\"value\"]);\n if (data[\"title\"] != null) return String(data[\"title\"]);\n if (data[\"url\"] != null) return String(data[\"url\"]);\n if (data[\"result\"] !== undefined) return JSON.stringify(data[\"result\"]);\n if (data[\"count\"] != null) return String(data[\"count\"]);\n if (data[\"attribute\"] != null && data[\"value\"] != null)\n return String(data[\"value\"]);\n if (data[\"box\"] != null) return JSON.stringify(data[\"box\"]);\n if (data[\"tabs\"] != null) return JSON.stringify(data[\"tabs\"]);\n return JSON.stringify(data);\n}\n\n/** Delete a session manager (called on close). */\nexport function deleteSessionManager(sessionId: string): void {\n sessionManagers.delete(sessionId);\n}\n","import type { Destination, Exchange } from \"@routecraft/routecraft\";\nimport type {\n AgentBrowserCommand,\n AgentBrowserResult,\n AgentBrowserOptionsMerged,\n} from \"./types.ts\";\nimport {\n resolve,\n sanitizeSessionId,\n buildLibraryCommand,\n getOrCreateManager,\n dataToStdout,\n deleteSessionManager,\n loadExecuteCommand,\n} from \"./shared.ts\";\n\n/**\n * AgentBrowserDestinationAdapter implements the Destination interface for browser automation.\n * Uses the agent-browser library to execute commands against a browser session.\n */\nexport class AgentBrowserDestinationAdapter<\n T = unknown,\n C extends AgentBrowserCommand = AgentBrowserCommand,\n> implements Destination<T, AgentBrowserResult> {\n readonly adapterId = \"routecraft.adapter.agent-browser\";\n\n constructor(\n private readonly command: C,\n private readonly options: AgentBrowserOptionsMerged<\n T,\n C\n > = {} as AgentBrowserOptionsMerged<T, C>,\n ) {}\n\n async send(exchange: Exchange<T>): Promise<AgentBrowserResult> {\n const session =\n resolve(this.options.session, exchange) ?? sanitizeSessionId(exchange.id);\n const headed = this.options.headed ?? false;\n\n const resolved: Record<string, unknown> = {};\n const raw = this.options as Record<string, unknown>;\n for (const key of Object.keys(raw)) {\n if (\n key === \"session\" ||\n key === \"headed\" ||\n key === \"json\" ||\n key === \"args\"\n )\n continue;\n const v = raw[key];\n if (typeof v === \"function\")\n resolved[key] = (v as (e: Exchange<T>) => unknown)(exchange);\n else resolved[key] = v;\n }\n\n const cmds = buildLibraryCommand(exchange.id, this.command, resolved);\n if (cmds.length === 0) {\n return {\n stdout: `agent-browser: no command could be built for action \"${this.command}\"`,\n exitCode: 1,\n };\n }\n\n const manager = await getOrCreateManager(session, headed);\n const executeCommand = await loadExecuteCommand();\n\n try {\n let lastData: Record<string, unknown> = {};\n for (const cmd of cmds) {\n const response = await executeCommand(cmd, manager);\n const res = response as {\n success: boolean;\n data?: Record<string, unknown>;\n error?: string;\n };\n if (!res.success) {\n return {\n stdout: res.error ?? \"Unknown error\",\n exitCode: 1,\n };\n }\n if (res.data) lastData = res.data;\n }\n\n if (this.command === \"close\" && typeof manager.close === \"function\") {\n await manager.close().catch(() => {});\n }\n const stdout = dataToStdout(lastData);\n const result: AgentBrowserResult = { stdout, exitCode: 0 };\n if (this.options.json) {\n result.parsed = lastData;\n }\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { stdout: message, exitCode: 1 };\n } finally {\n if (this.command === \"close\") {\n deleteSessionManager(session);\n }\n }\n }\n}\n","import type { Destination } from \"@routecraft/routecraft\";\nimport type {\n AgentBrowserCommand,\n AgentBrowserBaseOptions,\n AgentBrowserCommandMap,\n AgentBrowserResult,\n AgentBrowserOptionsMerged,\n} from \"./types.ts\";\nimport { AgentBrowserDestinationAdapter } from \"./destination.ts\";\n\n/**\n * Creates a browser destination adapter using the agent-browser library.\n * Session is derived from exchange.id so split/aggregate get isolated sessions.\n * Use with `.to()`, `.enrich()`, or `.tap()`. Requires `agent-browser` as a dependency.\n *\n * @param command - Agent-browser command (e.g. `open`, `click`, `snapshot`, `get`)\n * @param options - Command-specific options plus base options (session, headed, timeout, json)\n * @returns A Destination that runs the command and returns `{ stdout, parsed?, exitCode }`\n *\n * @example\n * ```typescript\n * .to(agentBrowser('open', { url: (ex) => ex.body.url }))\n * .tap(agentBrowser('snapshot', { json: true }))\n * .enrich(agentBrowser('get', { info: 'text', selector: 'h1' }), only((r) => r.stdout, 'title'))\n * ```\n */\nexport function agentBrowser<\n T = unknown,\n C extends AgentBrowserCommand = AgentBrowserCommand,\n>(\n command: C,\n options?: AgentBrowserCommandMap<T>[C] & AgentBrowserBaseOptions<T>,\n): Destination<T, AgentBrowserResult> {\n return new AgentBrowserDestinationAdapter<T, C>(\n command,\n (options ?? {}) as AgentBrowserOptionsMerged<T, C>,\n );\n}\n\n// Re-export types for public API\nexport type {\n AgentBrowserBaseOptions,\n AgentBrowserCommandMap,\n AgentBrowserCommand,\n AgentBrowserResult,\n} from \"./types.ts\";\n\n// Re-export sanitizeSessionId for testing\nexport { sanitizeSessionId } from \"./shared.ts\";\n"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@routecraft/os",
3
+ "version": "0.6.0-canary-20260612184105",
4
+ "description": "System-native host adapters for Routecraft: browser automation, with sandboxed shell execution and other host capabilities to follow",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts",
21
+ "test": "vitest",
22
+ "test:coverage": "vitest --coverage",
23
+ "prepublishOnly": "bun run build"
24
+ },
25
+ "devDependencies": {
26
+ "@routecraft/routecraft": "workspace:*",
27
+ "@routecraft/testing": "workspace:*",
28
+ "agent-browser": "^0.17.1",
29
+ "vitest": "^4.1.5"
30
+ },
31
+ "peerDependencies": {
32
+ "@routecraft/routecraft": "0.6.0-canary-20260612184105",
33
+ "agent-browser": "^0.17.1"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "agent-browser": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "keywords": [
41
+ "routecraft",
42
+ "os",
43
+ "shell",
44
+ "browser",
45
+ "automation",
46
+ "agent-browser",
47
+ "playwright"
48
+ ],
49
+ "author": "routecraftjs",
50
+ "license": "Apache-2.0",
51
+ "homepage": "https://routecraft.dev",
52
+ "bugs": "https://github.com/routecraftjs/routecraft/issues",
53
+ "repository": "https://github.com/routecraftjs/routecraft",
54
+ "publishConfig": {
55
+ "access": "public"
56
+ }
57
+ }