feat(connect): install + recognize BOTH native-messaging host names (staged extension migration)
Stage 1 of the agent-browser → chrome-use extension migration: the CLI now writes a host manifest under both com.agent_browser.connect (extension ≤0.4.2) AND com.leeguoo.chrome_use (the rebrand 0.5.0+), both pointing at the same launcher, and host_installed()/uninstall recognize both. So the relay works no matter which extension version a user has, with no forced re-install — which lets the store roll 0.4.3 (cosmetic name only, host unchanged) and later 0.5.0 (new host) without ever breaking the relay or re-popping the consent dialog.
This commit is contained in:
+35
-17
@@ -16,9 +16,19 @@ use std::io::Write;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Native-messaging host name; must match `HOST_NAME` in the extension and the
|
/// Native-messaging host name; must match `HOST_NAME` in the extension and the
|
||||||
/// manifest filename.
|
/// manifest filename. `com.agent_browser.connect` is the original name, used by
|
||||||
|
/// every shipped extension up to ab-connect 0.4.2.
|
||||||
pub const HOST_NAME: &str = "com.agent_browser.connect";
|
pub const HOST_NAME: &str = "com.agent_browser.connect";
|
||||||
|
|
||||||
|
/// Alternate host name for the chrome-use rebrand era (ab-connect 0.5.0+). We
|
||||||
|
/// install AND recognize both names so the relay works regardless of which
|
||||||
|
/// extension version a user has — old (0.4.2) or new — with no forced
|
||||||
|
/// re-install. See [`install_native_host`] / [`host_installed`].
|
||||||
|
pub const HOST_NAME_ALT: &str = "com.leeguoo.chrome_use";
|
||||||
|
|
||||||
|
/// Every native-messaging host name this CLI installs and accepts.
|
||||||
|
pub const HOST_NAMES: &[&str] = &[HOST_NAME, HOST_NAME_ALT];
|
||||||
|
|
||||||
/// Stable id of the `ab-connect` extension, pinned by the `key` in its
|
/// Stable id of the `ab-connect` extension, pinned by the `key` in its
|
||||||
/// manifest.json (and the signing key of the published `.crx`). Chrome only lets
|
/// manifest.json (and the signing key of the published `.crx`). Chrome only lets
|
||||||
/// that extension talk to this host, and the force-install policy references it.
|
/// that extension talk to this host, and the force-install policy references it.
|
||||||
@@ -182,8 +192,20 @@ fn install_native_host() -> Result<Vec<String>, String> {
|
|||||||
let _ = std::fs::set_permissions(&launcher, std::fs::Permissions::from_mode(0o755));
|
let _ = std::fs::set_permissions(&launcher, std::fs::Permissions::from_mode(0o755));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write a manifest under EVERY accepted host name (both point to the same
|
||||||
|
// launcher + allowed extensions), so any extension version's
|
||||||
|
// `connectNative(<its host name>)` finds a matching host json.
|
||||||
|
let mut written = Vec::new();
|
||||||
|
for dir in native_messaging_dirs() {
|
||||||
|
if let Some(parent) = dir.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
continue; // that browser isn't installed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
||||||
|
for host in HOST_NAMES {
|
||||||
let manifest = serde_json::json!({
|
let manifest = serde_json::json!({
|
||||||
"name": HOST_NAME,
|
"name": host,
|
||||||
"description": "chrome-use connect — native messaging host",
|
"description": "chrome-use connect — native messaging host",
|
||||||
"path": launcher.display().to_string(),
|
"path": launcher.display().to_string(),
|
||||||
"type": "stdio",
|
"type": "stdio",
|
||||||
@@ -193,19 +215,11 @@ fn install_native_host() -> Result<Vec<String>, String> {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
let body = serde_json::to_string_pretty(&manifest).map_err(|e| e.to_string())?;
|
let body = serde_json::to_string_pretty(&manifest).map_err(|e| e.to_string())?;
|
||||||
|
let path = dir.join(format!("{host}.json"));
|
||||||
let mut written = Vec::new();
|
|
||||||
for dir in native_messaging_dirs() {
|
|
||||||
if let Some(parent) = dir.parent() {
|
|
||||||
if !parent.exists() {
|
|
||||||
continue; // that browser isn't installed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
|
||||||
let path = dir.join(format!("{HOST_NAME}.json"));
|
|
||||||
std::fs::write(&path, &body).map_err(|e| e.to_string())?;
|
std::fs::write(&path, &body).map_err(|e| e.to_string())?;
|
||||||
written.push(path.display().to_string());
|
written.push(path.display().to_string());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if written.is_empty() {
|
if written.is_empty() {
|
||||||
return Err("no Chrome/Chromium NativeMessagingHosts directory found".into());
|
return Err("no Chrome/Chromium NativeMessagingHosts directory found".into());
|
||||||
}
|
}
|
||||||
@@ -289,11 +303,13 @@ fn remove_force_install_profile() -> bool {
|
|||||||
fn remove_host_manifests() -> usize {
|
fn remove_host_manifests() -> usize {
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
for dir in native_messaging_dirs() {
|
for dir in native_messaging_dirs() {
|
||||||
let path = dir.join(format!("{HOST_NAME}.json"));
|
for host in HOST_NAMES {
|
||||||
|
let path = dir.join(format!("{host}.json"));
|
||||||
if path.exists() && std::fs::remove_file(&path).is_ok() {
|
if path.exists() && std::fs::remove_file(&path).is_ok() {
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +350,7 @@ fn native_messaging_dirs() -> Vec<PathBuf> {
|
|||||||
fn host_manifest_path_for_chrome() -> Option<PathBuf> {
|
fn host_manifest_path_for_chrome() -> Option<PathBuf> {
|
||||||
native_messaging_dirs()
|
native_messaging_dirs()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| d.join(format!("{HOST_NAME}.json")))
|
.flat_map(|d| HOST_NAMES.iter().map(move |h| d.join(format!("{h}.json"))))
|
||||||
.find(|p| p.exists())
|
.find(|p| p.exists())
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
native_messaging_dirs()
|
native_messaging_dirs()
|
||||||
@@ -352,9 +368,11 @@ fn host_manifest_path_for_chrome() -> Option<PathBuf> {
|
|||||||
/// service worker; this manifest is the durable signal that the extension is
|
/// service worker; this manifest is the durable signal that the extension is
|
||||||
/// the chosen path.
|
/// the chosen path.
|
||||||
pub fn host_installed() -> bool {
|
pub fn host_installed() -> bool {
|
||||||
native_messaging_dirs()
|
native_messaging_dirs().into_iter().any(|d| {
|
||||||
.into_iter()
|
HOST_NAMES
|
||||||
.any(|d| d.join(format!("{HOST_NAME}.json")).exists())
|
.iter()
|
||||||
|
.any(|h| d.join(format!("{h}.json")).exists())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report(json: bool, ok: bool, msg: &str) {
|
fn report(json: bool, ok: bool, msg: &str) {
|
||||||
|
|||||||
Reference in New Issue
Block a user