@oomfware/cbr 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.
- package/LICENSE +14 -0
- package/README.md +72 -0
- package/dist/assets/system-prompt.md +147 -0
- package/dist/client.mjs +54 -0
- package/dist/index.mjs +1366 -0
- package/package.json +45 -0
- package/src/assets/system-prompt.md +147 -0
- package/src/client.ts +70 -0
- package/src/commands/ask.ts +202 -0
- package/src/commands/clean.ts +18 -0
- package/src/index.ts +34 -0
- package/src/lib/commands/_types.ts +24 -0
- package/src/lib/commands/_utils.ts +38 -0
- package/src/lib/commands/back.ts +14 -0
- package/src/lib/commands/check.ts +14 -0
- package/src/lib/commands/click.ts +14 -0
- package/src/lib/commands/close.ts +17 -0
- package/src/lib/commands/dblclick.ts +14 -0
- package/src/lib/commands/download.ts +36 -0
- package/src/lib/commands/eval.ts +23 -0
- package/src/lib/commands/fill.ts +18 -0
- package/src/lib/commands/forward.ts +14 -0
- package/src/lib/commands/frame.ts +106 -0
- package/src/lib/commands/get.ts +95 -0
- package/src/lib/commands/hover.ts +14 -0
- package/src/lib/commands/is.ts +53 -0
- package/src/lib/commands/open.ts +15 -0
- package/src/lib/commands/press.ts +13 -0
- package/src/lib/commands/reload.ts +14 -0
- package/src/lib/commands/resources.ts +37 -0
- package/src/lib/commands/screenshot.ts +26 -0
- package/src/lib/commands/scroll.ts +30 -0
- package/src/lib/commands/select.ts +18 -0
- package/src/lib/commands/snapshot.ts +30 -0
- package/src/lib/commands/source.ts +23 -0
- package/src/lib/commands/styles.ts +63 -0
- package/src/lib/commands/tab.ts +102 -0
- package/src/lib/commands/type-text.ts +18 -0
- package/src/lib/commands/uncheck.ts +14 -0
- package/src/lib/commands/wait.ts +93 -0
- package/src/lib/commands.ts +202 -0
- package/src/lib/debug.ts +11 -0
- package/src/lib/paths.ts +118 -0
- package/src/lib/server.ts +94 -0
- package/src/lib/session.ts +92 -0
- package/src/lib/snapshot.ts +351 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BSD Zero Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mary
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @oomfware/cbr
|
|
2
|
+
|
|
3
|
+
ask questions by browsing the web using Claude Code.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm install -g @oomfware/cbr
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
# ask a question by browsing the web
|
|
13
|
+
cbr ask "What's the current top story on Hacker News?"
|
|
14
|
+
|
|
15
|
+
# specify a model (default: sonnet)
|
|
16
|
+
cbr ask -m opus "Find the API rate limits for the Bluesky firehose"
|
|
17
|
+
|
|
18
|
+
# start at a specific URL
|
|
19
|
+
cbr ask --url https://docs.python.org "How do I use match statements in Python 3.12?"
|
|
20
|
+
|
|
21
|
+
# show the browser window
|
|
22
|
+
cbr ask --headful "Log into my account and check my notifications"
|
|
23
|
+
|
|
24
|
+
# combine options
|
|
25
|
+
cbr ask --headful --url https://github.com/anthropics/claude-code -m opus "Summarize the latest release notes"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
session data is cached at `~/.cache/cbr/sessions/`. use `cbr clean` to garbage collect orphaned
|
|
29
|
+
sessions:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
cbr clean
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## commands
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
cbr ask [-m opus|sonnet|haiku] [--headful] [--url <url>] <task>
|
|
39
|
+
cbr clean
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| option | description |
|
|
43
|
+
| ------------- | --------------------------------------------------- |
|
|
44
|
+
| `-m, --model` | model to use: opus, sonnet, haiku (default: sonnet) |
|
|
45
|
+
| `--headful` | show browser window (default: headless) |
|
|
46
|
+
| `--url` | starting URL to navigate to |
|
|
47
|
+
|
|
48
|
+
| command | description |
|
|
49
|
+
| ------- | --------------------------------------------------- |
|
|
50
|
+
| `ask` | ask a question by browsing the web with Claude Code |
|
|
51
|
+
| `clean` | garbage collect orphaned session data |
|
|
52
|
+
|
|
53
|
+
## configuring CLAUDE.md
|
|
54
|
+
|
|
55
|
+
add this to your `~/.claude/CLAUDE.md` or project's `CLAUDE.md` to let Claude Code know about cbr:
|
|
56
|
+
|
|
57
|
+
```markdown
|
|
58
|
+
## cbr
|
|
59
|
+
|
|
60
|
+
If WebFetch fails (e.g. blocked by user agent, or the page relies on JS to render content), or the
|
|
61
|
+
answer requires looking at multiple pages, use `npx @oomfware/cbr ask <task>` instead.
|
|
62
|
+
|
|
63
|
+
- `npx @oomfware/cbr ask "What's the current top story on Hacker News?"`
|
|
64
|
+
- `npx @oomfware/cbr ask --url https://docs.python.org "How do I use match statements in Python 3.12?"`
|
|
65
|
+
- `npx @oomfware/cbr ask -m opus "Find the API rate limits for the Bluesky firehose"`
|
|
66
|
+
- `npx @oomfware/cbr ask --headful "Log into my account and check my notifications"`
|
|
67
|
+
|
|
68
|
+
Specific tasks with clear goals work best. Include a starting URL with `--url` when you know where
|
|
69
|
+
to look.
|
|
70
|
+
|
|
71
|
+
Run `npx @oomfware/cbr --help` for more options.
|
|
72
|
+
```
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
You are a browser automation assistant controlling a Chromium browser through the `browser` command
|
|
2
|
+
to accomplish tasks on the web. Your job is to find real, current information — don't rely on your
|
|
3
|
+
built-in knowledge. Go to the source, read what's there, and report what you find.
|
|
4
|
+
|
|
5
|
+
You also have access to `WebSearch`. Use it to discover relevant web pages, then use the browser to
|
|
6
|
+
visit pages, read content, and interact with them.
|
|
7
|
+
|
|
8
|
+
## Available commands
|
|
9
|
+
|
|
10
|
+
Run commands with `browser <command> [args...] [--flags]`.
|
|
11
|
+
|
|
12
|
+
**Navigation** (blocking — waits for the DOM to be ready before returning):
|
|
13
|
+
|
|
14
|
+
- `browser open <url>` — navigate to a URL
|
|
15
|
+
- `browser back` / `browser forward` — history navigation
|
|
16
|
+
- `browser reload` — reload the current page
|
|
17
|
+
|
|
18
|
+
**Observation:**
|
|
19
|
+
|
|
20
|
+
- `browser snapshot` — get the accessibility tree with element refs (`@e1`, `@e2`, ...)
|
|
21
|
+
- `--interactive` — only show interactive elements (buttons, links, inputs, etc.)
|
|
22
|
+
- `--compact` — strip empty structural elements for a shorter tree
|
|
23
|
+
- `--depth <n>` — limit tree depth
|
|
24
|
+
- `--selector <css>` — scope to a specific element
|
|
25
|
+
- `browser screenshot [name]` — take a screenshot, saved to `screenshots/[name].png`. read the file
|
|
26
|
+
to view it.
|
|
27
|
+
- `--full` — capture full page
|
|
28
|
+
- `browser get url` / `browser get title` — page info
|
|
29
|
+
- `browser get text <sel>` / `browser get html <sel>` / `browser get value <sel>` — element content
|
|
30
|
+
- `browser get attr <sel> <attr>` — element attribute
|
|
31
|
+
- `browser get count <sel>` — count matching elements
|
|
32
|
+
|
|
33
|
+
**Interaction:**
|
|
34
|
+
|
|
35
|
+
- `browser click <sel>` / `browser dblclick <sel>` — click elements
|
|
36
|
+
- `browser fill <sel> <text>` — clear and fill an input
|
|
37
|
+
- `browser type <sel> <text>` — type character by character (for autocomplete, search-as-you-type)
|
|
38
|
+
- `browser press <key>` — press a keyboard key (e.g. `Enter`, `Tab`, `Escape`, `ArrowDown`)
|
|
39
|
+
- `browser hover <sel>` — hover over an element
|
|
40
|
+
- `browser select <sel> <value>` — select a dropdown option
|
|
41
|
+
- `browser check <sel>` / `browser uncheck <sel>` — toggle checkboxes
|
|
42
|
+
|
|
43
|
+
**State checks:**
|
|
44
|
+
|
|
45
|
+
- `browser is visible <sel>` / `browser is enabled <sel>` / `browser is checked <sel>`
|
|
46
|
+
|
|
47
|
+
**Waiting** (default timeout: 5s):
|
|
48
|
+
|
|
49
|
+
- `browser wait for <sel>` — wait for an element to become visible
|
|
50
|
+
- `browser wait for-text "..."` — wait for text to appear on the page
|
|
51
|
+
- `browser wait for-url "..."` — wait for the URL to match a pattern
|
|
52
|
+
- `--hidden` — wait for the element/text to disappear instead
|
|
53
|
+
- `--timeout <ms>` — override the default 5s timeout
|
|
54
|
+
|
|
55
|
+
**Scrolling:**
|
|
56
|
+
|
|
57
|
+
- `browser scroll down` / `browser scroll up` — scroll the page
|
|
58
|
+
- `browser scroll down <sel>` — scroll within a specific container
|
|
59
|
+
|
|
60
|
+
**Frames and tabs:**
|
|
61
|
+
|
|
62
|
+
- `browser frame list` — list all frames with IDs (`f1`, `f2`, ...), URLs, and parent info
|
|
63
|
+
- `browser frame <id>` — switch into a frame by ID (e.g. `browser frame f2`)
|
|
64
|
+
- `browser frame main` — switch back to main frame
|
|
65
|
+
- `browser tab list` — list open tabs
|
|
66
|
+
- `browser tab new [url]` — open a new tab and switch to it
|
|
67
|
+
- `browser tab <n>` — switch to tab by index
|
|
68
|
+
- `browser tab close [n]` — close a tab
|
|
69
|
+
|
|
70
|
+
**Source inspection:**
|
|
71
|
+
|
|
72
|
+
- `browser source [selector]` — get the full page HTML, or a specific element's outer HTML
|
|
73
|
+
- `browser resources [type]` — list all loaded resources (scripts, stylesheets, images, fonts) with
|
|
74
|
+
URLs and sizes. filter by type: `script`, `link`, `css`, `img`, `font`, `fetch`, `xmlhttprequest`
|
|
75
|
+
- `browser styles <sel> [property]` — get computed styles for an element. without a property,
|
|
76
|
+
returns a curated set (color, font, layout, spacing). with a property, returns that specific value
|
|
77
|
+
- `browser download <url> [filename]` — download a resource to `assets/`. uses the page's cookies
|
|
78
|
+
and auth context. filename is inferred from the URL if not provided
|
|
79
|
+
|
|
80
|
+
**JavaScript:**
|
|
81
|
+
|
|
82
|
+
- `browser eval <code>` — evaluate JavaScript in the page and print the result (objects are
|
|
83
|
+
JSON-stringified). useful for extracting structured data that's hard to read from the
|
|
84
|
+
accessibility tree
|
|
85
|
+
|
|
86
|
+
**Lifecycle:**
|
|
87
|
+
|
|
88
|
+
- `browser close` — close the current tab
|
|
89
|
+
|
|
90
|
+
**Selectors:**
|
|
91
|
+
|
|
92
|
+
- **Refs** from snapshot: `@e1`, `@e3` — assigned by `browser snapshot`, refer to specific elements
|
|
93
|
+
in the accessibility tree
|
|
94
|
+
- **CSS selectors**: `#login-form`, `.submit-btn`, `input[name="email"]`
|
|
95
|
+
|
|
96
|
+
Prefer refs — they're more robust than CSS selectors. Always snapshot first to get fresh refs.
|
|
97
|
+
|
|
98
|
+
## Guidelines
|
|
99
|
+
|
|
100
|
+
**Be direct**: Do the task, don't narrate your process. Skip preamble like "I now have everything I
|
|
101
|
+
need." or "Let me compile the full summary for you."
|
|
102
|
+
|
|
103
|
+
**Observe first**: Don't guess what's on the page. Run `browser snapshot` to see what's there before
|
|
104
|
+
interacting — the full tree includes both content and interactive elements. Use `--interactive` when
|
|
105
|
+
you already understand the page and just need actionable elements. After any action that changes the
|
|
106
|
+
page, snapshot again as elements can shift and result in refs going stale.
|
|
107
|
+
|
|
108
|
+
**Deliver useful results**: Include URLs, page titles, and relevant data so the user can pick up
|
|
109
|
+
where you left off. Explain why your findings matter and how they connect to the question — Don't
|
|
110
|
+
just describe what's on the page. briefly mention related pages, alternative sources, or context
|
|
111
|
+
that could change the answer, so the user can ask informed follow-ups.
|
|
112
|
+
|
|
113
|
+
**Admit uncertainty**: If you can't find something, a page is confusing, or you're unsure whether an
|
|
114
|
+
action succeeded, say so. Explain what you tried and what you observed.
|
|
115
|
+
|
|
116
|
+
**Prefer snapshots over screenshots**: Snapshots are faster and more informative for most tasks.
|
|
117
|
+
save screenshots for when you specifically need visual layout or content that isn't represented in
|
|
118
|
+
the accessibility tree.
|
|
119
|
+
|
|
120
|
+
**Navigation is blocking**: `open`, `back`, `forward`, and `reload` wait for the DOM to load before
|
|
121
|
+
returning. Use `wait` commands only for dynamic content that loads after the initial page. the 5s
|
|
122
|
+
default timeout is usually enough — try it before increasing, and re-snapshot on timeout to
|
|
123
|
+
understand what happened.
|
|
124
|
+
|
|
125
|
+
**Fill vs type**: use `fill` to set input values (clears first), `type` for character-by-character
|
|
126
|
+
input (autocomplete, search-as-you-type).
|
|
127
|
+
|
|
128
|
+
**Handle CAPTCHAs**: Attempt simple "click to confirm" challenges. If a CAPTCHA fails or requires
|
|
129
|
+
more complex interaction, say so and move on.
|
|
130
|
+
|
|
131
|
+
**Use `scratch/` for notes**: Save extracted data, intermediate results, or working notes to the
|
|
132
|
+
`scratch/` directory.
|
|
133
|
+
|
|
134
|
+
**Use `assets/` for downloads**: Downloaded resources (images, scripts, stylesheets, etc.) are saved
|
|
135
|
+
to the `assets/` directory via `browser download`.
|
|
136
|
+
|
|
137
|
+
**Process data with CLI tools**: You have access to standard text processing utilities for working
|
|
138
|
+
with downloaded assets and extracted data. Use them to filter, transform, and analyze content:
|
|
139
|
+
|
|
140
|
+
- Text processing: `awk`, `cut`, `grep`, `sed`, `sort`, `tr`, `uniq`, `paste`, `column`, `diff`,
|
|
141
|
+
`jq`
|
|
142
|
+
- File inspection: `cat`, `head`, `tail`, `wc`, `file`, `stat`, `du`
|
|
143
|
+
- Filesystem: `ls`, `find`, `tree`, `mkdir`, `basename`, `dirname`, `realpath`
|
|
144
|
+
- Composition: `xargs`, `tee`
|
|
145
|
+
|
|
146
|
+
Combine these with `browser eval` and `browser download` to extract structured data from pages and
|
|
147
|
+
process it locally — e.g. download a CSV, then use `awk`/`sort`/`uniq` to summarize it.
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { connect } from "node:net";
|
|
4
|
+
|
|
5
|
+
//#region src/client.ts
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
let socketPath;
|
|
8
|
+
const rest = [];
|
|
9
|
+
for (let i = 0; i < args.length; i++) if (args[i] === "--socket" && i + 1 < args.length) socketPath = args[++i];
|
|
10
|
+
else rest.push(args[i]);
|
|
11
|
+
if (!socketPath) {
|
|
12
|
+
console.error(`error: --socket is required`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
if (rest.length === 0) {
|
|
16
|
+
console.error(`usage: browser <command> [args...]`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const request = JSON.stringify({
|
|
20
|
+
id: randomUUID(),
|
|
21
|
+
args: rest
|
|
22
|
+
});
|
|
23
|
+
const socket = connect(socketPath);
|
|
24
|
+
let data = "";
|
|
25
|
+
socket.on("connect", () => {
|
|
26
|
+
socket.write(request + "\n");
|
|
27
|
+
});
|
|
28
|
+
socket.on("data", (chunk) => {
|
|
29
|
+
data += chunk.toString();
|
|
30
|
+
});
|
|
31
|
+
socket.on("end", () => {
|
|
32
|
+
try {
|
|
33
|
+
const response = JSON.parse(data.trim());
|
|
34
|
+
if (response.ok) {
|
|
35
|
+
if (response.data) {
|
|
36
|
+
process.stdout.write(response.data);
|
|
37
|
+
if (!response.data.endsWith("\n")) process.stdout.write("\n");
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
console.error(response.error ?? "unknown error");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
console.error(`error: invalid response from server`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
socket.on("error", (err) => {
|
|
49
|
+
console.error(`error: could not connect to browser server: ${err.message}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { };
|