Beachpatrol, a browser you can drive from the terminal
🏝️ A CLI Tool to Automate Your Everyday Web Browser.
At a glance
- What is it?
- Beachpatrol launches a Chromium or Firefox browser you can keep as your daily driver and then control from the command line with Playwright scripts. It suits people who want their logged-in browser to be scriptable, and it will not change how a browser handles profiles.
- Who is it for?
- Beachpatrol suits someone who already keeps a persistent logged-in browser and wants to script routine tasks against it from the terminal, including people who want a job search pipeline like SHOBR built on the same commands. It does not suit anyone who wants a fresh browser per script, who needs Chromium profile switching, or who would rather not keep a browser open as a service.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A browser as a long-running service, not a per-script launch
Beachpatrol is a CLI tool to replace and automate your everyday web browser. Running beachpatrol launches a Chromium or Firefox browser that can be controlled externally through Playwright scripts, and the README is explicit that you can use it as your daily driver, since it works like a regular browser.
That is the whole design in one sentence, and it is a different model from the usual browser automation. A typical Playwright or Puppeteer script starts a browser, does one thing and closes it. Beachpatrol keeps a browser running and hands you a control channel into it. Because the browser is your daily browser, it is already logged into your sites, so the script does not have to authenticate anywhere, and closing the window does not log you out. The README makes that explicit: if you close it and run beachpatrol again, you are still logged in to all your sites.
The stated motivation is that essential software should be fully automatable and web browsers are not. The examples given cover checking email, checking bank transactions, downloading a file, dumping the text of the current tab into a file, filling an online form, checking messages in a social or work chat platform, and dropping browser tasks into Bash or Python scripts. The project also points at SHOBR, a Python CLI that discovers, enriches, screens, writes PDFs and tracks job applications through its own beachpatrol commands, which is the clearest evidence that the command layer is meant to be a reusable interface rather than a demo.
How a command reaches the tab you are looking at
Beachpatrol resolves commands from two places, in order: the user commands home at $XDG_DATA_HOME/beachpatrol/commands/, which the README marks as recommended, and the bundled commands directory of the installation. A command is a JavaScript or TypeScript file, and to run one you call beachmsg with the script name and any arguments, for example beachmsg search "your search terms here".
Every command must export a default async function, or a generator if you want to stream, which is the entry point. The function receives an object holding context and activePage as its first argument, followed by any arguments passed on the command line. The context is the Playwright BrowserContext and activePage is the currently focused Page, or null when nothing is focused. From there you work with the ordinary Playwright API, which means you can act on the focused tab, move to an existing tab, open a new one or open a headless tab.
The return value is sent back to the client as output, and the details are nicely specified. To stream several lines you write the function as a generator and yield each line, with the caveat that a return inside a generator is ignored because iterator semantics do not treat it as part of the message. A thrown error is printed by the client, which then exits with code 1. That contract, a value or a stream out and an error out, is enough to build shell pipelines on top, which is what the OS integration examples rely on.
Three ways to organise the commands directory
The commands home is a normal directory, and the README describes three common setups for it, each with a different consequence for how dependencies resolve.
The plain setup is to add a package.json with "type": "module" to the commands home and run npm install for whatever the commands need. The second setup keeps the commands in a repository elsewhere and makes the commands home a symlink to that repository root, in which case every .js file there becomes a command and dependencies resolve from the repository's own node_modules. The third setup symlinks individual files, for example ln -s ~/path/to/my-command.js ~/.local/share/beachpatrol/commands/, which is the option to use when you want commands that live in separate folders with their own dependencies.
That third option is the interesting one for anyone building a larger library of automations, because it lets a command own its dependency tree instead of sharing one flat node_modules directory. The cost is a directory of symlinks to maintain.
One practical detail saves a lot of confusion: you can edit your command files and re-run them without restarting the beachpatrol server, and your changes are picked up automatically. For a tool whose whole point is iterating on small scripts, that is the difference between a one-second feedback loop and restarting a browser every time.
Writing a first command and checking the control channel
The README walks through a web search command. The script opens a new tab with context.newPage(), navigates to a search URL built from the arguments, and leaves the new page focused:
export default async ({ context, activePage }, ...searchTerms) => {
const page = await context.newPage();
await page.goto(
`https://www.google.com/search?q=${encodeURIComponent(searchTerms.join(" "))}`,
);
};The pattern generalises. A command that acts on the current tab uses activePage, a command that expects particular tabs open can find them through the context, and the choice of a visible tab or a headless one is left to you.
Before writing anything, there is a built-in check. Running beachmsg smoke-test runs the pre-installed test command, which performs the classic Selenium smoke test, opening the Selenium documentation example page, filling the form and submitting it. If the output reads Form submitted, the control channel works end to end.
Installation is four steps. Clone the repository, move into the folder, run npm install to install dependencies, and run make to install symlinks to the executables, which land in /usr/local/bin by default and should be on your PATH. On Windows you can run npm install -g . instead. The requirements are Linux with Wayland or X11, macOS or Windows, Node.js LTS at v22, and Chromium or Firefox, which Playwright installs automatically.
The extension that removes the terminal round trip
The obvious friction in the model above is the switch between the browser window and the terminal. The beachpatrol-browser-extension exists for exactly that, and it is the piece that makes the tool usable as a daily driver rather than a scripting exercise.
The extension's UI lets you select a command and call it with arguments, and it calls beachmsg itself through the browser extension's Native Messaging feature, so no terminal is involved. Beyond the invocation, it highlights the commands that are meant to run on the current URL, which matters when you have a library of commands and more than one browser tab open. It also provides GUI elements for common situations, with pagination and dropdowns named as the examples, and it supports hotkeys.
Native Messaging is the mechanism worth understanding, since it explains how a browser extension can execute something outside the browser. The extension talks to a host program that the browser launches, and the host here is beachmsg. The result is a two-way path: the CLI can drive the browser, and the browser can invoke commands that the CLI owns.
The extension is not required for the core workflow. Someone who prefers to script everything from a terminal can ignore it entirely, and the command layer works the same either way.
Profiles, incognito and the limits of the browser itself
The launch flags are minimal. beachpatrol --profile <profile-name> opens a specific profile and beachpatrol --incognito starts incognito, and the browser can be launched in the background with beachpatrol &.
One limitation is called out carefully. Due to a Chromium limitation, it is not possible to use the in-browser profile features to switch profiles, although the README notes this might be fixed in the future. That is a hard limit imposed by Chromium rather than by Beachpatrol, and it is worth knowing before you design a workflow around several identities. Chromium's own profile picker cannot be driven the way the rest of the browser can, so the profile flag and the profile switcher are not the same thing.
The dependency list in package.json also deserves a note. Alongside playwright and @playwright/test at version 1.62.1, the project depends on playwright-extra, patchright and puppeteer-extra-plugin-stealth. The presence of a stealth plugin in a browser automation tool tells you what kind of sites it expects to be used on, and it is a signal that some of the sites people automate will present a bot check. That is worth weighing before you point this at an account that matters to you.
The project is MIT licensed and at version 0.1.0, with the last release tagged on 2026-09-27 and the last push to the repository on 2026-10-15. At that version, expect the command API to move.
Editorial conclusion
Beachpatrol suits someone who already keeps a persistent logged-in browser and wants to script routine tasks against it from the terminal, including people who want a job search pipeline like SHOBR built on the same commands. It does not suit anyone who wants a fresh browser per script, who needs Chromium profile switching, or who would rather not keep a browser open as a service. Before you adopt it, run beachmsg smoke-test to confirm the control channel works on your machine, check that Node.js 22 and npm 10 are what you have, and read the Chromium profile limitation in the README, since in-browser profile switching will not work.
Frequently asked questions
What is Beachpatrol and how does it differ from a normal Playwright script?
Beachpatrol keeps a Chromium or Firefox browser running as your daily driver and lets you control it from the CLI with Playwright scripts, so the browser is already logged in to your sites. A normal Playwright script launches its own browser per run, which means no persistent session and no logged-in state to reuse.
How do I install Beachpatrol and confirm it works?
Clone the repository, cd into the folder, run npm install and then make, which installs symlinks to the executables in /usr/local/bin by default. On Windows you can run npm install -g . instead. To check the control channel, run beachmsg smoke-test, which performs the classic Selenium smoke test and prints Form submitted when it works.
Where do Beachpatrol commands live and what do they look like?
Commands are resolved from the user commands home at $XDG_DATA_HOME/beachpatrol/commands/, which defaults to ~/.local/share/beachpatrol/commands/, and then from the bundled commands directory. Each command exports a default async function receiving { context, activePage } plus any command line arguments, and it automates the browser through the Playwright API.
Can I switch browser profiles with Beachpatrol?
You can launch a named profile with beachpatrol --profile <profile-name>, but the README notes that a Chromium limitation makes it impossible to use the in-browser profile features to switch profiles, and says this might be fixed in the future.
What are the requirements for running Beachpatrol?
Linux with Wayland or X11, macOS or Windows, Node.js LTS at v22 with npm 10 or newer, and Chromium or Firefox, which Playwright installs automatically. The package.json declares engines of node >=22.0.0 and npm >=10.0.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sebastiancarlos-beachpatrol)Was this useful?