browsirai 0.1.0 → 0.2.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **Your browser. Your sessions. Your agent.**
7
7
 
8
- An MCP server that connects AI coding agents to your real Chrome via CDP. Like `httpd` or `sshd`, browsirai runs as a daemon it survives Chrome crashes, maintains state, and is always ready.
8
+ An MCP server + CLI that connects AI coding agents to your real Chrome via CDP. Use as an MCP server for LLM-driven automation, or as a standalone CLI for direct browser control from the terminal.
9
9
 
10
10
  ## Why browsirai?
11
11
 
@@ -170,6 +170,65 @@ mcpServers:
170
170
  ```
171
171
  </details>
172
172
 
173
+ ## CLI Mode
174
+
175
+ browsirai also works as a standalone CLI — no LLM required. Same commands, same Chrome connection.
176
+
177
+ ```bash
178
+ browsirai open example.com
179
+ browsirai snapshot -i
180
+ browsirai click @e5
181
+ browsirai fill @e2 "hello world"
182
+ browsirai press Enter
183
+ browsirai eval "document.title"
184
+ ```
185
+
186
+ ### Commands (30)
187
+
188
+ | Category | Commands |
189
+ |----------|----------|
190
+ | **Navigation** | `open` (goto, navigate), `back`, `scroll`, `wait`, `tab` (tabs), `close`, `resize` |
191
+ | **Observation** | `snapshot`, `screenshot`, `html`, `eval`, `find`, `source`, `console`, `network` |
192
+ | **Actions** | `click`, `fill`, `type`, `press` (key), `hover`, `drag`, `select`, `upload`, `dialog` |
193
+ | **Network** | `route`, `abort`, `unroute`, `save`, `load`, `diff` |
194
+
195
+ ### Short Flags
196
+
197
+ ```bash
198
+ browsirai snapshot -i # interactive elements only
199
+ browsirai snapshot -c # compact output
200
+ browsirai snapshot -d 3 # depth limit
201
+ browsirai snapshot -s "main" # scope to selector
202
+ browsirai screenshot -o ss.png # save to file
203
+ ```
204
+
205
+ ### Positional Arguments
206
+
207
+ ```bash
208
+ browsirai click @e5 # ref (not --ref=@e5)
209
+ browsirai click "#submit" # CSS selector
210
+ browsirai fill @e2 "text" # ref + value
211
+ browsirai drag @e1 @e2 # source + target
212
+ browsirai select @e3 "option1" # ref + value(s)
213
+ browsirai scroll down # direction
214
+ browsirai resize 1280 720 # width height
215
+ ```
216
+
217
+ ### Workflow Example
218
+
219
+ ```bash
220
+ browsirai open github.com/login
221
+ browsirai snapshot -i
222
+ # @e12 textbox "Username"
223
+ # @e15 textbox "Password"
224
+ # @e18 button "Sign in"
225
+ browsirai fill @e12 "user@example.com"
226
+ browsirai fill @e15 "password"
227
+ browsirai click @e18
228
+ browsirai wait --url="github.com/dashboard"
229
+ browsirai snapshot -i
230
+ ```
231
+
173
232
  ## Features
174
233
 
175
234
  | Feature | Description |
package/dist/bin.js CHANGED
@@ -4602,6 +4602,8 @@ __export(doctor_exports, {
4602
4602
  });
4603
4603
  import { execSync as execSync3 } from "child_process";
4604
4604
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
4605
+ import { homedir as homedir4 } from "os";
4606
+ import { join as join4 } from "path";
4605
4607
  function findChromePath() {
4606
4608
  const whichCommands = process.platform === "win32" ? ["where chrome", "where chromium", "where msedge"] : ["which google-chrome", "which chromium", "which chromium-browser", "which chrome"];
4607
4609
  for (const cmd of whichCommands) {
@@ -4638,41 +4640,52 @@ function checkNodeVersion() {
4638
4640
  message: ok ? `v${versionStr} (>= 18 required)` : `v${versionStr} \u2014 Node.js >= 18 is required`
4639
4641
  };
4640
4642
  }
4641
- function checkPlatformConfig() {
4642
- const detection = detectPlatform();
4643
- const config = getInstallConfig(detection.platform);
4644
- const configPath = config.configPath;
4645
- if (!existsSync4(configPath)) {
4646
- return {
4647
- ok: false,
4648
- label: "Platform config",
4649
- message: `Config file not found: ${configPath} (platform: ${detection.platform})`
4650
- };
4651
- }
4643
+ function resolvePath(p) {
4644
+ if (p.startsWith("~/")) return join4(homedir4(), p.slice(2));
4645
+ return p;
4646
+ }
4647
+ function checkConfigFile(filePath, configKey, platform) {
4648
+ const resolved = resolvePath(filePath);
4649
+ if (!existsSync4(resolved)) return null;
4652
4650
  try {
4653
- const content = readFileSync4(configPath, "utf-8");
4651
+ const content = readFileSync4(resolved, "utf-8");
4654
4652
  const parsed = JSON.parse(content);
4655
- const servers = parsed[config.configKey];
4653
+ const servers = parsed[configKey];
4656
4654
  if (servers && typeof servers === "object" && "browsirai" in servers) {
4657
4655
  return {
4658
4656
  ok: true,
4659
4657
  label: "Platform config",
4660
- message: `browsirai found in ${configPath} (platform: ${detection.platform})`
4658
+ message: `browsirai found in ${resolved} (platform: ${platform})`
4661
4659
  };
4662
4660
  }
4663
- return {
4664
- ok: false,
4665
- label: "Platform config",
4666
- message: `browsirai not found in ${configPath} under "${config.configKey}"`
4667
- };
4661
+ return null;
4668
4662
  } catch {
4669
- return {
4670
- ok: false,
4671
- label: "Platform config",
4672
- message: `Failed to parse config file: ${configPath}`
4673
- };
4663
+ return null;
4674
4664
  }
4675
4665
  }
4666
+ function checkPlatformConfig() {
4667
+ const detection = detectPlatform();
4668
+ const config = getInstallConfig(detection.platform);
4669
+ const detected = checkConfigFile(config.configPath, config.configKey, detection.platform);
4670
+ if (detected) return detected;
4671
+ const knownPaths = [
4672
+ { path: "~/.mcp.json", key: "mcpServers", platform: "claude-code (global)" },
4673
+ { path: ".mcp.json", key: "mcpServers", platform: "claude-code" },
4674
+ { path: ".cursor/mcp.json", key: "mcpServers", platform: "cursor" },
4675
+ { path: "~/.gemini/settings.json", key: "mcpServers", platform: "gemini-cli" },
4676
+ { path: ".vscode/mcp.json", key: "servers", platform: "vscode-copilot" },
4677
+ { path: "~/.codeium/windsurf/mcp_config.json", key: "mcpServers", platform: "windsurf" }
4678
+ ];
4679
+ for (const entry of knownPaths) {
4680
+ const result = checkConfigFile(entry.path, entry.key, entry.platform);
4681
+ if (result) return result;
4682
+ }
4683
+ return {
4684
+ ok: false,
4685
+ label: "Platform config",
4686
+ message: `browsirai not found in any known config file (detected: ${detection.platform})`
4687
+ };
4688
+ }
4676
4689
  async function runDoctor() {
4677
4690
  const checks = [];
4678
4691
  const method = getInstallMethod();
@@ -4750,13 +4763,13 @@ __export(install_exports, {
4750
4763
  import { intro, select, confirm, spinner, outro, isCancel, cancel, log } from "@clack/prompts";
4751
4764
  import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
4752
4765
  import { resolve, dirname as dirname2 } from "path";
4753
- import { homedir as homedir4 } from "os";
4766
+ import { homedir as homedir5 } from "os";
4754
4767
  function resolveConfigPath(configPath, scope) {
4755
4768
  if (configPath.startsWith("~")) {
4756
- return resolve(homedir4(), configPath.slice(2));
4769
+ return resolve(homedir5(), configPath.slice(2));
4757
4770
  }
4758
4771
  if (scope === "global") {
4759
- return resolve(homedir4(), configPath);
4772
+ return resolve(homedir5(), configPath);
4760
4773
  }
4761
4774
  return resolve(process.cwd(), configPath);
4762
4775
  }
@@ -4768,7 +4781,7 @@ async function runInstall() {
4768
4781
  const config2 = getInstallConfig(opt.value);
4769
4782
  const paths = [
4770
4783
  resolve(process.cwd(), config2.configPath),
4771
- config2.configPath.startsWith("~") ? resolve(homedir4(), config2.configPath.slice(2)) : resolve(homedir4(), config2.configPath)
4784
+ config2.configPath.startsWith("~") ? resolve(homedir5(), config2.configPath.slice(2)) : resolve(homedir5(), config2.configPath)
4772
4785
  ];
4773
4786
  for (const filePath2 of paths) {
4774
4787
  if (existsSync5(filePath2)) {
@@ -4901,21 +4914,9 @@ async function runCli(args) {
4901
4914
  break;
4902
4915
  }
4903
4916
  default: {
4904
- console.error(`Unknown command: ${command}`);
4905
- console.log(
4906
- [
4907
- `browsirai v${VERSION}`,
4908
- "",
4909
- "Usage: browsirai [command]",
4910
- "",
4911
- "Commands:",
4912
- " (none) Start the MCP server (default)",
4913
- " doctor Run diagnostics",
4914
- " install Install browser integration",
4915
- " --version Print version",
4916
- ""
4917
- ].join("\n")
4918
- );
4917
+ const cliUrl = new URL("./cli/run.js", import.meta.url);
4918
+ const { runCLI } = await import(cliUrl.href);
4919
+ await runCLI(args);
4919
4920
  break;
4920
4921
  }
4921
4922
  }