blowhorn chrome
blowhorn chrome: attach-chrome, extension-install, extension-mode, extension-status, map, profiles, status, suggest, unmap.
The blowhorn chrome commands. Each one's --help prints the same text.
| Command | What it does |
|---|---|
chrome attach-chrome |
Start or report a dedicated Chrome that is open to remote debugging with no Allow dialog, so '--chrome-launch cdp' can attach to a real Chrome unattended. |
chrome extension-install |
Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. |
chrome extension-mode |
Show or set how runs drive the Blowhorn Chrome extension. |
chrome extension-status |
Report whether the Blowhorn Chrome extension is installed and connected. |
chrome map |
Map one Blowhorn profile to one Chrome profile on this machine, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). |
chrome profiles |
List every profile Chrome knows. |
chrome status |
Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles i... |
chrome suggest |
Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Kate' proposes for the Blowhorn profile kate)... |
chrome unmap |
Remove one Blowhorn profile's Chrome profile mapping on this machine. |
blowhorn chrome attach-chrome
Start or report a dedicated Chrome that is open to remote debugging with no Allow dialog, so '--chrome-launch cdp' can attach to a real Chrome unattended. Chrome 136 and later ignore '--remote-debugging-port' on the default data directory, which is why this is a second Chrome on a tree of its own (~/.blowhorn/attach-chrome, on a port Chrome picks and records, bound to 127.0.0.1) and never your daily one. That port has no password and asks for no Allow, so any program running as you on this Mac can drive that Chrome: sign it in only to the accounts Blowhorn posts from.
'status' (the default) reads that tree's 'Local State', 'DevToolsActivePort' and 'SingletonLock' and nothing else -- no browser opens, nothing connects -- and its last line says what to do next. 'signin' opens the tree with no remote debugging at all, for the one-time sign-in to each account Blowhorn posts from, because some sites (Google among them) refuse to sign in inside a browser started with debugging on; quit that Chrome when you are done and the sign-ins carry over. 'stop' quits the Chrome holding the tree the way Cmd-Q does (DevTools Browser.close, so fresh sign-ins are saved), waits, then sends SIGTERM; it signals only that Chrome and never SIGKILLs. 'install-agent' adds a per-user LaunchAgent (ai.blowhorn.attach-chrome) that runs 'start' once at each login, so scheduler ticks find it up, and 'uninstall-agent' removes it (the Chrome keeps running). 'start' launches Chrome on the tree, detached, with the port open, and prints the steps that finish the setup: point chrome.data_dir at the tree, map your profiles, then run with '--chrome-launch cdp'. It writes no configuration of its own. The tree is fixed and Chrome picks the port, so there is nothing to choose: this command has no options of its own. A recorded port is evidence a Chrome is up on that tree, not proof a connect would succeed, and it counts only while a live Chrome holds the tree. Starting is refused when the tree is Google Chrome's own default data directory (never the one chrome.data_dir names, which step 1 above points at this tree), when a live Chrome already has a port recorded there, and while the tree is open without debugging (the signin window): quit that Chrome first. A port file a crashed Chrome left behind is cleared, not refused. --dry-run prints the command line and starts nothing.
blowhorn chrome attach-chrome [OPTIONS] [ACTION]| Argument | Required | Description |
|---|---|---|
ACTION |
no | 'status' (the default) reports the tree without starting anything; 'signin' opens it with no remote debugging for the one-time sign-in; 'start' launches Chrome on it with the port open; 'stop' quits that Chrome cleanly; 'install-agent' / 'uninstall-agent' manage the LaunchAgent that starts it at login |
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome attach-chrome' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome attach-chrome
blowhorn chrome attach-chrome start
blowhorn chrome attach-chrome signin
blowhorn chrome attach-chrome start --dry-run
blowhorn chrome attach-chrome stop
blowhorn chrome attach-chrome install-agent
blowhorn chrome attach-chrome --jsonOutput: json with --json, text otherwise.
blowhorn chrome extension-install
Write the native-messaging host manifest Chrome launches for the Blowhorn Chrome extension, and print where the unpacked extension lives so you can load it. The manifest is com.blowhorn.chrome_extension.json under Chrome's NativeMessagingHosts directory (~/Library/Application Support/Google/Chrome/NativeMessagingHosts on macOS, /.config/google-chrome/NativeMessagingHosts on Linux), named com.blowhorn.chrome_extension, type stdio, path an executable launcher chrome_extension-host in Blowhorn's directory (/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux) that runs this Python on the native-host program, and allowed_origins naming the extension id from the key in extension/manifest.json, the Chrome Web Store id and the older unpacked id. If you already installed the host, run this command again and reload the extension once. A missing key or an unreadable extension manifest is one sentence and nothing is written; --dry-run prints both paths and the manifest and writes nothing. This command does not launch Chrome and does not load the extension.
--profile <name> names the Blowhorn profile whose mapped Chrome profile should get the extension (resolved through 'blowhorn chrome map' on this machine); --chrome-profile '<directory>' names the Chrome profile directory itself, as 'blowhorn chrome profiles' prints it, and can be given more than once. Either one writes the native host as above, checks the directory is in Chrome's Local State, and prints the steps to load the extension in that profile alone: Chrome has no file this command can write that installs an extension in one profile only, since the policy and the external-extensions file both reach every profile. A profile with no mapping, a directory Chrome does not have, and either flag with --all-profiles are one sentence and nothing is written; a store that cannot be reached is the named cause and exit 3. A --profile inherited from BLOWHORN_PROFILE or defaults.profile does not count.
--all-profiles also asks Chrome to install that extension in every Chrome profile on this machine. It is every Chrome profile, not --profile all. The default route writes ExtensionInstallForcelist: silent, every profile, Chrome shows Managed by your organization, and macOS may ask for an administrator password once. On Linux the policy file (/etc/opt/chrome/policies/managed/blowhorn.json) and the external file (/usr/share/google-chrome/extensions/<id>.json) need root, Blowhorn never asks for it, and a write this user cannot make is refused with the file, its exact content and the sudo command that writes that one file. Never run this command under sudo: the host and launcher would land under root's home, which your Chrome never reads, so a run under sudo is refused by name. --external writes Chrome's external-extensions file instead, so Chrome offers the install in each profile, one click there, and does not show that banner. --external without --all-profiles is refused and writes nothing. Switching route removes the other route's file for this extension id. Both routes name the extension id from that same key and the Chrome Web Store update url; Chrome can fetch the extension only once that id is published. A policy file that exists and cannot be read is one sentence and nothing is written. Per-profile lines say what Chrome was asked to do, never that a profile's copy was confirmed. Those lines cover the configured Chrome data directory. The policy route applies the extension to every Chrome profile on this machine. --json appends one trailing object (manifest, launcher, extension_dir, removed), for a script or the desktop app. With --all-profiles that object also carries route, extension_id, profiles, profiles_error and profiles_scope; with --profile or --chrome-profile it carries targets.
blowhorn chrome extension-install [OPTIONS]| Flag | Type | Default | Description |
|---|---|---|---|
--all-profiles |
boolean | false |
Also ask Chrome to install the Chrome extension in every Chrome profile on this machine. The default is the policy route (silent, every profile, Managed by your organization). This is every Chrome profile, not --profile all. |
--external |
boolean | false |
With --all-profiles, write Chrome's external-extensions file instead of the policy, so Chrome offers the install in each profile and does not show Managed by your organization. Refused without --all-profiles; nothing is written. |
--chrome-profile |
string, repeatable | A Chrome profile directory, exactly as 'blowhorn chrome profiles' prints it ('Profile 12'): write the host and print the steps to load the extension in that profile alone. Repeatable. Refused with --all-profiles | |
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome extension-install' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome extension-install
blowhorn chrome extension-install --dry-run
blowhorn chrome extension-install --json
blowhorn chrome extension-install --all-profiles
blowhorn chrome extension-install --all-profiles --dry-run
blowhorn chrome extension-install --all-profiles --external
blowhorn chrome extension-install --all-profiles --json
blowhorn chrome extension-install --profile kate
blowhorn chrome extension-install --chrome-profile 'Profile 12'Output: json with --json, text otherwise.
blowhorn chrome extension-mode
Show or set how runs drive the Blowhorn Chrome extension. background is the default. classic is the behavior the store build has shipped: Blowhorn's tab opens at the end of the window and comes forward for a screenshot or a login. background keeps Blowhorn's tab inactive in a group beside yours, keeps focus emulation on from the first page, takes the visible screenshot without switching tabs, types with trusted input and brings the tab forward only when you are needed. With no MODE it reports the mode a run started now would use and where it comes from: the BLOWHORN_EXTENSION_ROLLBACK override, your Layer5 Cloud account setting, or the background default. A job whose driver mode is extension-classic or extension-background outranks the account setting for that job. Setting MODE writes extensionMode to your account's Blowhorn preferences in Layer5 Cloud (sign in first with 'blowhorn auth login'); default clears it. --json emits the report as one object, for a script or the desktop app.
blowhorn chrome extension-mode [OPTIONS] [MODE]| Argument | Required | Description |
|---|---|---|
MODE |
no | classic, background, or default to clear the account setting; omit to show |
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome extension-mode' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome extension-mode
blowhorn chrome extension-mode classic
blowhorn chrome extension-mode default --jsonOutput: json with --json, text otherwise.
blowhorn chrome extension-status
Report whether the Blowhorn Chrome extension is installed and connected: the unpacked extension directory, the native-host manifest (missing, current, or stale against this checkout's extension/manifest.json), the launcher, and whether the Chrome extension answers hello on its socket. An answered hello proves the extension is loaded in the real Chrome and talking; this command navigates nowhere, probes no tab, and contacts no platform. A manifest whose launcher is gone is not installed. The report also says which install route is on disk: policy (ExtensionInstallForcelist, every Chrome profile, Managed by your organization), external (Chrome offers the install in each profile), or none. That read writes nothing. Which Chrome profile accepted that install is not something this command can see. It does name each Chrome profile whose Secure Preferences or Preferences record a Chrome Web Store install of the extension, with its version, and tells a store install too old for this Blowhorn to update in chrome://extensions, never to load it unpacked. The one thing written is the bridge lock beside the socket (chrome_extension.lock in Blowhorn's directory: ~/Library/Application Support/Blowhorn on macOS, ~/.local/share/blowhorn on Linux), which every Blowhorn client holds while it talks to the Chrome extension: while a run holds it, no hello is written, the Chrome extension is reported not checked, and --json carries bridge_busy: true with a detail saying a run may be holding the bridge. --json emits the report as one object, for a script or the desktop app.
blowhorn chrome extension-status [OPTIONS]| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome extension-status' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome extension-status
blowhorn chrome extension-status --jsonOutput: json with --json, text otherwise.
blowhorn chrome map
Map one Blowhorn profile to one Chrome profile on this machine, by the Chrome profile's directory name as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12'). The command checks that the Blowhorn profile is in the store, that Chrome has that directory (Chrome silently creates a new, empty profile for a directory it does not know, so a typo is refused rather than recorded), and that the directory is not already mapped to another Blowhorn profile on this machine; then it records the display name and signed-in account Chrome shows for the directory and prints what it recorded. Mapping a profile that is already mapped replaces its mapping, which is also how a drifted name or account is confirmed. One Blowhorn profile per Chrome profile, one Chrome profile per Blowhorn profile, per machine.
A mapping is identity, never permission: it does not make a profile eligible for anything; every browser run reads it to find the profile's Chrome and refuses a profile without one.
Mapping also pairs the profile: once the mapping is recorded the command asks Chrome to open a window on the Blowhorn extension's pairing page in that Chrome profile, carrying a token made from this machine's pairing key (chrome_pairing.key in Blowhorn's launcher directory, created on first use and readable only by you). The page keeps the token in the extension's storage, and a run through the extension refuses before any action unless the extension it reaches reports this profile's token. Mapping a profile to the directory it already has pairs it again. When Chrome cannot be asked to open the page the mapping still stands and the command says why and what to run. --json adds 'pairing': opened (whether Chrome was asked to open the page) and detail (why not, or null). With --dry-run the command checks everything, records nothing and opens no pairing page. Each refusal is one sentence and exit 1; a store that cannot be reached is the named cause and exit 3.
blowhorn chrome map [OPTIONS] name directory| Argument | Required | Description |
|---|---|---|
name |
yes | The Blowhorn profile, as the store's profiles name it (kate) |
directory |
yes | The Chrome profile directory, exactly as 'blowhorn chrome profiles' prints it ('Default', 'Profile 12') |
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome map' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome map kate 'Profile 12'
blowhorn chrome map kate 'Profile 12' --dry-run
blowhorn chrome map lee Default --jsonOutput: json with --json, text otherwise.
blowhorn chrome profiles
List every profile Chrome knows: its directory name (the identity a mapping uses), its display name, the signed-in Google account (blank when nobody is signed in), a marker on the profile Chrome used last, and the Blowhorn profile it is mapped to on this machine with whether that mapping's recorded name and account still match ('ok' or 'drift'). The profile list is read from 'Local State' in the Chrome data directory, beside 'DevToolsActivePort', and from no other file; the mapping is read from the store once; no browser opens.
--json emits the same list as JSON with a stable shape: one object per profile with directory, display_name, account, gaia_name, last_used, mapped_to (a Blowhorn profile name or null) and identity ('ok', 'drift' or null when unmapped), plus host, for a script or the desktop app to read. When 'Local State' is missing or cannot be read the command says so in one line and exits 1; a store that cannot be reached is the named cause and exit 3.
blowhorn chrome profiles [OPTIONS]| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome profiles' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome profiles
blowhorn chrome profiles --jsonOutput: json with --json, text otherwise.
blowhorn chrome status
Report whether Google Chrome is installed, whether it is running, whether its remote-debugging switch is on (chrome://inspect/#remote-debugging), the debugging port it recorded, how many profiles it has, and the launch mode a run would use right now: the configured setting (--chrome-launch, BLOWHORN_CHROME_LAUNCH, defaults.chrome.launch_mode, then the extension default), whether 'auto' would attach to this Chrome or launch Blowhorn's own, and why. When something is off, the last line says what to do next: where to turn the switch on, or that Chrome needs opening. Read-only: two files under the Chrome data directory and one process listing; no browser opens and nothing connects to Chrome.
--json emits the same report as JSON with a stable shape (installed, running, remote_debugging.enabled, remote_debugging.port, profile_count, last_used, ready, next_step, launch_mode.setting, launch_mode.mode, launch_mode.reason). The command exits 0 whenever it could report, whatever the report says.
blowhorn chrome status [OPTIONS]| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome status' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome status
blowhorn chrome status --json
blowhorn chrome status --chrome-launch cdpOutput: json with --json, text otherwise.
blowhorn chrome suggest
Print a proposed mapping from every Blowhorn profile in the store to a Chrome profile on this machine, matched by display name (a Chrome profile named 'Kate' proposes for the Blowhorn profile kate), and apply nothing. Each row says why: 'name match', 'none' when no Chrome profile matches, 'ambiguous' when two match, 'already mapped' for a profile that has a mapping, and the holder's name when the matching directory is mapped to someone else. Apply a row you agree with by running the 'blowhorn chrome map' line it prints.
--json emits the same rows as JSON. A store that cannot be reached is the named cause and exit 3.
blowhorn chrome suggest [OPTIONS]| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome suggest' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome suggest
blowhorn chrome suggest --jsonOutput: json with --json, text otherwise.
blowhorn chrome unmap
Remove one Blowhorn profile's Chrome profile mapping on this machine. Chrome is not read and nothing under its directory is touched: only the store's row goes, so a mapping can be removed when 'Local State' is missing or unreadable. A profile with no mapping on this machine is a one-sentence refusal and exit 1; with --dry-run the command says what would be removed and removes nothing.
blowhorn chrome unmap [OPTIONS] name| Argument | Required | Description |
|---|---|---|
name |
yes | The Blowhorn profile, as the store's profiles name it (kate) |
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Emit the report as JSON with a stable shape, for a script or the desktop app |
--platform |
string | Accepted and ignored by 'blowhorn chrome': this command never acts on a platform, so --platform has no effect here -- including a value inherited from BLOWHORN_PLATFORM or defaults.platform in .blowhorn.yaml | |
--pace |
string | Accepted by 'blowhorn chrome unmap' because --pace is a global flag, but this command does no paced work -- it opens no browser and sleeps between nothing -- so the value changes nothing about how it runs and appears only in the Pace column of the -v summary. Resolved as CLI flag, then BLOWHORN_PACE, then defaults.pace in .blowhorn.yaml (normal when none is set); an unrecognized value is rejected before anything runs |
Also takes the shared flags: --config, --log-dir, --profile, --exclude, --headless, --chrome-launch, --headed, --dry-run, --verbose.
Examples:
blowhorn chrome unmap kate
blowhorn chrome unmap kate --dry-run --jsonOutput: json with --json, text otherwise.