@xiaotianxt/skills 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/EXCLUDED.md +42 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/SECURITY.md +23 -0
- package/SOURCES.md +45 -0
- package/bin/skills.mjs +241 -0
- package/package.json +38 -0
- package/skills/1password/SKILL.md +94 -0
- package/skills/1password/agents/openai.yaml +4 -0
- package/skills/1password/references/item-management.md +80 -0
- package/skills/1password/references/op-cli.md +107 -0
- package/skills/apple-calendar-event/SKILL.md +81 -0
- package/skills/apple-calendar-event/agents/openai.yaml +4 -0
- package/skills/apple-calendar-event/scripts/calendar_audit.py +201 -0
- package/skills/apple-calendar-event/scripts/calendar_event.py +164 -0
- package/skills/bro-browser/SKILL.md +118 -0
- package/skills/bro-browser/agents/openai.yaml +4 -0
- package/skills/bro-browser/references/tool-map.md +102 -0
- package/skills/bro-browser/references/workflows.md +146 -0
- package/skills/bro-browser/scripts/bro-call.mjs +189 -0
- package/skills/calendar/SKILL.md +182 -0
- package/skills/calendar/agents/openai.yaml +4 -0
- package/skills/calendar/references/operations.md +255 -0
- package/skills/calendar/scripts/calendar_list_review.py +157 -0
- package/skills/calendar/scripts/event_dedupe_preview.py +155 -0
- package/skills/canvas/SKILL.md +70 -0
- package/skills/canvas/agents/openai.yaml +4 -0
- package/skills/canvas/references/canvas-api.md +76 -0
- package/skills/course-exam-review-planner/SKILL.md +127 -0
- package/skills/cx/SKILL.md +25 -0
- package/skills/gh-fix-ci/LICENSE.txt +201 -0
- package/skills/gh-fix-ci/SKILL.md +81 -0
- package/skills/gh-fix-ci/agents/openai.yaml +6 -0
- package/skills/gh-fix-ci/assets/github-small.svg +3 -0
- package/skills/gh-fix-ci/assets/github.png +0 -0
- package/skills/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- package/skills/gh-review-workflow/SKILL.md +61 -0
- package/skills/gh-review-workflow/agents/openai.yaml +4 -0
- package/skills/gh-review-workflow/references/workflow.md +48 -0
- package/skills/gh-review-workflow/scripts/fetch_review_state.py +222 -0
- package/skills/gh-review-workflow/scripts/resolve_review_threads.py +83 -0
- package/skills/github/SKILL.md +74 -0
- package/skills/github/agents/openai.yaml +6 -0
- package/skills/github/assets/github-small.svg +3 -0
- package/skills/github/assets/github.png +0 -0
- package/skills/gws-calendar/SKILL.md +126 -0
- package/skills/gws-calendar-agenda/SKILL.md +52 -0
- package/skills/gws-calendar-insert/SKILL.md +66 -0
- package/skills/gws-docs/SKILL.md +48 -0
- package/skills/gws-docs-write/SKILL.md +49 -0
- package/skills/gws-drive/SKILL.md +137 -0
- package/skills/gws-drive-upload/SKILL.md +52 -0
- package/skills/gws-gmail/SKILL.md +62 -0
- package/skills/gws-gmail-forward/SKILL.md +55 -0
- package/skills/gws-gmail-reply/SKILL.md +58 -0
- package/skills/gws-gmail-reply-all/SKILL.md +62 -0
- package/skills/gws-gmail-send/SKILL.md +57 -0
- package/skills/gws-gmail-triage/SKILL.md +50 -0
- package/skills/gws-gmail-watch/SKILL.md +58 -0
- package/skills/gws-shared/SKILL.md +27 -0
- package/skills/helium-browser-mcp/SKILL.md +137 -0
- package/skills/helium-browser-mcp/agents/openai.yaml +4 -0
- package/skills/helium-browser-mcp/scripts/obmcp.mjs +92 -0
- package/skills/helium-browser-mcp/scripts/openbrowsermcp-stdio-proxy.mjs +170 -0
- package/skills/learn/SKILL.md +122 -0
- package/skills/learn/agents/openai.yaml +7 -0
- package/skills/learn/assets/AGENTS.template.md +33 -0
- package/skills/learn/assets/errorlog.template.typ +61 -0
- package/skills/learn/assets/reading-sequence.template.md +23 -0
- package/skills/learn/assets/source-index.template.md +17 -0
- package/skills/learn/assets/tasklog.template.typ +57 -0
- package/skills/learn/assets/workbook.template.typ +60 -0
- package/skills/learn/references/learning-science.md +103 -0
- package/skills/learn/scripts/init_learning_workspace.py +70 -0
- package/skills/macos-messages/SKILL.md +258 -0
- package/skills/memory/SKILL.md +33 -0
- package/skills/memory/codex.md +186 -0
- package/skills/memory/opencode.md +164 -0
- package/skills/mimestreamctl/SKILL.md +170 -0
- package/skills/mimestreamctl/agents/openai.yaml +4 -0
- package/skills/mimestreamctl/scripts/mimestreamctl +33 -0
- package/skills/mon/SKILL.md +51 -0
- package/skills/mon/scripts/mon_spend_review.py +458 -0
- package/skills/ocr/SKILL.md +136 -0
- package/skills/ocr/agents/openai.yaml +4 -0
- package/skills/ocr/references/local-ocr-best-practices.md +297 -0
- package/skills/ocr/references/mineru-api.md +159 -0
- package/skills/ocr/scripts/ocr-router +22 -0
- package/skills/ocr/scripts/ocr_router.py +741 -0
- package/skills/panopto-mp4-bulk-download/SKILL.md +57 -0
- package/skills/panopto-mp4-bulk-download/agents/openai.yaml +4 -0
- package/skills/panopto-mp4-bulk-download/references/url-patterns.md +26 -0
- package/skills/panopto-mp4-bulk-download/scripts/panopto_bulk_mp4.sh +213 -0
- package/skills/rust-systems-style/SKILL.md +109 -0
- package/skills/rust-systems-style/agents/openai.yaml +4 -0
- package/skills/rust-systems-style/references/rust-review-checklist.md +77 -0
- package/skills/rust-systems-style/references/style-sources.md +68 -0
- package/skills/ship-ai-native-cli/SKILL.md +76 -0
- package/skills/ship-ai-native-cli/agents/openai.yaml +4 -0
- package/skills/ship-ai-native-cli/references/case-notes.md +83 -0
- package/skills/ship-ai-native-cli/references/product-method.md +82 -0
- package/skills/ship-ai-native-cli/references/release-checklist.md +147 -0
- package/skills/ship-ai-native-cli/references/rust-cli-shape.md +111 -0
- package/skills/telegram-mtproto-session/SKILL.md +125 -0
- package/skills/telegram-mtproto-session/agents/openai.yaml +4 -0
- package/skills/telegram-mtproto-session/scripts/telegram_session.py +687 -0
- package/skills/tg/SKILL.md +173 -0
- package/skills/things3-manager/SKILL.md +116 -0
- package/skills/things3-manager/scripts/things +42 -0
- package/skills/things3-manager/scripts/things_cli.py +514 -0
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +74 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +379 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/yeet/LICENSE.txt +201 -0
- package/skills/yeet/SKILL.md +71 -0
- package/skills/yeet/agents/openai.yaml +6 -0
- package/skills/yeet/assets/yeet-small.svg +3 -0
- package/skills/yeet/assets/yeet.png +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bro-browser
|
|
3
|
+
description: Control and inspect a real local Chromium-family browser through the bro MCP server and WebExtension. Use when Codex needs logged-in browser access, current tab inspection, page text or link extraction, multi-URL background extraction, click/fill/read flows, or browser console and network diagnostics. Do not use for ordinary web lookup when generic web browsing is sufficient.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bro Browser
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use bro when the user wants an agent to operate the user's real local browser, especially for logged-in pages or dynamic pages that generic HTTP fetch cannot read. Treat the browser as sensitive user state.
|
|
11
|
+
|
|
12
|
+
bro is the local Rust MCP server in `/Users/yupeit/dev/bro`. It exposes:
|
|
13
|
+
|
|
14
|
+
- HTTP server: `http://127.0.0.1:3500`
|
|
15
|
+
- MCP endpoint: `http://127.0.0.1:3500/mcp`
|
|
16
|
+
- WebSocket extension bridge: `ws://127.0.0.1:3500/ws`
|
|
17
|
+
- settings token path: `~/.bro/settings.json`
|
|
18
|
+
- unpacked extension path: `/Users/yupeit/dev/bro/extension/dist`
|
|
19
|
+
|
|
20
|
+
Use `127.0.0.1`, not `localhost`, to avoid IPv6 mismatch with local services.
|
|
21
|
+
|
|
22
|
+
## Fast Path First
|
|
23
|
+
|
|
24
|
+
For a known URL or a user request that can be satisfied by opening one page and reading it, start with the highest-level one-shot extraction before doing discovery:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
scripts/bro-call.mjs browser.extract '{"url":"https://example.com","active":false,"cleanup":true,"maxChars":8000}'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use this as the default first move for unknown page content, logged-in pages, and ordinary "look at this page" tasks. It minimizes tool calls and keeps browser state contained by opening a task-owned background tab and cleaning it up.
|
|
31
|
+
|
|
32
|
+
If the user says the target page is already open or asks about the current logged-in browser page, use current-tab extraction directly:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
scripts/bro-call.mjs browser.current.extract '{"maxChars":8000}'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Skip these fast paths only when the task needs foreground interaction, form submission, browser/tab state discovery, or a target URL is not known.
|
|
39
|
+
|
|
40
|
+
## First Checks
|
|
41
|
+
|
|
42
|
+
Use read-only status checks when the fast path is not applicable, when an extraction fails for connection/auth reasons, or when you need to operate on existing tabs:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
curl -sS http://127.0.0.1:3500/status
|
|
46
|
+
scripts/bro-call.mjs browsers_context
|
|
47
|
+
scripts/bro-call.mjs tabs_context '{"all":true}'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Also confirm the bro settings file exists:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ls -l ~/.bro/settings.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If running from outside this skill directory, use the absolute helper path:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
/Users/yupeit/.codex/skills/bro-browser/scripts/bro-call.mjs browsers_context
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If the server is not running, start it from the bro repo:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cargo run --manifest-path /Users/yupeit/dev/bro/Cargo.toml -- serve
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If no extension is connected, load `/Users/yupeit/dev/bro/extension/dist` as an unpacked extension in a Chromium-family browser, open the extension options, set server URL to `ws://127.0.0.1:3500/ws`, and paste the token from `~/.bro/settings.json`. Do not print the token.
|
|
69
|
+
|
|
70
|
+
## Tool Choice
|
|
71
|
+
|
|
72
|
+
Choose the highest-level bro tool that matches the user outcome:
|
|
73
|
+
|
|
74
|
+
- Extract one known URL or unknown page content: call `browser.extract` first, preferably with `active:false`, `cleanup:true`, and a task-appropriate `maxChars`.
|
|
75
|
+
- Extract the current/open page: call `browser.current.extract` first. Do not spend separate calls on `browsers_context` and `tabs_context` unless the current page is ambiguous or extraction fails.
|
|
76
|
+
- Extract many independent URLs: call `browser.batch.extract`.
|
|
77
|
+
- Read text from many URLs when links and diagnostics are not needed: call `browser.batch.run`.
|
|
78
|
+
- Interact with one page over multiple steps: use `browser.flow.start`, `browser.flow.act`, `browser.flow.observe`, then `browser.flow.finish`.
|
|
79
|
+
- Inspect or operate on an existing tab: call `browsers_context`, then `tabs_context`, pin `browserId` and `tabId`, and use raw tab tools.
|
|
80
|
+
- Debug a page or local app: create or pin a tab, then use `read_console_messages`, `read_network_requests`, and `get_response_body`.
|
|
81
|
+
|
|
82
|
+
Use compact extraction defaults. Leave `includeLinks:false` unless URLs are part of the answer or the next crawl step. Leave `includeA11y:false` unless DOM extraction is partial/empty or you need controls and labels. Read `references/workflows.md` for concrete workflow recipes and `references/tool-map.md` for tool arguments and fallback rules.
|
|
83
|
+
|
|
84
|
+
## Safety Rules
|
|
85
|
+
|
|
86
|
+
- Treat tab URLs, page text, screenshots, cookies, account state, extension state, signed URLs, and tokens as sensitive.
|
|
87
|
+
- Never print the bro bearer token. It is acceptable to print the settings file path.
|
|
88
|
+
- Prefer background tabs with `active:false` unless foreground focus is part of the request.
|
|
89
|
+
- After discovery, never operate on "whatever tab is active". Record `browserId` and `tabId`, then pass them explicitly.
|
|
90
|
+
- Track every task-owned tab. Close it with `tabs_close`, `agent_done`, or `browser.flow.finish` unless the user asked to keep it open.
|
|
91
|
+
- Ask before submitting forms, sending messages, uploading files, making purchases, changing account settings, or reading pages likely to contain highly sensitive data.
|
|
92
|
+
- Keep bro generic. Do not add site-specific research policy to the bro bridge; put site workflows in downstream skills or task-local instructions.
|
|
93
|
+
|
|
94
|
+
## Common Calls
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
scripts/bro-call.mjs browser.extract '{"url":"https://example.com","active":false,"cleanup":true,"maxChars":8000}'
|
|
98
|
+
scripts/bro-call.mjs browser.current.extract '{"maxChars":8000}'
|
|
99
|
+
scripts/bro-call.mjs browser.batch.extract '{"urls":["https://example.com/a","https://example.com/b"],"concurrency":4,"maxChars":6000}'
|
|
100
|
+
scripts/bro-call.mjs browser.flow.start '{"url":"https://example.com","active":false}'
|
|
101
|
+
scripts/bro-call.mjs browser.flow.observe '{"sessionId":"SESSION_ID","mode":"text"}'
|
|
102
|
+
scripts/bro-call.mjs browser.flow.finish '{"sessionId":"SESSION_ID","cleanup":true}'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For structured output, add `--json`.
|
|
106
|
+
|
|
107
|
+
## Failure Handling
|
|
108
|
+
|
|
109
|
+
- Connection refused: start `bro serve` or verify the port.
|
|
110
|
+
- Missing `~/.bro/settings.json`: run `bro doctor` or `bro serve` from `/Users/yupeit/dev/bro` to initialize bro local state.
|
|
111
|
+
- Unauthorized MCP call: check that `~/.bro/settings.json` exists, the helper is reading that file, and the extension options use the same token.
|
|
112
|
+
- No browsers connected: connect the bro extension and verify `/status`.
|
|
113
|
+
- Unknown `browserId`: refresh `browsers_context`; do not silently fall back to another browser.
|
|
114
|
+
- `browser.extract` or `browser.current.extract` returns an error or a clearly partial/empty result: inspect diagnostics, then fall back in this order as relevant:
|
|
115
|
+
1. Retry the same facade tool with `includeA11y:true`, `includeLinks:true` only if links matter, a larger `maxChars`, or a higher `minChars`.
|
|
116
|
+
2. Use `browsers_context` and `tabs_context` only if you need to confirm connection state, find an existing tab, or recover a task-owned tab left open by a failed extraction.
|
|
117
|
+
3. Use raw tab tools such as `get_page_text` or `extract_page` after pinning `browserId` and `tabId`.
|
|
118
|
+
4. Use a browser flow when the page requires clicks, form input, navigation, or stateful observation.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Bro Tool Map
|
|
2
|
+
|
|
3
|
+
Prefer facade tools before raw extension tools. They encode bro's cleanup, bounded concurrency, and readiness policy.
|
|
4
|
+
|
|
5
|
+
## Facade Tools
|
|
6
|
+
|
|
7
|
+
`browser.extract`
|
|
8
|
+
|
|
9
|
+
- Use for one URL.
|
|
10
|
+
- Required: `url`.
|
|
11
|
+
- Useful options: `id`, `minChars`, `maxChars`, `maxLinks`, `includeA11y`, `includeLinks`, `cleanup`, `active`, `browserId`.
|
|
12
|
+
- Defaults: `maxChars:8000`, `includeLinks:false`, `includeA11y:false`, `cleanup:true`, `active:false`.
|
|
13
|
+
- Output includes status, text, optional links, and diagnostics.
|
|
14
|
+
|
|
15
|
+
`browser.current.extract`
|
|
16
|
+
|
|
17
|
+
- Use for the current/default active tab when the user says the page is already open.
|
|
18
|
+
- Useful options: `id`, `minChars`, `maxChars`, `maxLinks`, `includeA11y`, `includeLinks`, `browserId`.
|
|
19
|
+
- Defaults: `maxChars:8000`, `includeLinks:false`, `includeA11y:false`.
|
|
20
|
+
- Prefer this over `browsers_context` + `tabs_context` + raw text reads when the current page is unambiguous.
|
|
21
|
+
|
|
22
|
+
`browser.batch.extract`
|
|
23
|
+
|
|
24
|
+
- Use for multiple independent URLs when links or diagnostics matter.
|
|
25
|
+
- Provide either `urls` or `inputs`; do not provide both.
|
|
26
|
+
- Defaults: `concurrency:6`, `maxChars:8000`, `includeLinks:false`, `cleanup:true`, `active:false`.
|
|
27
|
+
- Use `inputs` with `{id,url}` when stable IDs matter.
|
|
28
|
+
|
|
29
|
+
`browser.batch.run`
|
|
30
|
+
|
|
31
|
+
- Use for multiple independent URLs when plain text is enough.
|
|
32
|
+
- Provide either `urls` or `inputs`; do not provide both.
|
|
33
|
+
- Defaults: `concurrency:6`, `timeoutMs:12000`, `cleanup:true`, `active:false`.
|
|
34
|
+
|
|
35
|
+
`browser.flow.start`
|
|
36
|
+
|
|
37
|
+
- Use for a single stateful page interaction.
|
|
38
|
+
- Required: `url`.
|
|
39
|
+
- Defaults: `active:false`, `cleanup:true`.
|
|
40
|
+
- Save `sessionId`.
|
|
41
|
+
|
|
42
|
+
`browser.flow.observe`
|
|
43
|
+
|
|
44
|
+
- Required: `sessionId`.
|
|
45
|
+
- `mode:"text"` by default; use `mode:"a11y"` for controls and labels.
|
|
46
|
+
|
|
47
|
+
`browser.flow.act`
|
|
48
|
+
|
|
49
|
+
- Required: `sessionId`, `steps`.
|
|
50
|
+
- Step types: `goto`, `eval`, `click`, `fill`, `wait`, `read_text`.
|
|
51
|
+
- Stops at first failed step and returns prior step results plus failure location.
|
|
52
|
+
|
|
53
|
+
`browser.flow.finish`
|
|
54
|
+
|
|
55
|
+
- Required: `sessionId`.
|
|
56
|
+
- Use `cleanup:true` unless the user asked to keep the tab.
|
|
57
|
+
|
|
58
|
+
## Raw Browser Tools
|
|
59
|
+
|
|
60
|
+
Use raw tools when operating on an existing tab, debugging, uploading, or using page primitives unavailable through flow.
|
|
61
|
+
|
|
62
|
+
- `browsers_context`: list connected browser instances and browser IDs.
|
|
63
|
+
- `tabs_context`: list tabs; pass `all:true` for a full listing.
|
|
64
|
+
- `tabs_create`: create a tab; default to `active:false`.
|
|
65
|
+
- `tabs_close`: close a known tab ID.
|
|
66
|
+
- `navigate`: navigate a known tab.
|
|
67
|
+
- `get_page_text`: extract `document.body.innerText` from a known tab; pass `maxChars` when only a small slice is needed.
|
|
68
|
+
- `read_page`: read the accessibility tree.
|
|
69
|
+
- `find`: find an accessible element by description.
|
|
70
|
+
- `click_element`: click a `refId`.
|
|
71
|
+
- `fill_element`: clear and type into a `refId`.
|
|
72
|
+
- `form_input`: set an input value by `refId`.
|
|
73
|
+
- `javascript_tool`: evaluate page JavaScript.
|
|
74
|
+
- `read_console_messages`: read console logs and errors.
|
|
75
|
+
- `read_network_requests`: read network records, optionally failed only.
|
|
76
|
+
- `get_response_body`: read a response body by request ID.
|
|
77
|
+
- `file_upload` and `upload_image`: upload through a file input after confirmation.
|
|
78
|
+
- `computer`: screenshot and low-level input when DOM primitives are insufficient. Screenshots default to compact JPEG quality; raise `quality` only when visual detail matters.
|
|
79
|
+
|
|
80
|
+
For tab-targeted raw tools, pass `browserId` and `tabId` explicitly. Do not depend on the active tab.
|
|
81
|
+
|
|
82
|
+
## Helper Script
|
|
83
|
+
|
|
84
|
+
The bundled helper calls bro's Streamable HTTP MCP endpoint and reads the bearer token from `~/.bro/settings.json`.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
scripts/bro-call.mjs <tool-name> [json-arguments]
|
|
88
|
+
scripts/bro-call.mjs --list
|
|
89
|
+
scripts/bro-call.mjs --status
|
|
90
|
+
scripts/bro-call.mjs browser.extract '{"url":"https://example.com"}' --json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `--json` when you need `sessionId`, structured diagnostics, or exact result fields.
|
|
94
|
+
|
|
95
|
+
## Error Interpretation
|
|
96
|
+
|
|
97
|
+
- `No browsers connected`: the server is up but no extension authenticated over `/ws`.
|
|
98
|
+
- `Browser ... not found`: refresh `browsers_context`; never silently retarget.
|
|
99
|
+
- `requires tabId`: choose a tab first with `tabs_context` or create one with `tabs_create`.
|
|
100
|
+
- `partial`: extraction produced some data but readiness did not meet quality thresholds; inspect diagnostics and retry with a better method.
|
|
101
|
+
- Missing `~/.bro/settings.json`: bro has not initialized local state.
|
|
102
|
+
- HTTP 401 or unauthorized: token mismatch between the helper, server, or extension options.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Bro Browser Workflows
|
|
2
|
+
|
|
3
|
+
Use these recipes to translate user outcomes into stable bro calls.
|
|
4
|
+
|
|
5
|
+
## Read-Only Orientation
|
|
6
|
+
|
|
7
|
+
Use this when the user asks about current browser state, open tabs, or whether bro is working.
|
|
8
|
+
|
|
9
|
+
1. Check server status:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
scripts/bro-call.mjs --status
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. Confirm bro local state exists:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
ls -l ~/.bro/settings.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If the settings file is missing, initialize bro with `bro doctor` or `bro serve`.
|
|
22
|
+
|
|
23
|
+
3. Check connected browser instances:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
scripts/bro-call.mjs browsers_context
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
4. List tabs only after a browser is connected:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
scripts/bro-call.mjs tabs_context '{"all":true}'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If the output includes a `browserId`, reuse it in later calls. If you target an existing tab, record its `tabId` and pass both IDs explicitly.
|
|
36
|
+
|
|
37
|
+
## Extract Known URLs
|
|
38
|
+
|
|
39
|
+
Use `browser.extract` for one URL:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
scripts/bro-call.mjs browser.extract '{"url":"https://example.com","maxChars":8000,"cleanup":true}'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `browser.batch.extract` for multiple URLs:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
scripts/bro-call.mjs browser.batch.extract '{"inputs":[{"id":"a","url":"https://example.com/a"},{"id":"b","url":"https://example.com/b"}],"concurrency":4,"maxChars":6000,"cleanup":true}'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Prefer `inputs` with stable IDs when the answer must cite which URL produced each result. Add `includeLinks:true` only when URLs are part of the answer or the next crawl step. Keep `cleanup:true` unless the user needs to inspect the opened tabs.
|
|
52
|
+
|
|
53
|
+
## Sequential Page Interaction
|
|
54
|
+
|
|
55
|
+
Use a flow when a page needs clicks, filling, waiting, or navigation state.
|
|
56
|
+
|
|
57
|
+
1. Start a background flow:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
scripts/bro-call.mjs browser.flow.start '{"url":"https://example.com","active":false,"cleanup":true}' --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2. Save `sessionId` from the structured result.
|
|
64
|
+
|
|
65
|
+
3. Observe the page:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
scripts/bro-call.mjs browser.flow.observe '{"sessionId":"SESSION_ID","mode":"text"}'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
4. Act with ordered steps:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
scripts/bro-call.mjs browser.flow.act '{"sessionId":"SESSION_ID","steps":[{"type":"click","css":"button[type=submit]"},{"type":"wait","ms":500},{"type":"read_text"}]}' --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
5. Finish even if a step failed:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
scripts/bro-call.mjs browser.flow.finish '{"sessionId":"SESSION_ID","cleanup":true}'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `mode:"a11y"` when visible text is insufficient to identify controls. Ask for explicit confirmation before a flow submits data or changes user state.
|
|
84
|
+
|
|
85
|
+
## Existing Tab Inspection
|
|
86
|
+
|
|
87
|
+
Use this when the user asks about an already open tab or says to use their current logged-in browser.
|
|
88
|
+
|
|
89
|
+
If the current tab is the target, do one call:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
scripts/bro-call.mjs browser.current.extract '{"maxChars":8000}'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Only use tab discovery when the current tab is ambiguous, the user mentioned a non-current open tab, or current extraction fails.
|
|
96
|
+
|
|
97
|
+
1. Call `browsers_context`.
|
|
98
|
+
2. Call `tabs_context {"all":true,"browserId":"..."}`.
|
|
99
|
+
3. Identify the target tab from title and URL.
|
|
100
|
+
4. Read the tab with `get_page_text` or `read_page`, always passing `browserId` and `tabId`.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
scripts/bro-call.mjs get_page_text '{"browserId":"BROWSER_ID","tabId":123,"maxChars":12000}'
|
|
106
|
+
scripts/bro-call.mjs read_page '{"browserId":"BROWSER_ID","tabId":123,"filter":"interactive","compact":true,"maxChars":20000}'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Do not use foreground focus as identity after this point. The user may keep using the browser while the agent works.
|
|
110
|
+
|
|
111
|
+
## Debug A Local Web App
|
|
112
|
+
|
|
113
|
+
Use this for localhost app testing when the user wants browser-side console, network, or DOM evidence.
|
|
114
|
+
|
|
115
|
+
1. Create a background tab:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
scripts/bro-call.mjs tabs_create '{"url":"http://127.0.0.1:3000","active":false}' --json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
2. Save `browserId` and `tabId`.
|
|
122
|
+
3. Read page text or accessibility tree.
|
|
123
|
+
4. Inspect console and network:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
scripts/bro-call.mjs read_console_messages '{"browserId":"BROWSER_ID","tabId":123,"clear":false}'
|
|
127
|
+
scripts/bro-call.mjs read_network_requests '{"browserId":"BROWSER_ID","tabId":123,"filter":"failed","timeoutMs":3000}'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
5. Close task-owned tabs:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
scripts/bro-call.mjs tabs_close '{"browserId":"BROWSER_ID","tabId":123}'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
If the task needs screenshots or visual QA, use browser-specific tooling available in the session when required by the frontend workflow; bro is best for browser state and extraction.
|
|
137
|
+
|
|
138
|
+
## Cleanup
|
|
139
|
+
|
|
140
|
+
For raw tabs, call `tabs_close` on every tab you created. For flow sessions, call `browser.flow.finish`. For multi-tab agent work, `agent_done` can signal the session end:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
scripts/bro-call.mjs agent_done '{"browserId":"BROWSER_ID","tabIds":[123,124]}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Do not close tabs that existed before the task unless the user explicitly asked for that.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_PORT = 3500;
|
|
5
|
+
const tokenPath = `${process.env.HOME}/.bro/settings.json`;
|
|
6
|
+
|
|
7
|
+
function usage(exitCode = 2) {
|
|
8
|
+
const text = [
|
|
9
|
+
'usage: bro-call.mjs <tool-name> [json-arguments] [--json] [--port PORT]',
|
|
10
|
+
' bro-call.mjs --list [--json] [--port PORT]',
|
|
11
|
+
' bro-call.mjs --status [--port PORT]',
|
|
12
|
+
'',
|
|
13
|
+
'examples:',
|
|
14
|
+
' bro-call.mjs browsers_context',
|
|
15
|
+
' bro-call.mjs tabs_context \'{"all":true}\'',
|
|
16
|
+
' bro-call.mjs browser.extract \'{"url":"https://example.com"}\' --json',
|
|
17
|
+
].join('\n');
|
|
18
|
+
console.error(text);
|
|
19
|
+
process.exit(exitCode);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let port = DEFAULT_PORT;
|
|
23
|
+
let jsonOutput = false;
|
|
24
|
+
let listTools = false;
|
|
25
|
+
let statusOnly = false;
|
|
26
|
+
const positionals = [];
|
|
27
|
+
|
|
28
|
+
for (let i = 2; i < process.argv.length; i += 1) {
|
|
29
|
+
const arg = process.argv[i];
|
|
30
|
+
if (arg === '--help' || arg === '-h') usage(0);
|
|
31
|
+
if (arg === '--json') {
|
|
32
|
+
jsonOutput = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (arg === '--list') {
|
|
36
|
+
listTools = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (arg === '--status') {
|
|
40
|
+
statusOnly = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (arg === '--port') {
|
|
44
|
+
const next = process.argv[i + 1];
|
|
45
|
+
if (!next) usage();
|
|
46
|
+
port = Number.parseInt(next, 10);
|
|
47
|
+
i += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
positionals.push(arg);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
54
|
+
console.error(`invalid port: ${port}`);
|
|
55
|
+
process.exit(2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const endpoint = `http://127.0.0.1:${port}/mcp`;
|
|
59
|
+
|
|
60
|
+
async function printStatus() {
|
|
61
|
+
const response = await fetch(`http://127.0.0.1:${port}/status`);
|
|
62
|
+
const text = await response.text();
|
|
63
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
|
|
64
|
+
console.log(JSON.stringify(JSON.parse(text), null, 2));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readToken() {
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`failed to read ${tokenPath}: ${error.message}. Run bro doctor or bro serve to initialize bro local state.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (!parsed.token || typeof parsed.token !== 'string') {
|
|
77
|
+
throw new Error(`missing token in ${tokenPath}`);
|
|
78
|
+
}
|
|
79
|
+
return parsed.token;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseArgs(raw) {
|
|
83
|
+
if (!raw) return {};
|
|
84
|
+
try {
|
|
85
|
+
const value = JSON.parse(raw);
|
|
86
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
87
|
+
throw new Error('arguments must be a JSON object');
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new Error(`invalid JSON arguments: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseSseOrJson(text) {
|
|
96
|
+
const payloads = [];
|
|
97
|
+
for (const line of text.split('\n')) {
|
|
98
|
+
if (!line.startsWith('data: ')) continue;
|
|
99
|
+
const payload = line.slice(6).trim();
|
|
100
|
+
if (payload) payloads.push(payload);
|
|
101
|
+
}
|
|
102
|
+
if (payloads.length > 0) return JSON.parse(payloads[payloads.length - 1]);
|
|
103
|
+
return JSON.parse(text);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function rpc(token, sessionId, id, method, params) {
|
|
107
|
+
const headers = {
|
|
108
|
+
'content-type': 'application/json',
|
|
109
|
+
accept: 'application/json, text/event-stream',
|
|
110
|
+
authorization: `Bearer ${token}`,
|
|
111
|
+
};
|
|
112
|
+
if (sessionId) headers['mcp-session-id'] = sessionId;
|
|
113
|
+
|
|
114
|
+
const response = await fetch(endpoint, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers,
|
|
117
|
+
body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
|
|
118
|
+
});
|
|
119
|
+
const text = await response.text();
|
|
120
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
|
|
121
|
+
return {
|
|
122
|
+
sessionId: response.headers.get('mcp-session-id'),
|
|
123
|
+
body: parseSseOrJson(text),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function printResult(body) {
|
|
128
|
+
if (jsonOutput) {
|
|
129
|
+
console.log(JSON.stringify(body, null, 2));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = body.result;
|
|
134
|
+
const structured = result?.structuredContent ?? result?.structured_content;
|
|
135
|
+
let printed = false;
|
|
136
|
+
|
|
137
|
+
if (structured !== undefined) {
|
|
138
|
+
console.log(JSON.stringify(structured, null, 2));
|
|
139
|
+
printed = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const content = result?.content;
|
|
143
|
+
if (Array.isArray(content)) {
|
|
144
|
+
for (const item of content) {
|
|
145
|
+
if (item?.type === 'text') console.log(item.text);
|
|
146
|
+
else console.log(JSON.stringify(item));
|
|
147
|
+
printed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!printed) console.log(JSON.stringify(body, null, 2));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function main() {
|
|
155
|
+
if (statusOnly) {
|
|
156
|
+
if (positionals.length > 0 || listTools) usage();
|
|
157
|
+
await printStatus();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const toolName = positionals[0];
|
|
162
|
+
if (!listTools && !toolName) usage();
|
|
163
|
+
if (positionals.length > (listTools ? 0 : 2)) usage();
|
|
164
|
+
|
|
165
|
+
const token = readToken();
|
|
166
|
+
const init = await rpc(token, null, 1, 'initialize', {
|
|
167
|
+
protocolVersion: '2024-11-05',
|
|
168
|
+
capabilities: {},
|
|
169
|
+
clientInfo: { name: 'codex-bro-helper', version: '0' },
|
|
170
|
+
});
|
|
171
|
+
const sessionId = init.sessionId;
|
|
172
|
+
if (!sessionId) throw new Error('bro did not return an MCP session id');
|
|
173
|
+
|
|
174
|
+
const call = listTools
|
|
175
|
+
? await rpc(token, sessionId, 2, 'tools/list', {})
|
|
176
|
+
: await rpc(token, sessionId, 2, 'tools/call', {
|
|
177
|
+
name: toolName,
|
|
178
|
+
arguments: parseArgs(positionals[1]),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
printResult(call.body);
|
|
182
|
+
const result = call.body.result;
|
|
183
|
+
if (result?.isError === true || result?.is_error === true) process.exitCode = 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main().catch((error) => {
|
|
187
|
+
console.error(error.message);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|