mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-25 17:44:24 +08:00
feat(hermes): add browser camofox controls
This commit is contained in:
@@ -4770,6 +4770,17 @@ function normalizeHermesOptionalString(value, key) {
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
function normalizeHermesCamofoxIdentity(value, key) {
|
||||
if (value == null || value === '') return ''
|
||||
if (typeof value !== 'string') throw new Error(`${key} 必须是字符串`)
|
||||
const text = value.trim()
|
||||
if (!text) return ''
|
||||
if (!/^[A-Za-z0-9_.:@+-]+$/.test(text)) {
|
||||
throw new Error(`${key} 只能包含字母、数字、下划线、点、冒号、@、加号和短横线`)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
export function buildHermesModelConfigValues(config = {}) {
|
||||
const root = config && typeof config === 'object' && !Array.isArray(config) ? config : {}
|
||||
const model = root.model && typeof root.model === 'object' && !Array.isArray(root.model) ? root.model : {}
|
||||
@@ -5639,6 +5650,9 @@ export function buildHermesBrowserConfigValues(config = {}) {
|
||||
const browser = root.browser && typeof root.browser === 'object' && !Array.isArray(root.browser)
|
||||
? root.browser
|
||||
: {}
|
||||
const camofox = browser.camofox && typeof browser.camofox === 'object' && !Array.isArray(browser.camofox)
|
||||
? browser.camofox
|
||||
: {}
|
||||
return {
|
||||
browserInactivityTimeout: parseHermesInteger(browser.inactivity_timeout, 'browser.inactivity_timeout', 120, 1, 86400, false),
|
||||
browserCommandTimeout: parseHermesInteger(browser.command_timeout, 'browser.command_timeout', 30, 5, 3600, false),
|
||||
@@ -5647,6 +5661,10 @@ export function buildHermesBrowserConfigValues(config = {}) {
|
||||
browserAllowPrivateUrls: readHermesBool(browser.allow_private_urls, false),
|
||||
browserAutoLocalForPrivateUrls: readHermesBool(browser.auto_local_for_private_urls, true),
|
||||
browserCdpUrl: normalizeHermesOptionalString(browser.cdp_url, 'browser.cdp_url'),
|
||||
browserCamofoxManagedPersistence: readHermesBool(camofox.managed_persistence, false),
|
||||
browserCamofoxUserId: normalizeHermesCamofoxIdentity(camofox.user_id, 'browser.camofox.user_id'),
|
||||
browserCamofoxSessionKey: normalizeHermesCamofoxIdentity(camofox.session_key, 'browser.camofox.session_key'),
|
||||
browserCamofoxAdoptExistingTab: readHermesBool(camofox.adopt_existing_tab, false),
|
||||
browserDialogPolicy: normalizeHermesBrowserDialogPolicy(browser.dialog_policy, false),
|
||||
browserDialogTimeout: parseHermesInteger(browser.dialog_timeout_s, 'browser.dialog_timeout_s', 300, 1, 86400, false),
|
||||
}
|
||||
@@ -5667,6 +5685,18 @@ export function mergeHermesBrowserConfig(config = {}, form = {}) {
|
||||
const cdpUrl = normalizeHermesOptionalString(Object.hasOwn(form, 'browserCdpUrl') ? form.browserCdpUrl : currentValues.browserCdpUrl, 'browser.cdp_url')
|
||||
if (cdpUrl) browser.cdp_url = cdpUrl
|
||||
else delete browser.cdp_url
|
||||
const camofox = browser.camofox && typeof browser.camofox === 'object' && !Array.isArray(browser.camofox)
|
||||
? mergeConfigsPreservingFields(browser.camofox, {})
|
||||
: {}
|
||||
camofox.managed_persistence = formHermesBool(form, 'browserCamofoxManagedPersistence', currentValues.browserCamofoxManagedPersistence)
|
||||
const camofoxUserId = normalizeHermesCamofoxIdentity(Object.hasOwn(form, 'browserCamofoxUserId') ? form.browserCamofoxUserId : currentValues.browserCamofoxUserId, 'browser.camofox.user_id')
|
||||
if (camofoxUserId) camofox.user_id = camofoxUserId
|
||||
else delete camofox.user_id
|
||||
const camofoxSessionKey = normalizeHermesCamofoxIdentity(Object.hasOwn(form, 'browserCamofoxSessionKey') ? form.browserCamofoxSessionKey : currentValues.browserCamofoxSessionKey, 'browser.camofox.session_key')
|
||||
if (camofoxSessionKey) camofox.session_key = camofoxSessionKey
|
||||
else delete camofox.session_key
|
||||
camofox.adopt_existing_tab = formHermesBool(form, 'browserCamofoxAdoptExistingTab', currentValues.browserCamofoxAdoptExistingTab)
|
||||
browser.camofox = camofox
|
||||
browser.dialog_policy = normalizeHermesBrowserDialogPolicy(Object.hasOwn(form, 'browserDialogPolicy') ? form.browserDialogPolicy : currentValues.browserDialogPolicy, true)
|
||||
browser.dialog_timeout_s = parseHermesInteger(Object.hasOwn(form, 'browserDialogTimeout') ? form.browserDialogTimeout : currentValues.browserDialogTimeout, 'browser.dialog_timeout_s', 300, 1, 86400, true)
|
||||
next.browser = browser
|
||||
|
||||
@@ -2603,6 +2603,23 @@ fn set_optional_yaml_string(map: &mut serde_yaml::Mapping, key: &str, value: Str
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_hermes_camofox_identity(value: Option<String>, key: &str) -> Result<String, String> {
|
||||
let text = value.unwrap_or_default().trim().to_string();
|
||||
if text.is_empty() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
if text
|
||||
.chars()
|
||||
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '.' | ':' | '@' | '+' | '-'))
|
||||
{
|
||||
Ok(text)
|
||||
} else {
|
||||
Err(format!(
|
||||
"{key} 只能包含字母、数字、下划线、点、冒号、@、加号和短横线"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn yaml_string_sequence_field(map: &serde_yaml::Mapping, key: &str) -> Vec<String> {
|
||||
yaml_get(map, key)
|
||||
.and_then(|value| value.as_sequence())
|
||||
@@ -8583,6 +8600,23 @@ fn build_hermes_browser_config_values(config: &serde_yaml::Value) -> Value {
|
||||
let browser_cdp_url = browser
|
||||
.and_then(|map| yaml_string_field(map, "cdp_url"))
|
||||
.unwrap_or_default();
|
||||
let camofox = browser.and_then(|map| yaml_get_mapping(map, "camofox"));
|
||||
let browser_camofox_managed_persistence = camofox
|
||||
.and_then(|map| yaml_bool_field(map, "managed_persistence"))
|
||||
.unwrap_or(false);
|
||||
let browser_camofox_user_id = normalize_hermes_camofox_identity(
|
||||
camofox.and_then(|map| yaml_string_field(map, "user_id")),
|
||||
"browser.camofox.user_id",
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let browser_camofox_session_key = normalize_hermes_camofox_identity(
|
||||
camofox.and_then(|map| yaml_string_field(map, "session_key")),
|
||||
"browser.camofox.session_key",
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let browser_camofox_adopt_existing_tab = camofox
|
||||
.and_then(|map| yaml_bool_field(map, "adopt_existing_tab"))
|
||||
.unwrap_or(false);
|
||||
let browser_dialog_policy = normalize_hermes_browser_dialog_policy(
|
||||
browser.and_then(|map| yaml_string_field(map, "dialog_policy")),
|
||||
false,
|
||||
@@ -8600,6 +8634,10 @@ fn build_hermes_browser_config_values(config: &serde_yaml::Value) -> Value {
|
||||
"browserAllowPrivateUrls": browser_allow_private_urls,
|
||||
"browserAutoLocalForPrivateUrls": browser_auto_local_for_private_urls,
|
||||
"browserCdpUrl": browser_cdp_url,
|
||||
"browserCamofoxManagedPersistence": browser_camofox_managed_persistence,
|
||||
"browserCamofoxUserId": browser_camofox_user_id,
|
||||
"browserCamofoxSessionKey": browser_camofox_session_key,
|
||||
"browserCamofoxAdoptExistingTab": browser_camofox_adopt_existing_tab,
|
||||
"browserDialogPolicy": browser_dialog_policy,
|
||||
"browserDialogTimeout": browser_dialog_timeout,
|
||||
})
|
||||
@@ -8663,6 +8701,44 @@ fn merge_hermes_browser_config(config: &mut serde_yaml::Value, form: &Value) ->
|
||||
.trim()
|
||||
.to_string()
|
||||
};
|
||||
let browser_camofox_managed_persistence = form_bool(form, "browserCamofoxManagedPersistence")
|
||||
.unwrap_or_else(|| {
|
||||
current["browserCamofoxManagedPersistence"]
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
});
|
||||
let browser_camofox_user_id = normalize_hermes_camofox_identity(
|
||||
if form.get("browserCamofoxUserId").is_some() {
|
||||
Some(
|
||||
form_string(form, "browserCamofoxUserId")
|
||||
.ok_or_else(|| "browser.camofox.user_id 必须是字符串".to_string())?,
|
||||
)
|
||||
} else {
|
||||
current["browserCamofoxUserId"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
},
|
||||
"browser.camofox.user_id",
|
||||
)?;
|
||||
let browser_camofox_session_key = normalize_hermes_camofox_identity(
|
||||
if form.get("browserCamofoxSessionKey").is_some() {
|
||||
Some(
|
||||
form_string(form, "browserCamofoxSessionKey")
|
||||
.ok_or_else(|| "browser.camofox.session_key 必须是字符串".to_string())?,
|
||||
)
|
||||
} else {
|
||||
current["browserCamofoxSessionKey"]
|
||||
.as_str()
|
||||
.map(ToString::to_string)
|
||||
},
|
||||
"browser.camofox.session_key",
|
||||
)?;
|
||||
let browser_camofox_adopt_existing_tab = form_bool(form, "browserCamofoxAdoptExistingTab")
|
||||
.unwrap_or_else(|| {
|
||||
current["browserCamofoxAdoptExistingTab"]
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
});
|
||||
let browser_dialog_policy = normalize_hermes_browser_dialog_policy(
|
||||
if form.get("browserDialogPolicy").is_some() {
|
||||
form_string(form, "browserDialogPolicy")
|
||||
@@ -8712,6 +8788,17 @@ fn merge_hermes_browser_config(config: &mut serde_yaml::Value, form: &Value) ->
|
||||
serde_yaml::Value::Bool(browser_auto_local_for_private_urls),
|
||||
);
|
||||
set_optional_yaml_string(browser, "cdp_url", browser_cdp_url);
|
||||
let camofox = yaml_child_object(browser, "camofox")?;
|
||||
camofox.insert(
|
||||
yaml_key("managed_persistence"),
|
||||
serde_yaml::Value::Bool(browser_camofox_managed_persistence),
|
||||
);
|
||||
set_optional_yaml_string(camofox, "user_id", browser_camofox_user_id);
|
||||
set_optional_yaml_string(camofox, "session_key", browser_camofox_session_key);
|
||||
camofox.insert(
|
||||
yaml_key("adopt_existing_tab"),
|
||||
serde_yaml::Value::Bool(browser_camofox_adopt_existing_tab),
|
||||
);
|
||||
browser.insert(
|
||||
yaml_key("dialog_policy"),
|
||||
serde_yaml::Value::String(browser_dialog_policy),
|
||||
@@ -18257,6 +18344,10 @@ mod hermes_browser_config_tests {
|
||||
assert_eq!(values["browserAllowPrivateUrls"], false);
|
||||
assert_eq!(values["browserAutoLocalForPrivateUrls"], true);
|
||||
assert_eq!(values["browserCdpUrl"], "");
|
||||
assert_eq!(values["browserCamofoxManagedPersistence"], false);
|
||||
assert_eq!(values["browserCamofoxUserId"], "");
|
||||
assert_eq!(values["browserCamofoxSessionKey"], "");
|
||||
assert_eq!(values["browserCamofoxAdoptExistingTab"], false);
|
||||
assert_eq!(values["browserDialogPolicy"], "must_respond");
|
||||
assert_eq!(values["browserDialogTimeout"], 300);
|
||||
}
|
||||
@@ -18273,6 +18364,11 @@ browser:
|
||||
allow_private_urls: true
|
||||
auto_local_for_private_urls: false
|
||||
cdp_url: ws://127.0.0.1:9222/devtools/browser/demo
|
||||
camofox:
|
||||
managed_persistence: true
|
||||
user_id: shared-camofox-user
|
||||
session_key: shared-session-key
|
||||
adopt_existing_tab: true
|
||||
dialog_policy: auto_accept
|
||||
dialog_timeout_s: 120
|
||||
"#,
|
||||
@@ -18289,6 +18385,10 @@ browser:
|
||||
values["browserCdpUrl"],
|
||||
"ws://127.0.0.1:9222/devtools/browser/demo"
|
||||
);
|
||||
assert_eq!(values["browserCamofoxManagedPersistence"], true);
|
||||
assert_eq!(values["browserCamofoxUserId"], "shared-camofox-user");
|
||||
assert_eq!(values["browserCamofoxSessionKey"], "shared-session-key");
|
||||
assert_eq!(values["browserCamofoxAdoptExistingTab"], true);
|
||||
assert_eq!(values["browserDialogPolicy"], "auto_accept");
|
||||
assert_eq!(values["browserDialogTimeout"], 120);
|
||||
}
|
||||
@@ -18306,7 +18406,11 @@ browser:
|
||||
engine: auto
|
||||
cdp_url: ws://127.0.0.1:9222/devtools/browser/demo
|
||||
camofox:
|
||||
managed_persistence: true
|
||||
managed_persistence: false
|
||||
user_id: old-user
|
||||
session_key: old-session
|
||||
adopt_existing_tab: false
|
||||
custom_flag: keep-camofox
|
||||
custom_flag: keep-browser
|
||||
streaming:
|
||||
enabled: true
|
||||
@@ -18324,6 +18428,10 @@ streaming:
|
||||
"browserAllowPrivateUrls": true,
|
||||
"browserAutoLocalForPrivateUrls": false,
|
||||
"browserCdpUrl": "http://127.0.0.1:9222",
|
||||
"browserCamofoxManagedPersistence": true,
|
||||
"browserCamofoxUserId": "shared-camofox-user",
|
||||
"browserCamofoxSessionKey": "shared-session-key",
|
||||
"browserCamofoxAdoptExistingTab": true,
|
||||
"browserDialogPolicy": "auto_dismiss",
|
||||
"browserDialogTimeout": "45",
|
||||
}),
|
||||
@@ -18357,6 +18465,69 @@ streaming:
|
||||
config["browser"]["camofox"]["managed_persistence"].as_bool(),
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["user_id"].as_str(),
|
||||
Some("shared-camofox-user")
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["session_key"].as_str(),
|
||||
Some("shared-session-key")
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["adopt_existing_tab"].as_bool(),
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["custom_flag"].as_str(),
|
||||
Some("keep-camofox")
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["custom_flag"].as_str(),
|
||||
Some("keep-browser")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_browser_config_removes_empty_camofox_identity_fields() {
|
||||
let mut config: serde_yaml::Value = serde_yaml::from_str(
|
||||
r#"
|
||||
browser:
|
||||
camofox:
|
||||
managed_persistence: true
|
||||
user_id: old-user
|
||||
session_key: old-session
|
||||
adopt_existing_tab: true
|
||||
custom_flag: keep-camofox
|
||||
custom_flag: keep-browser
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
merge_hermes_browser_config(
|
||||
&mut config,
|
||||
&json!({
|
||||
"browserCamofoxManagedPersistence": false,
|
||||
"browserCamofoxUserId": " ",
|
||||
"browserCamofoxSessionKey": "",
|
||||
"browserCamofoxAdoptExistingTab": false,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["managed_persistence"].as_bool(),
|
||||
Some(false)
|
||||
);
|
||||
assert!(config["browser"]["camofox"]["user_id"].is_null());
|
||||
assert!(config["browser"]["camofox"]["session_key"].is_null());
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["adopt_existing_tab"].as_bool(),
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["camofox"]["custom_flag"].as_str(),
|
||||
Some("keep-camofox")
|
||||
);
|
||||
assert_eq!(
|
||||
config["browser"]["custom_flag"].as_str(),
|
||||
Some("keep-browser")
|
||||
@@ -18406,6 +18577,21 @@ browser:
|
||||
let err =
|
||||
merge_hermes_browser_config(&mut config, &json!({ "browserCdpUrl": 123 })).unwrap_err();
|
||||
assert!(err.contains("browser.cdp_url"));
|
||||
let err = merge_hermes_browser_config(&mut config, &json!({ "browserCamofoxUserId": 123 }))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("browser.camofox.user_id"));
|
||||
let err = merge_hermes_browser_config(
|
||||
&mut config,
|
||||
&json!({ "browserCamofoxUserId": "bad user" }),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("browser.camofox.user_id"));
|
||||
let err = merge_hermes_browser_config(
|
||||
&mut config,
|
||||
&json!({ "browserCamofoxSessionKey": "bad session" }),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("browser.camofox.session_key"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,6 +309,10 @@ const BROWSER_DEFAULTS = {
|
||||
browserAllowPrivateUrls: false,
|
||||
browserAutoLocalForPrivateUrls: true,
|
||||
browserCdpUrl: '',
|
||||
browserCamofoxManagedPersistence: false,
|
||||
browserCamofoxUserId: '',
|
||||
browserCamofoxSessionKey: '',
|
||||
browserCamofoxAdoptExistingTab: false,
|
||||
browserDialogPolicy: 'must_respond',
|
||||
browserDialogTimeout: 300,
|
||||
}
|
||||
@@ -2411,6 +2415,27 @@ export function render() {
|
||||
<span>${t('engine.hermesBrowserConfigAutoLocalForPrivateUrls')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-config-subtitle">${t('engine.hermesBrowserConfigCamofoxTitle')}</div>
|
||||
<div class="hm-config-runtime-grid hm-config-browser-camofox-grid">
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesBrowserConfigCamofoxUserId')}</span>
|
||||
<input id="hm-browser-camofox-user-id" class="hm-input" type="text" autocomplete="off" spellcheck="false" value="${esc(browserValues.browserCamofoxUserId)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesBrowserConfigCamofoxSessionKey')}</span>
|
||||
<input id="hm-browser-camofox-session-key" class="hm-input" type="text" autocomplete="off" spellcheck="false" value="${esc(browserValues.browserCamofoxSessionKey)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-config-check-grid">
|
||||
<label class="hm-channel-check">
|
||||
<input id="hm-browser-camofox-managed-persistence" type="checkbox" ${browserValues.browserCamofoxManagedPersistence ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesBrowserConfigCamofoxManagedPersistence')}</span>
|
||||
</label>
|
||||
<label class="hm-channel-check">
|
||||
<input id="hm-browser-camofox-adopt-existing-tab" type="checkbox" ${browserValues.browserCamofoxAdoptExistingTab ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesBrowserConfigCamofoxAdoptExistingTab')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-channel-footnote">${t('engine.hermesBrowserConfigFootnote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4816,6 +4841,10 @@ export function render() {
|
||||
browserAllowPrivateUrls: !!el.querySelector('#hm-browser-allow-private-urls')?.checked,
|
||||
browserAutoLocalForPrivateUrls: !!el.querySelector('#hm-browser-auto-local-for-private-urls')?.checked,
|
||||
browserCdpUrl: el.querySelector('#hm-browser-cdp-url')?.value || '',
|
||||
browserCamofoxManagedPersistence: !!el.querySelector('#hm-browser-camofox-managed-persistence')?.checked,
|
||||
browserCamofoxUserId: el.querySelector('#hm-browser-camofox-user-id')?.value || '',
|
||||
browserCamofoxSessionKey: el.querySelector('#hm-browser-camofox-session-key')?.value || '',
|
||||
browserCamofoxAdoptExistingTab: !!el.querySelector('#hm-browser-camofox-adopt-existing-tab')?.checked,
|
||||
browserDialogPolicy: el.querySelector('#hm-browser-dialog-policy')?.value || 'must_respond',
|
||||
browserDialogTimeout: el.querySelector('#hm-browser-dialog-timeout')?.value || '300',
|
||||
}
|
||||
|
||||
@@ -714,12 +714,17 @@ export default {
|
||||
hermesBrowserConfigAutoLocalForPrivateUrls: _('私网地址自动切到本地浏览器', 'Use local browser automatically for private URLs', '私網位址自動切到本機瀏覽器'),
|
||||
hermesBrowserConfigCdpUrl: _('CDP 连接地址', 'CDP endpoint URL', 'CDP 連線位址'),
|
||||
hermesBrowserConfigCdpUrlPlaceholder: _('留空则自动创建浏览器', 'Leave empty to launch browser automatically', '留空則自動建立瀏覽器'),
|
||||
hermesBrowserConfigCamofoxTitle: _('Camofox 高级身份', 'Camofox advanced identity', 'Camofox 進階身分'),
|
||||
hermesBrowserConfigCamofoxManagedPersistence: _('启用 Camofox 托管持久化', 'Enable Camofox managed persistence', '啟用 Camofox 託管持久化'),
|
||||
hermesBrowserConfigCamofoxUserId: _('Camofox 用户 ID', 'Camofox user ID', 'Camofox 使用者 ID'),
|
||||
hermesBrowserConfigCamofoxSessionKey: _('Camofox 会话 Key', 'Camofox session key', 'Camofox 工作階段 Key'),
|
||||
hermesBrowserConfigCamofoxAdoptExistingTab: _('接管已有 Camofox 标签页', 'Adopt existing Camofox tab', '接管既有 Camofox 分頁'),
|
||||
hermesBrowserConfigDialogPolicy: _('弹窗处理策略', 'Dialog handling policy', '彈窗處理策略'),
|
||||
hermesBrowserConfigDialogPolicy_must_respond: _('等待用户处理', 'Wait for user response', '等待使用者處理'),
|
||||
hermesBrowserConfigDialogPolicy_auto_dismiss: _('自动取消弹窗', 'Auto dismiss dialogs', '自動取消彈窗'),
|
||||
hermesBrowserConfigDialogPolicy_auto_accept: _('自动确认弹窗', 'Auto accept dialogs', '自動確認彈窗'),
|
||||
hermesBrowserConfigDialogTimeout: _('弹窗等待超时秒数', 'Dialog wait timeout seconds', '彈窗等待逾時秒數'),
|
||||
hermesBrowserConfigFootnote: _('Lightpanda 导航更快但不支持截图;录制会把 WebM 写入 Hermes browser_recordings 目录,请只在需要审计时开启。允许私网地址会放开 localhost / 192.168 等目标;CDP 地址留空表示自动创建浏览器;Camofox 嵌套高级字段会保留在 raw YAML 中。', 'Lightpanda navigates faster but does not support screenshots. Recording writes WebM files into the Hermes browser_recordings directory, so enable it only for audits. Allowing private URLs opens localhost / 192.168-style targets. Empty CDP URL means Hermes launches a browser automatically. Nested Camofox advanced fields stay in raw YAML.', 'Lightpanda 導覽更快但不支援截圖;錄製會把 WebM 寫入 Hermes browser_recordings 目錄,請只在需要稽核時開啟。允許私網位址會放開 localhost / 192.168 等目標;CDP 位址留空表示自動建立瀏覽器;Camofox 巢狀進階欄位會保留在 raw YAML 中。'),
|
||||
hermesBrowserConfigFootnote: _('Lightpanda 导航更快但不支持截图;录制会把 WebM 写入 Hermes browser_recordings 目录,请只在需要审计时开启。CDP 地址留空表示自动创建浏览器。Camofox 身份字段只影响 Camofox 后端,user_id / session_key 会复用远端 profile 或 session,不保存密码或 API Key;接管已有标签页仅在 Camofox 服务返回 tabs 时生效。', 'Lightpanda navigates faster but does not support screenshots. Recording writes WebM files into the Hermes browser_recordings directory, so enable it only for audits. Empty CDP URL means Hermes launches a browser automatically. Camofox identity fields only affect the Camofox backend; user_id / session_key reuse a remote profile or session and do not store passwords or API keys. Existing-tab adoption only works when the Camofox service returns tabs.', 'Lightpanda 導覽更快但不支援截圖;錄製會把 WebM 寫入 Hermes browser_recordings 目錄,請只在需要稽核時開啟。CDP 位址留空表示自動建立瀏覽器。Camofox 身分欄位只影響 Camofox 後端,user_id / session_key 會複用遠端 profile 或 session,不儲存密碼或 API Key;接管既有分頁僅在 Camofox 服務返回 tabs 時生效。'),
|
||||
hermesWebConfigTitle: _('Web 工具后端', 'Web tool backends', 'Web 工具後端'),
|
||||
hermesWebConfigDesc: _('控制 web_search / web_extract 的默认和分能力后端,只保存后端选择,不保存 API Key。', 'Control default and per-capability backends for web_search / web_extract. This stores backend choices only, not API keys.', '控制 web_search / web_extract 的預設和分能力後端,只儲存後端選擇,不儲存 API Key。'),
|
||||
hermesWebConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
|
||||
@@ -17,6 +17,10 @@ test('Hermes 浏览器配置读取会提供上游默认值', () => {
|
||||
browserAllowPrivateUrls: false,
|
||||
browserAutoLocalForPrivateUrls: true,
|
||||
browserCdpUrl: '',
|
||||
browserCamofoxManagedPersistence: false,
|
||||
browserCamofoxUserId: '',
|
||||
browserCamofoxSessionKey: '',
|
||||
browserCamofoxAdoptExistingTab: false,
|
||||
browserDialogPolicy: 'must_respond',
|
||||
browserDialogTimeout: 300,
|
||||
})
|
||||
@@ -32,6 +36,12 @@ test('Hermes 浏览器配置读取会回显 YAML 字段', () => {
|
||||
allow_private_urls: true,
|
||||
auto_local_for_private_urls: false,
|
||||
cdp_url: 'ws://127.0.0.1:9222/devtools/browser/demo',
|
||||
camofox: {
|
||||
managed_persistence: true,
|
||||
user_id: 'shared-camofox-user',
|
||||
session_key: 'shared-session-key',
|
||||
adopt_existing_tab: true,
|
||||
},
|
||||
dialog_policy: 'auto_accept',
|
||||
dialog_timeout_s: 120,
|
||||
},
|
||||
@@ -44,6 +54,10 @@ test('Hermes 浏览器配置读取会回显 YAML 字段', () => {
|
||||
assert.equal(values.browserAllowPrivateUrls, true)
|
||||
assert.equal(values.browserAutoLocalForPrivateUrls, false)
|
||||
assert.equal(values.browserCdpUrl, 'ws://127.0.0.1:9222/devtools/browser/demo')
|
||||
assert.equal(values.browserCamofoxManagedPersistence, true)
|
||||
assert.equal(values.browserCamofoxUserId, 'shared-camofox-user')
|
||||
assert.equal(values.browserCamofoxSessionKey, 'shared-session-key')
|
||||
assert.equal(values.browserCamofoxAdoptExistingTab, true)
|
||||
assert.equal(values.browserDialogPolicy, 'auto_accept')
|
||||
assert.equal(values.browserDialogTimeout, 120)
|
||||
})
|
||||
@@ -57,7 +71,13 @@ test('Hermes 浏览器配置保存会保留未知字段并写入上游结构', (
|
||||
record_sessions: false,
|
||||
engine: 'auto',
|
||||
cdp_url: 'ws://127.0.0.1:9222/devtools/browser/demo',
|
||||
camofox: { managed_persistence: true },
|
||||
camofox: {
|
||||
managed_persistence: false,
|
||||
user_id: 'old-user',
|
||||
session_key: 'old-session',
|
||||
adopt_existing_tab: false,
|
||||
custom_flag: 'keep-camofox',
|
||||
},
|
||||
custom_flag: 'keep-browser',
|
||||
},
|
||||
streaming: { enabled: true },
|
||||
@@ -69,6 +89,10 @@ test('Hermes 浏览器配置保存会保留未知字段并写入上游结构', (
|
||||
browserAllowPrivateUrls: true,
|
||||
browserAutoLocalForPrivateUrls: false,
|
||||
browserCdpUrl: 'http://127.0.0.1:9222',
|
||||
browserCamofoxManagedPersistence: true,
|
||||
browserCamofoxUserId: 'shared-camofox-user',
|
||||
browserCamofoxSessionKey: 'shared-session-key',
|
||||
browserCamofoxAdoptExistingTab: true,
|
||||
browserDialogPolicy: 'auto_dismiss',
|
||||
browserDialogTimeout: '45',
|
||||
})
|
||||
@@ -84,7 +108,40 @@ test('Hermes 浏览器配置保存会保留未知字段并写入上游结构', (
|
||||
assert.equal(next.browser.cdp_url, 'http://127.0.0.1:9222')
|
||||
assert.equal(next.browser.dialog_policy, 'auto_dismiss')
|
||||
assert.equal(next.browser.dialog_timeout_s, 45)
|
||||
assert.deepEqual(next.browser.camofox, { managed_persistence: true })
|
||||
assert.deepEqual(next.browser.camofox, {
|
||||
managed_persistence: true,
|
||||
user_id: 'shared-camofox-user',
|
||||
session_key: 'shared-session-key',
|
||||
adopt_existing_tab: true,
|
||||
custom_flag: 'keep-camofox',
|
||||
})
|
||||
assert.equal(next.browser.custom_flag, 'keep-browser')
|
||||
})
|
||||
|
||||
test('Hermes 浏览器配置保存空 Camofox 身份字段会删除对应覆盖', () => {
|
||||
const next = mergeHermesBrowserConfig({
|
||||
browser: {
|
||||
camofox: {
|
||||
managed_persistence: true,
|
||||
user_id: 'old-user',
|
||||
session_key: 'old-session',
|
||||
adopt_existing_tab: true,
|
||||
custom_flag: 'keep-camofox',
|
||||
},
|
||||
custom_flag: 'keep-browser',
|
||||
},
|
||||
}, {
|
||||
browserCamofoxManagedPersistence: false,
|
||||
browserCamofoxUserId: ' ',
|
||||
browserCamofoxSessionKey: '',
|
||||
browserCamofoxAdoptExistingTab: false,
|
||||
})
|
||||
|
||||
assert.equal(next.browser.camofox.managed_persistence, false)
|
||||
assert.equal(Object.hasOwn(next.browser.camofox, 'user_id'), false)
|
||||
assert.equal(Object.hasOwn(next.browser.camofox, 'session_key'), false)
|
||||
assert.equal(next.browser.camofox.adopt_existing_tab, false)
|
||||
assert.equal(next.browser.camofox.custom_flag, 'keep-camofox')
|
||||
assert.equal(next.browser.custom_flag, 'keep-browser')
|
||||
})
|
||||
|
||||
@@ -127,4 +184,16 @@ test('Hermes 浏览器配置保存会拒绝非法引擎和越界值', () => {
|
||||
() => mergeHermesBrowserConfig({}, { browserCdpUrl: 123 }),
|
||||
/browser\.cdp_url/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesBrowserConfig({}, { browserCamofoxUserId: 123 }),
|
||||
/browser\.camofox\.user_id/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesBrowserConfig({}, { browserCamofoxUserId: 'bad user' }),
|
||||
/browser\.camofox\.user_id/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesBrowserConfig({}, { browserCamofoxSessionKey: 'bad session' }),
|
||||
/browser\.camofox\.session_key/,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -458,6 +458,10 @@ test('Hermes 配置页会暴露浏览器基础结构化配置字段', () => {
|
||||
'hm-browser-allow-private-urls',
|
||||
'hm-browser-auto-local-for-private-urls',
|
||||
'hm-browser-cdp-url',
|
||||
'hm-browser-camofox-managed-persistence',
|
||||
'hm-browser-camofox-user-id',
|
||||
'hm-browser-camofox-session-key',
|
||||
'hm-browser-camofox-adopt-existing-tab',
|
||||
'hm-browser-dialog-policy',
|
||||
'hm-browser-dialog-timeout',
|
||||
]) {
|
||||
|
||||
Reference in New Issue
Block a user