feat(browser): Brave Origin works for real-profile browsing and default-browser detection

Extends the real-profile machinery (PR #95620) to Brave Origin — Brave's
standalone paid build with a fully separate install identity:

- new canonical key 'brave-origin' in _CHROMIUM_BROWSERS
- Windows: BraveOHTML ProgId -> brave-origin; channel ProgIds BraveOBHTML/
  BraveODHTML/BraveOSHTM fail closed (identifiers from brave-core
  install_static)
- macOS: com.brave.Browser.origin bundle id (exact match); .beta/.dev/
  .nightly channel bundles fail closed; /Applications/Brave Origin.app
- Linux: brave-origin.desktop matched BEFORE the bare 'brave' fragment
  (substring scan would otherwise resolve an Origin default to stable
  Brave and drive the wrong profile — #95549 wrong-principal invariant);
  brave-origin-{beta,nightly,dev} fail closed
- profile dirs: BraveSoftware/Brave-Origin on all three OSes (per
  brave-core kProductPathName + Homebrew cask zap paths)
- /browser connect launch tables: Brave Origin split into its OWN group
  so a 'brave' executable lookup can never resolve to the Origin binary
- user-facing strings/docs/desktop tooltip updated

Tests: progid/bundle/desktop map params + data-dir resolution for all
three OSes; 125 passed in the three browser test files.
This commit is contained in:
Teknium
2026-08-29 18:03:29 -07:00
parent bca0a865c8
commit b6d535dd88
8 changed files with 93 additions and 9 deletions
+1 -1
View File
@@ -558,7 +558,7 @@ export const FIELD_DESCRIPTIONS: Record<string, string> = defineFieldCopy({
timezone: 'IANA timezone identifier. Blank uses the system timezone.',
browser: {
useRealProfile:
"Local browsing uses your real logins. Hermes copies your default browser's profile (cookies, logins, preferences) into a managed snapshot and drives it with its packaged Chromium — your live profile is never opened directly, and the copy is refreshed from it on each run. Also lets the agent open a local real-profile session on request even when a cloud browser backend is configured. Only Chromium browsers (Chrome, Edge, Brave, Chromium) are supported; a non-Chromium default fails with a clear message. Off by default."
"Local browsing uses your real logins. Hermes copies your default browser's profile (cookies, logins, preferences) into a managed snapshot and drives it with its packaged Chromium — your live profile is never opened directly, and the copy is refreshed from it on each run. Also lets the agent open a local real-profile session on request even when a cloud browser backend is configured. Only Chromium browsers (Chrome, Edge, Brave, Brave Origin, Chromium) are supported; a non-Chromium default fails with a clear message. Off by default."
},
agent: {
imageInputMode: 'Controls how image attachments are sent to the model.',
+56 -3
View File
@@ -27,6 +27,7 @@ _DARWIN_APPS = (
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"/Applications/Chromium.app/Contents/MacOS/Chromium",
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
"/Applications/Brave Origin.app/Contents/MacOS/Brave Origin",
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
)
@@ -37,6 +38,13 @@ _WINDOWS_BROWSER_GROUPS = (
(("Chromium", "Application", "chrome.exe"), ("Chromium", "Application", "chromium.exe")),
),
(("brave.exe", "brave"), (("BraveSoftware", "Brave-Browser", "Application", "brave.exe"),)),
(
("brave-origin.exe", "brave-origin"),
(
("BraveSoftware", "Brave-Origin", "Application", "brave.exe"),
("BraveSoftware", "Brave-Origin", "Application", "brave-origin.exe"),
),
),
(("msedge.exe", "msedge"), (("Microsoft", "Edge", "Application", "msedge.exe"),)),
)
@@ -53,7 +61,7 @@ _LINUX_BROWSER_GROUPS = (
("/usr/bin/chromium-browser", "/usr/bin/chromium"),
),
(
("brave-browser", "brave-browser-stable", "brave", "brave-origin", "brave-origin-nightly"),
("brave-browser", "brave-browser-stable", "brave"),
(
"/usr/bin/brave-browser",
"/usr/bin/brave-browser-stable",
@@ -61,9 +69,20 @@ _LINUX_BROWSER_GROUPS = (
"/snap/bin/brave",
"/opt/brave.com/brave/brave-browser",
"/opt/brave.com/brave/brave",
"/opt/brave-bin/brave",
),
),
# Brave Origin is a SEPARATE product identity (side-by-side installable
# with Brave), so it gets its own group: the executable fallback in
# chromium_executable() matches by group, and mixing Origin binaries into
# the brave group would let a "brave" lookup resolve to the Origin binary
# (or vice versa) — driving the wrong browser's profile.
(
("brave-origin", "brave-origin-nightly"),
(
"/usr/bin/brave-origin",
"/opt/brave.com/brave-origin/brave-origin",
"/opt/brave.com/brave-origin-nightly/brave-origin",
"/opt/brave-bin/brave",
),
),
(
@@ -93,7 +112,12 @@ _LINUX_INSTALL_PATHS = tuple(path for _, paths in _LINUX_BROWSER_GROUPS for path
# ---------------------------------------------------------------------------
# Canonical Chromium browser keys we support for real-profile driving.
_CHROMIUM_BROWSERS = ("chrome", "edge", "brave", "chromium")
# ``brave-origin`` is Brave's standalone paid build: same Chromium core, but a
# fully distinct install identity (BraveSoftware/Brave-Origin product path,
# ``BraveOHTML`` ProgId, ``com.brave.Browser.origin`` bundle id) so it
# side-by-side installs with regular Brave — its profile is NOT under
# Brave-Browser and must never be conflated with the ``brave`` key.
_CHROMIUM_BROWSERS = ("chrome", "edge", "brave", "chromium", "brave-origin")
# Windows UserChoice ProgId prefixes → canonical browser key. Matched
# case-insensitively by prefix so version suffixes (e.g. ``ChromeHTML.X``)
@@ -104,6 +128,9 @@ _CHROMIUM_BROWSERS = ("chrome", "edge", "brave", "chromium")
_WINDOWS_PROGID_MAP = (
("chromehtml", "chrome"),
("msedgehtm", "edge"),
# Brave Origin stable is ``BraveOHTML`` (brave-core install_static). Listed
# before ``bravehtml`` for clarity; the prefixes don't collide either way.
("braveohtml", "brave-origin"),
("bravehtml", "brave"),
("chromiumhtm", "chromium"),
)
@@ -117,6 +144,9 @@ _WINDOWS_CHANNEL_PROGIDS = (
"chromebhtml", "chromedhtml", "chromesshtml", "chromecanaryhtml",
"msedgebhtml", "msedgedhtml", "msedgechtml",
"bravebetahtml", "bravenightlyhtml",
# Brave Origin channels (brave-core install_static): Beta=BraveOBHTML,
# Dev=BraveODHTML, Nightly/SxS=BraveOSHTM (no trailing L — 10-char cap).
"braveobhtml", "braveodhtml", "braveoshtm",
)
# Linux xdg default-web-browser .desktop name fragments → canonical STABLE key.
@@ -128,6 +158,11 @@ _LINUX_DESKTOP_MAP = (
("google-chrome", "chrome"),
("com.google.chrome", "chrome"),
("chromium", "chromium"),
# ORDER MATTERS: ``brave-origin.desktop`` contains the bare ``brave``
# fragment, so the substring scan must hit the Origin entry first —
# otherwise an Origin default resolves to stable Brave and real-profile
# mode drives a DIFFERENT browser's profile (wrong-principal, #95549).
("brave-origin", "brave-origin"),
("brave", "brave"),
("microsoft-edge", "edge"),
("com.microsoft.edge", "edge"),
@@ -141,6 +176,7 @@ _LINUX_CHANNEL_FRAGMENTS = (
"com.google.chrome.beta", "com.google.chrome.dev", "com.google.chrome.canary",
"microsoft-edge-beta", "microsoft-edge-dev", "microsoft-edge-canary",
"brave-browser-beta", "brave-browser-nightly", "brave-browser-dev",
"brave-origin-beta", "brave-origin-nightly", "brave-origin-dev",
)
# Where sandboxed Linux packages keep the profile instead of $XDG_CONFIG_HOME.
@@ -161,6 +197,10 @@ _DARWIN_BUNDLE_MAP = (
("com.google.chrome", "chrome"),
("com.microsoft.edgemac", "edge"),
("com.brave.browser", "brave"),
# Brave Origin reuses the Brave bundle id with an ``.origin`` suffix
# (Homebrew cask: com.brave.Browser.origin). Exact matching keeps it from
# ever being read as plain ``com.brave.browser``.
("com.brave.browser.origin", "brave-origin"),
("org.chromium.chromium", "chromium"),
)
@@ -169,6 +209,8 @@ _DARWIN_CHANNEL_BUNDLES = (
"com.google.chrome.beta", "com.google.chrome.dev", "com.google.chrome.canary",
"com.microsoft.edgemac.beta", "com.microsoft.edgemac.dev", "com.microsoft.edgemac.canary",
"com.brave.browser.beta", "com.brave.browser.nightly",
"com.brave.browser.origin.beta", "com.brave.browser.origin.dev",
"com.brave.browser.origin.nightly",
)
# Sentinel returned when the OS default is a recognized-but-unsupported
@@ -201,6 +243,11 @@ def _real_profile_relparts(browser: str) -> tuple:
("Chromium", "User Data"),
"chromium",
),
"brave-origin": (
("BraveSoftware", "Brave-Origin"),
("BraveSoftware", "Brave-Origin", "User Data"),
"BraveSoftware/Brave-Origin",
),
}[browser]
@@ -258,6 +305,7 @@ def chromium_executable(browser: str, system: str | None = None) -> str | None:
"chrome": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"chromium": "/Applications/Chromium.app/Contents/MacOS/Chromium",
"brave": "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
"brave-origin": "/Applications/Brave Origin.app/Contents/MacOS/Brave Origin",
"edge": "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
}[browser]
return app if os.path.isfile(app) else None
@@ -266,6 +314,10 @@ def chromium_executable(browser: str, system: str | None = None) -> str | None:
"chrome": (("Google", "Chrome", "Application", "chrome.exe"),),
"chromium": (("Chromium", "Application", "chrome.exe"), ("Chromium", "Application", "chromium.exe")),
"brave": (("BraveSoftware", "Brave-Browser", "Application", "brave.exe"),),
"brave-origin": (
("BraveSoftware", "Brave-Origin", "Application", "brave.exe"),
("BraveSoftware", "Brave-Origin", "Application", "brave-origin.exe"),
),
"edge": (("Microsoft", "Edge", "Application", "msedge.exe"),),
}[browser]
bases = [
@@ -280,6 +332,7 @@ def chromium_executable(browser: str, system: str | None = None) -> str | None:
"chrome": ("google-chrome", "google-chrome-stable"),
"chromium": ("chromium-browser", "chromium"),
"brave": ("brave-browser", "brave-browser-stable", "brave"),
"brave-origin": ("brave-origin",),
"edge": ("microsoft-edge", "microsoft-edge-stable"),
}[browser]
for name in linux:
+1 -1
View File
@@ -595,7 +595,7 @@ DEFAULT_CONFIG = {
# never contends with the user's running browser. Turning this back off
# deletes the snapshot store (~/.hermes/browser-profile/) so copied
# credentials don't outlive consent. Only Chromium-family default
# browsers are supported (Chrome, Edge, Brave, Chromium); a non-Chromium
# browsers are supported (Chrome, Edge, Brave, Brave Origin, Chromium); a non-Chromium
# default (e.g. Firefox) fails closed with a clear message. Default
# false. Also gates the browser_exec ``local`` argument, which forces a
# real-profile local session even under a cloud browser backend. Toggle
+1 -1
View File
@@ -13298,7 +13298,7 @@ def main():
)
browser_close.add_argument(
"--browser",
help="Override detected default browser (chrome/edge/brave/chromium)",
help="Override detected default browser (chrome/edge/brave/brave-origin/chromium)",
)
def _dispatch_browser(_args):
@@ -97,8 +97,11 @@ class TestDetectDefaultDarwin:
[
("com.google.Chrome", "chrome"),
("com.brave.Browser", "brave"),
("com.brave.Browser.origin", "brave-origin"),
("com.microsoft.edgemac", "edge"),
("org.chromium.Chromium", "chromium"),
("com.brave.Browser.origin.beta", bc.UNSUPPORTED_CHANNEL),
("com.brave.Browser.origin.nightly", bc.UNSUPPORTED_CHANNEL),
],
)
def test_bundle_map(self, bundle, expected):
@@ -122,6 +125,9 @@ class TestDetectDefaultLinux:
("org.chromium.Chromium.desktop", "chromium"),
("brave-browser.desktop", "brave"),
("com.brave.Browser.desktop", "brave"),
("brave-origin.desktop", "brave-origin"),
("brave-origin-beta.desktop", bc.UNSUPPORTED_CHANNEL),
("brave-origin-nightly.desktop", bc.UNSUPPORTED_CHANNEL),
("microsoft-edge.desktop", "edge"),
("com.microsoft.Edge.desktop", "edge"),
("firefox.desktop", None),
+23
View File
@@ -42,6 +42,29 @@ class TestRealProfileResolvers:
assert m["chromehtml"] == "chrome"
assert m["msedgehtm"] == "edge"
assert m["bravehtml"] == "brave"
assert m["braveohtml"] == "brave-origin"
def test_brave_origin_data_dirs(self):
import hermes_cli.browser_connect as bc
with patch.dict(os.environ, {"LOCALAPPDATA": r"C:\Users\T\AppData\Local"}, clear=False):
win = bc.real_profile_data_dir("brave-origin", "Windows")
assert win and win.endswith(ntpath.join("BraveSoftware", "Brave-Origin", "User Data"))
with patch.dict(os.environ, {"XDG_CONFIG_HOME": "/home/t/.config"}, clear=False):
assert (
bc.real_profile_data_dir("brave-origin", "Linux")
== "/home/t/.config/BraveSoftware/Brave-Origin"
)
mac = bc.real_profile_data_dir("brave-origin", "Darwin")
assert mac and mac.endswith("Library/Application Support/BraveSoftware/Brave-Origin")
def test_brave_origin_channel_progids_fail_closed(self):
import hermes_cli.browser_connect as bc
# Beta=BraveOBHTML, Dev=BraveODHTML, Nightly=BraveOSHTM must be caught
# by the channel list, and must be checked BEFORE the stable map — note
# none of them share the braveohtml stable prefix, but ordering is the
# invariant the detector relies on for the other families.
for chan in ("braveobhtml", "braveodhtml", "braveoshtm"):
assert chan in bc._WINDOWS_CHANNEL_PROGIDS
def test_detect_default_non_chromium_is_none(self):
import hermes_cli.browser_connect as bc
+4 -2
View File
@@ -1596,7 +1596,8 @@ def _real_profile_cdp() -> tuple:
if browser is None:
return None, (
"browser.use_real_profile is on, but your default browser is not a "
"supported Chromium browser (Chrome, Edge, Brave, Chromium). "
"supported Chromium browser (Chrome, Edge, Brave, Brave Origin, "
"Chromium). "
"Real-profile browsing requires a Chromium default; set one or turn "
"the toggle off."
)
@@ -1610,7 +1611,8 @@ def _real_profile_cdp() -> tuple:
"browser.use_real_profile is on, but your default browser is a "
"pre-release Chromium channel (Beta / Dev / Canary), which "
"real-profile browsing does not support. Set your default to a "
"stable Chrome / Edge / Brave / Chromium, or turn the toggle off."
"stable Chrome / Edge / Brave / Brave Origin / Chromium, or turn "
"the toggle off."
)
# Reuse BEFORE writing anything. A shared copy-browser may already be up
+1 -1
View File
@@ -206,7 +206,7 @@ losing unsaved tabs), then retries. If the profile is still locked after that
to fully quit the browser — it won't loop or kill again on its own.
:::
- **Supported browsers:** Chrome, Edge, Brave, Chromium (whichever is your OS
- **Supported browsers:** Chrome, Edge, Brave, Brave Origin, Chromium (whichever is your OS
default). A non-Chromium default (e.g. Firefox) fails closed with a clear
message rather than guessing.
- **Works on any backend.** On a local backend it's automatic once the toggle