mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-29 04:10:00 +08:00
feat(hermes): add logging settings
This commit is contained in:
@@ -3327,6 +3327,7 @@ const HERMES_TERMINAL_BACKENDS = new Set(['local', 'ssh', 'docker', 'singularity
|
||||
const HERMES_BROWSER_ENGINES = new Set(['auto', 'lightpanda', 'chrome'])
|
||||
const HERMES_APPROVAL_MODES = new Set(['manual', 'smart', 'off'])
|
||||
const HERMES_APPROVAL_CRON_MODES = new Set(['deny', 'approve'])
|
||||
const HERMES_LOGGING_LEVELS = new Set(['DEBUG', 'INFO', 'WARNING'])
|
||||
const HERMES_AGENT_IMAGE_INPUT_MODES = new Set(['auto', 'native', 'text'])
|
||||
const HERMES_DISPLAY_TOOL_PROGRESS_VALUES = new Set(['off', 'new', 'all', 'verbose'])
|
||||
const HERMES_DISPLAY_STREAMING_VALUES = new Set(['inherit', 'true', 'false'])
|
||||
@@ -3426,6 +3427,13 @@ function normalizeHermesApprovalCronMode(value, strict = false) {
|
||||
return 'deny'
|
||||
}
|
||||
|
||||
function normalizeHermesLoggingLevel(value, strict = false) {
|
||||
const level = String(value ?? '').trim().toUpperCase() || 'INFO'
|
||||
if (HERMES_LOGGING_LEVELS.has(level)) return level
|
||||
if (strict) throw new Error('logging.level 必须是 DEBUG、INFO 或 WARNING')
|
||||
return 'INFO'
|
||||
}
|
||||
|
||||
function normalizeHermesImageInputMode(value, strict = false) {
|
||||
const mode = String(value ?? '').trim().toLowerCase() || 'auto'
|
||||
if (HERMES_AGENT_IMAGE_INPUT_MODES.has(mode)) return mode
|
||||
@@ -4091,6 +4099,43 @@ export function mergeHermesCronConfig(config = {}, form = {}) {
|
||||
return next
|
||||
}
|
||||
|
||||
export function buildHermesLoggingConfigValues(config = {}) {
|
||||
const root = config && typeof config === 'object' && !Array.isArray(config) ? config : {}
|
||||
const logging = root.logging && typeof root.logging === 'object' && !Array.isArray(root.logging)
|
||||
? root.logging
|
||||
: {}
|
||||
const memoryMonitor = logging.memory_monitor && typeof logging.memory_monitor === 'object' && !Array.isArray(logging.memory_monitor)
|
||||
? logging.memory_monitor
|
||||
: {}
|
||||
return {
|
||||
loggingLevel: normalizeHermesLoggingLevel(logging.level, false),
|
||||
loggingMaxSizeMb: parseHermesInteger(logging.max_size_mb, 'logging.max_size_mb', 5, 1, 102400, false),
|
||||
loggingBackupCount: parseHermesInteger(logging.backup_count, 'logging.backup_count', 3, 0, 1000, false),
|
||||
loggingMemoryMonitorEnabled: readHermesBool(memoryMonitor.enabled, true),
|
||||
loggingMemoryMonitorIntervalSeconds: parseHermesInteger(memoryMonitor.interval_seconds, 'logging.memory_monitor.interval_seconds', 300, 1, 86400, false),
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeHermesLoggingConfig(config = {}, form = {}) {
|
||||
const next = mergeConfigsPreservingFields({}, config && typeof config === 'object' && !Array.isArray(config) ? config : {})
|
||||
const currentValues = buildHermesLoggingConfigValues(next)
|
||||
const logging = next.logging && typeof next.logging === 'object' && !Array.isArray(next.logging)
|
||||
? mergeConfigsPreservingFields(next.logging, {})
|
||||
: {}
|
||||
const memoryMonitor = logging.memory_monitor && typeof logging.memory_monitor === 'object' && !Array.isArray(logging.memory_monitor)
|
||||
? mergeConfigsPreservingFields(logging.memory_monitor, {})
|
||||
: {}
|
||||
|
||||
logging.level = normalizeHermesLoggingLevel(Object.hasOwn(form, 'loggingLevel') ? form.loggingLevel : currentValues.loggingLevel, true)
|
||||
logging.max_size_mb = parseHermesInteger(Object.hasOwn(form, 'loggingMaxSizeMb') ? form.loggingMaxSizeMb : currentValues.loggingMaxSizeMb, 'logging.max_size_mb', 5, 1, 102400, true)
|
||||
logging.backup_count = parseHermesInteger(Object.hasOwn(form, 'loggingBackupCount') ? form.loggingBackupCount : currentValues.loggingBackupCount, 'logging.backup_count', 3, 0, 1000, true)
|
||||
memoryMonitor.enabled = formHermesBool(form, 'loggingMemoryMonitorEnabled', currentValues.loggingMemoryMonitorEnabled)
|
||||
memoryMonitor.interval_seconds = parseHermesInteger(Object.hasOwn(form, 'loggingMemoryMonitorIntervalSeconds') ? form.loggingMemoryMonitorIntervalSeconds : currentValues.loggingMemoryMonitorIntervalSeconds, 'logging.memory_monitor.interval_seconds', 300, 1, 86400, true)
|
||||
logging.memory_monitor = memoryMonitor
|
||||
next.logging = logging
|
||||
return next
|
||||
}
|
||||
|
||||
export function buildHermesApprovalsConfigValues(config = {}) {
|
||||
const root = config && typeof config === 'object' && !Array.isArray(config) ? config : {}
|
||||
const approvals = root.approvals && typeof root.approvals === 'object' && !Array.isArray(root.approvals)
|
||||
@@ -10855,6 +10900,27 @@ const handlers = {
|
||||
}
|
||||
},
|
||||
|
||||
hermes_logging_config_read() {
|
||||
const { configPath, exists, config } = readHermesConfigYamlObject()
|
||||
return {
|
||||
exists,
|
||||
configPath,
|
||||
values: buildHermesLoggingConfigValues(config),
|
||||
}
|
||||
},
|
||||
|
||||
hermes_logging_config_save({ form } = {}) {
|
||||
const { configPath, config } = readHermesConfigYamlObject()
|
||||
const next = mergeHermesLoggingConfig(config, form || {})
|
||||
const backup = writeHermesConfigYamlObject(configPath, next)
|
||||
return {
|
||||
ok: true,
|
||||
configPath,
|
||||
backup,
|
||||
values: buildHermesLoggingConfigValues(next),
|
||||
}
|
||||
},
|
||||
|
||||
hermes_approvals_config_read() {
|
||||
const { configPath, exists, config } = readHermesConfigYamlObject()
|
||||
return {
|
||||
|
||||
@@ -4668,6 +4668,23 @@ fn normalize_hermes_approval_cron_mode(
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_hermes_logging_level(value: Option<String>, strict: bool) -> Result<String, String> {
|
||||
let level = value.unwrap_or_default().trim().to_ascii_uppercase();
|
||||
let level = if level.is_empty() {
|
||||
"INFO".to_string()
|
||||
} else {
|
||||
level
|
||||
};
|
||||
if matches!(level.as_str(), "DEBUG" | "INFO" | "WARNING") {
|
||||
return Ok(level);
|
||||
}
|
||||
if strict {
|
||||
Err("logging.level 必须是 DEBUG、INFO 或 WARNING".to_string())
|
||||
} else {
|
||||
Ok("INFO".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn hermes_streaming_config_source(config: &serde_yaml::Value) -> Option<&serde_yaml::Mapping> {
|
||||
let root = config.as_mapping()?;
|
||||
if let Some(streaming) = yaml_get_mapping(root, "streaming") {
|
||||
@@ -5137,6 +5154,114 @@ fn merge_hermes_cron_config(config: &mut serde_yaml::Value, form: &Value) -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_hermes_logging_config_values(config: &serde_yaml::Value) -> Value {
|
||||
let root = config.as_mapping();
|
||||
let logging = root.and_then(|map| yaml_get_mapping(map, "logging"));
|
||||
let memory_monitor = logging.and_then(|map| yaml_get_mapping(map, "memory_monitor"));
|
||||
let logging_level = normalize_hermes_logging_level(
|
||||
logging.and_then(|map| yaml_string_field(map, "level")),
|
||||
false,
|
||||
)
|
||||
.unwrap_or_else(|_| "INFO".to_string());
|
||||
let logging_max_size_mb = logging
|
||||
.map(|map| bounded_hermes_i64(yaml_i64_field(map, "max_size_mb"), 5, 1, 102400))
|
||||
.unwrap_or(5);
|
||||
let logging_backup_count = logging
|
||||
.map(|map| bounded_hermes_i64(yaml_i64_field(map, "backup_count"), 3, 0, 1000))
|
||||
.unwrap_or(3);
|
||||
let logging_memory_monitor_enabled = memory_monitor
|
||||
.and_then(|map| yaml_bool_field(map, "enabled"))
|
||||
.unwrap_or(true);
|
||||
let logging_memory_monitor_interval_seconds = memory_monitor
|
||||
.map(|map| bounded_hermes_i64(yaml_i64_field(map, "interval_seconds"), 300, 1, 86400))
|
||||
.unwrap_or(300);
|
||||
|
||||
serde_json::json!({
|
||||
"loggingLevel": logging_level,
|
||||
"loggingMaxSizeMb": logging_max_size_mb,
|
||||
"loggingBackupCount": logging_backup_count,
|
||||
"loggingMemoryMonitorEnabled": logging_memory_monitor_enabled,
|
||||
"loggingMemoryMonitorIntervalSeconds": logging_memory_monitor_interval_seconds,
|
||||
})
|
||||
}
|
||||
|
||||
fn merge_hermes_logging_config(config: &mut serde_yaml::Value, form: &Value) -> Result<(), String> {
|
||||
let current = build_hermes_logging_config_values(config);
|
||||
let logging_level = normalize_hermes_logging_level(
|
||||
if form.get("loggingLevel").is_some() {
|
||||
form_string(form, "loggingLevel")
|
||||
} else {
|
||||
current["loggingLevel"].as_str().map(ToString::to_string)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
let logging_max_size_mb = validate_hermes_i64(
|
||||
if form.get("loggingMaxSizeMb").is_some() {
|
||||
form_i64(form, "loggingMaxSizeMb")
|
||||
} else {
|
||||
Some(current["loggingMaxSizeMb"].as_i64().unwrap_or(5))
|
||||
},
|
||||
"logging.max_size_mb",
|
||||
5,
|
||||
1,
|
||||
102400,
|
||||
)?;
|
||||
let logging_backup_count = validate_hermes_i64(
|
||||
if form.get("loggingBackupCount").is_some() {
|
||||
form_i64(form, "loggingBackupCount")
|
||||
} else {
|
||||
Some(current["loggingBackupCount"].as_i64().unwrap_or(3))
|
||||
},
|
||||
"logging.backup_count",
|
||||
3,
|
||||
0,
|
||||
1000,
|
||||
)?;
|
||||
let logging_memory_monitor_enabled = form_bool(form, "loggingMemoryMonitorEnabled")
|
||||
.unwrap_or_else(|| {
|
||||
current["loggingMemoryMonitorEnabled"]
|
||||
.as_bool()
|
||||
.unwrap_or(true)
|
||||
});
|
||||
let logging_memory_monitor_interval_seconds = validate_hermes_i64(
|
||||
if form.get("loggingMemoryMonitorIntervalSeconds").is_some() {
|
||||
form_i64(form, "loggingMemoryMonitorIntervalSeconds")
|
||||
} else {
|
||||
Some(
|
||||
current["loggingMemoryMonitorIntervalSeconds"]
|
||||
.as_i64()
|
||||
.unwrap_or(300),
|
||||
)
|
||||
},
|
||||
"logging.memory_monitor.interval_seconds",
|
||||
300,
|
||||
1,
|
||||
86400,
|
||||
)?;
|
||||
|
||||
let root = ensure_yaml_object(config)?;
|
||||
let logging = yaml_child_object(root, "logging")?;
|
||||
logging.insert(yaml_key("level"), serde_yaml::Value::String(logging_level));
|
||||
logging.insert(
|
||||
yaml_key("max_size_mb"),
|
||||
serde_yaml::Value::Number(logging_max_size_mb.into()),
|
||||
);
|
||||
logging.insert(
|
||||
yaml_key("backup_count"),
|
||||
serde_yaml::Value::Number(logging_backup_count.into()),
|
||||
);
|
||||
let memory_monitor = yaml_child_object(logging, "memory_monitor")?;
|
||||
memory_monitor.insert(
|
||||
yaml_key("enabled"),
|
||||
serde_yaml::Value::Bool(logging_memory_monitor_enabled),
|
||||
);
|
||||
memory_monitor.insert(
|
||||
yaml_key("interval_seconds"),
|
||||
serde_yaml::Value::Number(logging_memory_monitor_interval_seconds.into()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_hermes_approvals_config_values(config: &serde_yaml::Value) -> Value {
|
||||
let root = config.as_mapping();
|
||||
let approvals = root.and_then(|map| yaml_get_mapping(map, "approvals"));
|
||||
@@ -6886,6 +7011,30 @@ pub fn hermes_cron_config_save(form: Value) -> Result<Value, String> {
|
||||
}))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn hermes_logging_config_read() -> Result<Value, String> {
|
||||
let (config_path, exists, config) = read_hermes_channel_yaml_config()?;
|
||||
ensure_yaml_object(&mut config.clone())?;
|
||||
Ok(serde_json::json!({
|
||||
"exists": exists,
|
||||
"configPath": config_path.to_string_lossy(),
|
||||
"values": build_hermes_logging_config_values(&config),
|
||||
}))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn hermes_logging_config_save(form: Value) -> Result<Value, String> {
|
||||
let (config_path, _exists, mut config) = read_hermes_channel_yaml_config()?;
|
||||
merge_hermes_logging_config(&mut config, &form)?;
|
||||
let backup = write_hermes_yaml_config(&config_path, &config)?;
|
||||
Ok(serde_json::json!({
|
||||
"ok": true,
|
||||
"configPath": config_path.to_string_lossy(),
|
||||
"backup": backup,
|
||||
"values": build_hermes_logging_config_values(&config),
|
||||
}))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn hermes_approvals_config_read() -> Result<Value, String> {
|
||||
let (config_path, exists, config) = read_hermes_channel_yaml_config()?;
|
||||
@@ -12944,6 +13093,117 @@ cron:
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod hermes_logging_config_tests {
|
||||
use super::{build_hermes_logging_config_values, merge_hermes_logging_config};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn logging_values_have_upstream_defaults() {
|
||||
let config: serde_yaml::Value = serde_yaml::from_str("{}").unwrap();
|
||||
let values = build_hermes_logging_config_values(&config);
|
||||
assert_eq!(values["loggingLevel"], "INFO");
|
||||
assert_eq!(values["loggingMaxSizeMb"], 5);
|
||||
assert_eq!(values["loggingBackupCount"], 3);
|
||||
assert_eq!(values["loggingMemoryMonitorEnabled"], true);
|
||||
assert_eq!(values["loggingMemoryMonitorIntervalSeconds"], 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logging_values_read_yaml_fields() {
|
||||
let config: serde_yaml::Value = serde_yaml::from_str(
|
||||
r#"
|
||||
logging:
|
||||
level: DEBUG
|
||||
max_size_mb: 12
|
||||
backup_count: 7
|
||||
memory_monitor:
|
||||
enabled: false
|
||||
interval_seconds: 120
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let values = build_hermes_logging_config_values(&config);
|
||||
assert_eq!(values["loggingLevel"], "DEBUG");
|
||||
assert_eq!(values["loggingMaxSizeMb"], 12);
|
||||
assert_eq!(values["loggingBackupCount"], 7);
|
||||
assert_eq!(values["loggingMemoryMonitorEnabled"], false);
|
||||
assert_eq!(values["loggingMemoryMonitorIntervalSeconds"], 120);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_logging_config_preserves_unknown_fields() {
|
||||
let mut config: serde_yaml::Value = serde_yaml::from_str(
|
||||
r#"
|
||||
logging:
|
||||
level: INFO
|
||||
custom_flag: keep-logging
|
||||
memory_monitor:
|
||||
custom_flag: keep-memory-monitor
|
||||
cron:
|
||||
wrap_response: true
|
||||
streaming:
|
||||
enabled: true
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
merge_hermes_logging_config(
|
||||
&mut config,
|
||||
&json!({
|
||||
"loggingLevel": "WARNING",
|
||||
"loggingMaxSizeMb": "20",
|
||||
"loggingBackupCount": "5",
|
||||
"loggingMemoryMonitorEnabled": true,
|
||||
"loggingMemoryMonitorIntervalSeconds": "180",
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config["cron"]["wrap_response"].as_bool(), Some(true));
|
||||
assert_eq!(config["streaming"]["enabled"].as_bool(), Some(true));
|
||||
assert_eq!(config["logging"]["level"].as_str(), Some("WARNING"));
|
||||
assert_eq!(config["logging"]["max_size_mb"].as_i64(), Some(20));
|
||||
assert_eq!(config["logging"]["backup_count"].as_i64(), Some(5));
|
||||
assert_eq!(
|
||||
config["logging"]["memory_monitor"]["enabled"].as_bool(),
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
config["logging"]["memory_monitor"]["interval_seconds"].as_i64(),
|
||||
Some(180)
|
||||
);
|
||||
assert_eq!(
|
||||
config["logging"]["custom_flag"].as_str(),
|
||||
Some("keep-logging")
|
||||
);
|
||||
assert_eq!(
|
||||
config["logging"]["memory_monitor"]["custom_flag"].as_str(),
|
||||
Some("keep-memory-monitor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_logging_config_rejects_invalid_values() {
|
||||
let mut config = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
|
||||
let err = merge_hermes_logging_config(&mut config, &json!({ "loggingLevel": "TRACE" }))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("logging.level"));
|
||||
let err = merge_hermes_logging_config(&mut config, &json!({ "loggingMaxSizeMb": 0 }))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("logging.max_size_mb"));
|
||||
let err = merge_hermes_logging_config(&mut config, &json!({ "loggingBackupCount": -1 }))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("logging.backup_count"));
|
||||
let err = merge_hermes_logging_config(
|
||||
&mut config,
|
||||
&json!({ "loggingMemoryMonitorIntervalSeconds": 0 }),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.contains("logging.memory_monitor.interval_seconds"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod hermes_approvals_config_tests {
|
||||
use super::{build_hermes_approvals_config_values, merge_hermes_approvals_config};
|
||||
|
||||
@@ -291,6 +291,8 @@ pub fn run() {
|
||||
hermes::hermes_checkpoints_config_save,
|
||||
hermes::hermes_cron_config_read,
|
||||
hermes::hermes_cron_config_save,
|
||||
hermes::hermes_logging_config_read,
|
||||
hermes::hermes_logging_config_save,
|
||||
hermes::hermes_approvals_config_read,
|
||||
hermes::hermes_approvals_config_save,
|
||||
hermes::hermes_privacy_config_read,
|
||||
|
||||
@@ -141,6 +141,14 @@ const CRON_DEFAULTS = {
|
||||
cronMaxParallelJobs: 0,
|
||||
}
|
||||
|
||||
const LOGGING_DEFAULTS = {
|
||||
loggingLevel: 'INFO',
|
||||
loggingMaxSizeMb: 5,
|
||||
loggingBackupCount: 3,
|
||||
loggingMemoryMonitorEnabled: true,
|
||||
loggingMemoryMonitorIntervalSeconds: 300,
|
||||
}
|
||||
|
||||
const APPROVALS_DEFAULTS = {
|
||||
approvalMode: 'manual',
|
||||
approvalTimeout: 60,
|
||||
@@ -186,6 +194,7 @@ const DISPLAY_RESUME_VALUES = ['full', 'minimal']
|
||||
const HUMAN_DELAY_MODES = ['off', 'natural', 'custom']
|
||||
const APPROVAL_MODES = ['manual', 'smart', 'off']
|
||||
const APPROVAL_CRON_MODES = ['deny', 'approve']
|
||||
const LOGGING_LEVELS = ['DEBUG', 'INFO', 'WARNING']
|
||||
|
||||
export function render() {
|
||||
const el = document.createElement('div')
|
||||
@@ -209,6 +218,7 @@ export function render() {
|
||||
let ioSafetyValues = { ...IO_SAFETY_DEFAULTS }
|
||||
let checkpointsValues = { ...CHECKPOINTS_DEFAULTS }
|
||||
let cronValues = { ...CRON_DEFAULTS }
|
||||
let loggingValues = { ...LOGGING_DEFAULTS }
|
||||
let approvalsValues = { ...APPROVALS_DEFAULTS }
|
||||
let privacyValues = { ...PRIVACY_DEFAULTS }
|
||||
let browserValues = { ...BROWSER_DEFAULTS }
|
||||
@@ -231,6 +241,7 @@ export function render() {
|
||||
let ioSafetyLoading = true
|
||||
let checkpointsLoading = true
|
||||
let cronLoading = true
|
||||
let loggingLoading = true
|
||||
let approvalsLoading = true
|
||||
let privacyLoading = true
|
||||
let browserLoading = true
|
||||
@@ -253,6 +264,7 @@ export function render() {
|
||||
let ioSafetySaving = false
|
||||
let checkpointsSaving = false
|
||||
let cronSaving = false
|
||||
let loggingSaving = false
|
||||
let approvalsSaving = false
|
||||
let privacySaving = false
|
||||
let browserSaving = false
|
||||
@@ -275,6 +287,7 @@ export function render() {
|
||||
let ioSafetyError = null
|
||||
let checkpointsError = null
|
||||
let cronError = null
|
||||
let loggingError = null
|
||||
let approvalsError = null
|
||||
let privacyError = null
|
||||
let browserError = null
|
||||
@@ -289,7 +302,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function isBusy() {
|
||||
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || agentToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || approvalsLoading || privacyLoading || browserLoading || terminalLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
|
||||
return loading || runtimeLoading || compressionLoading || toolGuardrailsLoading || memoryLoading || skillsLoading || quickCommandsLoading || agentToolsetsLoading || agentRuntimeLoading || unauthorizedDmLoading || securityLoading || displayLoading || humanDelayLoading || streamingLoading || executionLimitsLoading || ioSafetyLoading || checkpointsLoading || cronLoading || loggingLoading || approvalsLoading || privacyLoading || browserLoading || terminalLoading || saving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
|
||||
}
|
||||
|
||||
function option(labelKey, value, selected) {
|
||||
@@ -306,7 +319,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderRuntimePanel() {
|
||||
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || runtimeLoading || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -354,7 +367,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderCompressionPanel() {
|
||||
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || compressionLoading || compressionSaving || runtimeSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-compression-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -404,7 +417,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderToolGuardrailsPanel() {
|
||||
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || toolGuardrailsLoading || toolGuardrailsSaving || runtimeSaving || compressionSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-guardrails-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -466,7 +479,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderMemoryPanel() {
|
||||
const disabled = loading || saving || memoryLoading || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || memoryLoading || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-memory-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -516,7 +529,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderSkillsConfigPanel() {
|
||||
const disabled = loading || saving || skillsLoading || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || skillsLoading || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-skills-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -548,7 +561,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderQuickCommandsConfigPanel() {
|
||||
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || quickCommandsLoading || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-quick-commands-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -574,7 +587,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderAgentToolsetsConfigPanel() {
|
||||
const disabled = loading || saving || agentToolsetsLoading || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || agentToolsetsLoading || agentToolsetsSaving || agentRuntimeSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-agent-toolsets-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -600,7 +613,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderAgentRuntimeConfigPanel() {
|
||||
const disabled = loading || saving || agentRuntimeLoading || agentRuntimeSaving || agentToolsetsSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
|
||||
const disabled = loading || saving || agentRuntimeLoading || agentRuntimeSaving || agentToolsetsSaving || unauthorizedDmSaving || securitySaving || displaySaving || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-agent-runtime-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -662,7 +675,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderUnauthorizedDmConfigPanel() {
|
||||
const disabled = loading || saving || unauthorizedDmLoading || unauthorizedDmSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || unauthorizedDmLoading || unauthorizedDmSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-unauthorized-dm-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -692,7 +705,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderSecurityConfigPanel() {
|
||||
const disabled = loading || saving || securityLoading || securitySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || securityLoading || securitySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-security-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -734,7 +747,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderDisplayConfigPanel() {
|
||||
const disabled = loading || saving || displayLoading || displaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || displayLoading || displaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || humanDelaySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-display-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -798,7 +811,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderHumanDelayConfigPanel() {
|
||||
const disabled = loading || saving || humanDelayLoading || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || humanDelayLoading || humanDelaySaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || streamingSaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-human-delay-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -836,7 +849,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderStreamingPanel() {
|
||||
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || executionLimitsSaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving
|
||||
const disabled = loading || saving || streamingLoading || streamingSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || securitySaving || executionLimitsSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-streaming-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -888,7 +901,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderExecutionLimitsPanel() {
|
||||
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || checkpointsSaving || cronSaving || approvalsSaving
|
||||
const disabled = loading || saving || executionLimitsLoading || executionLimitsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-execution-limits-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -960,7 +973,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderIoSafetyPanel() {
|
||||
const disabled = loading || saving || ioSafetyLoading || ioSafetySaving || checkpointsSaving || cronSaving || approvalsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || ioSafetyLoading || ioSafetySaving || checkpointsSaving || cronSaving || loggingSaving || approvalsSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-io-safety-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1000,7 +1013,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderCheckpointsPanel() {
|
||||
const disabled = loading || saving || checkpointsLoading || checkpointsSaving || ioSafetySaving || cronSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
|
||||
const disabled = loading || saving || checkpointsLoading || checkpointsSaving || ioSafetySaving || cronSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-checkpoints-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1058,7 +1071,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderCronPanel() {
|
||||
const disabled = loading || saving || cronLoading || cronSaving || checkpointsSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
|
||||
const disabled = loading || saving || cronLoading || cronSaving || checkpointsSaving || loggingSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-cron-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1091,8 +1104,56 @@ export function render() {
|
||||
`
|
||||
}
|
||||
|
||||
function renderLoggingPanel() {
|
||||
const disabled = loading || saving || loggingLoading || loggingSaving || checkpointsSaving || cronSaving || approvalsSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-logging-panel">
|
||||
<div class="hm-panel-header">
|
||||
<div>
|
||||
<div class="hm-panel-title">${t('engine.hermesLoggingConfigTitle')}</div>
|
||||
<div class="hm-channel-panel-desc">${t('engine.hermesLoggingConfigDesc')}</div>
|
||||
</div>
|
||||
<div class="hm-panel-actions">
|
||||
<span class="hm-muted">${loggingSaving ? t('engine.hermesConfigStatusSaving') : loggingLoading ? t('engine.hermesConfigStatusLoading') : t('engine.hermesLoggingConfigStatusReady')}</span>
|
||||
<button class="hm-btn hm-btn--cta hm-btn--sm" id="hm-logging-save" ${disabled ? 'disabled' : ''}>${t('engine.hermesLoggingConfigSave')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hm-panel-body">
|
||||
${renderError(loggingError)}
|
||||
<div class="hm-config-check-grid">
|
||||
<label class="hm-channel-check">
|
||||
<input id="hm-logging-memory-monitor-enabled" type="checkbox" ${loggingValues.loggingMemoryMonitorEnabled ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
|
||||
<span>${t('engine.hermesLoggingConfigMemoryMonitorEnabled')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-config-runtime-grid hm-config-logging-grid">
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesLoggingConfigLevel')}</span>
|
||||
<select id="hm-logging-level" class="hm-input" ${disabled ? 'disabled' : ''}>
|
||||
${LOGGING_LEVELS.map(level => option(`engine.hermesLoggingConfigLevel_${level}`, level, loggingValues.loggingLevel)).join('')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesLoggingConfigMaxSizeMb')}</span>
|
||||
<input id="hm-logging-max-size-mb" class="hm-input" type="number" inputmode="numeric" min="1" max="102400" step="1" value="${esc(loggingValues.loggingMaxSizeMb)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesLoggingConfigBackupCount')}</span>
|
||||
<input id="hm-logging-backup-count" class="hm-input" type="number" inputmode="numeric" min="0" max="1000" step="1" value="${esc(loggingValues.loggingBackupCount)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
<label class="hm-field">
|
||||
<span class="hm-field-label">${t('engine.hermesLoggingConfigMemoryMonitorIntervalSeconds')}</span>
|
||||
<input id="hm-logging-memory-monitor-interval-seconds" class="hm-input" type="number" inputmode="numeric" min="1" max="86400" step="1" value="${esc(loggingValues.loggingMemoryMonitorIntervalSeconds)}" ${disabled ? 'disabled' : ''}>
|
||||
</label>
|
||||
</div>
|
||||
<div class="hm-channel-footnote">${t('engine.hermesLoggingConfigFootnote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function renderApprovalsPanel() {
|
||||
const disabled = loading || saving || approvalsLoading || approvalsSaving || checkpointsSaving || cronSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
|
||||
const disabled = loading || saving || approvalsLoading || approvalsSaving || checkpointsSaving || cronSaving || loggingSaving || privacySaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-approvals-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1142,7 +1203,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderPrivacyPanel() {
|
||||
const disabled = loading || saving || privacyLoading || privacySaving || approvalsSaving || cronSaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
const disabled = loading || saving || privacyLoading || privacySaving || approvalsSaving || cronSaving || loggingSaving || browserSaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-privacy-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1170,7 +1231,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderBrowserPanel() {
|
||||
const disabled = loading || saving || browserLoading || browserSaving || approvalsSaving || cronSaving || privacySaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
const disabled = loading || saving || browserLoading || browserSaving || approvalsSaving || cronSaving || loggingSaving || privacySaving || terminalSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || ioSafetySaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-browser-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1214,7 +1275,7 @@ export function render() {
|
||||
}
|
||||
|
||||
function renderTerminalPanel() {
|
||||
const disabled = loading || saving || terminalLoading || terminalSaving || approvalsSaving || cronSaving || browserSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving
|
||||
const disabled = loading || saving || terminalLoading || terminalSaving || approvalsSaving || cronSaving || loggingSaving || browserSaving || runtimeSaving || compressionSaving || toolGuardrailsSaving || memorySaving || skillsSaving || quickCommandsSaving || agentToolsetsSaving || agentRuntimeSaving || unauthorizedDmSaving || streamingSaving || executionLimitsSaving || checkpointsSaving
|
||||
return `
|
||||
<div class="hm-panel hm-config-runtime-panel hm-config-terminal-panel">
|
||||
<div class="hm-panel-header">
|
||||
@@ -1305,6 +1366,7 @@ export function render() {
|
||||
${renderIoSafetyPanel()}
|
||||
${renderCheckpointsPanel()}
|
||||
${renderCronPanel()}
|
||||
${renderLoggingPanel()}
|
||||
${renderApprovalsPanel()}
|
||||
${renderPrivacyPanel()}
|
||||
${renderBrowserPanel()}
|
||||
@@ -1355,6 +1417,7 @@ export function render() {
|
||||
el.querySelector('#hm-io-safety-save')?.addEventListener('click', saveIoSafety)
|
||||
el.querySelector('#hm-checkpoints-save')?.addEventListener('click', saveCheckpoints)
|
||||
el.querySelector('#hm-cron-save')?.addEventListener('click', saveCronConfig)
|
||||
el.querySelector('#hm-logging-save')?.addEventListener('click', saveLoggingConfig)
|
||||
el.querySelector('#hm-approvals-save')?.addEventListener('click', saveApprovalsConfig)
|
||||
el.querySelector('#hm-privacy-save')?.addEventListener('click', savePrivacyConfig)
|
||||
el.querySelector('#hm-browser-save')?.addEventListener('click', saveBrowserConfig)
|
||||
@@ -1451,6 +1514,11 @@ export function render() {
|
||||
cronValues = { ...CRON_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function loadLoggingConfig() {
|
||||
const data = await api.hermesLoggingConfigRead()
|
||||
loggingValues = { ...LOGGING_DEFAULTS, ...(data?.values || {}) }
|
||||
}
|
||||
|
||||
async function loadApprovalsConfig() {
|
||||
const data = await api.hermesApprovalsConfigRead()
|
||||
approvalsValues = { ...APPROVALS_DEFAULTS, ...(data?.values || {}) }
|
||||
@@ -1490,6 +1558,7 @@ export function render() {
|
||||
ioSafetyLoading = true
|
||||
checkpointsLoading = true
|
||||
cronLoading = true
|
||||
loggingLoading = true
|
||||
approvalsLoading = true
|
||||
privacyLoading = true
|
||||
browserLoading = true
|
||||
@@ -1512,6 +1581,7 @@ export function render() {
|
||||
ioSafetyError = null
|
||||
checkpointsError = null
|
||||
cronError = null
|
||||
loggingError = null
|
||||
approvalsError = null
|
||||
privacyError = null
|
||||
browserError = null
|
||||
@@ -1588,6 +1658,14 @@ export function render() {
|
||||
cronLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadLoggingConfig()
|
||||
} catch (err) {
|
||||
loggingError = humanizeError(err, t('engine.hermesLoggingConfigLoadFailed') || 'Load logging config failed')
|
||||
} finally {
|
||||
loggingLoading = false
|
||||
draw()
|
||||
}
|
||||
try {
|
||||
await loadApprovalsConfig()
|
||||
} catch (err) {
|
||||
@@ -1764,6 +1842,9 @@ export function render() {
|
||||
try {
|
||||
await loadCronConfig()
|
||||
} catch {}
|
||||
try {
|
||||
await loadLoggingConfig()
|
||||
} catch {}
|
||||
try {
|
||||
await loadApprovalsConfig()
|
||||
} catch {}
|
||||
@@ -2277,6 +2358,35 @@ export function render() {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveLoggingConfig() {
|
||||
const form = {
|
||||
loggingLevel: el.querySelector('#hm-logging-level')?.value || 'INFO',
|
||||
loggingMaxSizeMb: el.querySelector('#hm-logging-max-size-mb')?.value || '5',
|
||||
loggingBackupCount: el.querySelector('#hm-logging-backup-count')?.value || '3',
|
||||
loggingMemoryMonitorEnabled: !!el.querySelector('#hm-logging-memory-monitor-enabled')?.checked,
|
||||
loggingMemoryMonitorIntervalSeconds: el.querySelector('#hm-logging-memory-monitor-interval-seconds')?.value || '300',
|
||||
}
|
||||
loggingSaving = true
|
||||
loggingError = null
|
||||
draw()
|
||||
try {
|
||||
const result = await api.hermesLoggingConfigSave(form)
|
||||
loggingValues = { ...LOGGING_DEFAULTS, ...(result?.values || form) }
|
||||
await refreshRawAfterStructuredSave()
|
||||
const backup = result?.backup || ''
|
||||
toast({
|
||||
message: t('engine.hermesLoggingConfigSaveSuccess'),
|
||||
hint: backup ? t('engine.hermesConfigBackupHint', { path: backup }) : '',
|
||||
}, 'success')
|
||||
} catch (err) {
|
||||
loggingError = humanizeError(err, t('engine.hermesLoggingConfigSaveFailed') || 'Save logging config failed')
|
||||
toast(loggingError, 'error')
|
||||
} finally {
|
||||
loggingSaving = false
|
||||
draw()
|
||||
}
|
||||
}
|
||||
|
||||
async function saveApprovalsConfig() {
|
||||
const form = {
|
||||
approvalMode: el.querySelector('#hm-approval-mode')?.value || 'manual',
|
||||
|
||||
@@ -543,6 +543,8 @@ export const api = {
|
||||
hermesCheckpointsConfigSave: (form) => invoke('hermes_checkpoints_config_save', { form }),
|
||||
hermesCronConfigRead: () => invoke('hermes_cron_config_read'),
|
||||
hermesCronConfigSave: (form) => invoke('hermes_cron_config_save', { form }),
|
||||
hermesLoggingConfigRead: () => invoke('hermes_logging_config_read'),
|
||||
hermesLoggingConfigSave: (form) => invoke('hermes_logging_config_save', { form }),
|
||||
hermesApprovalsConfigRead: () => invoke('hermes_approvals_config_read'),
|
||||
hermesApprovalsConfigSave: (form) => invoke('hermes_approvals_config_save', { form }),
|
||||
hermesPrivacyConfigRead: () => invoke('hermes_privacy_config_read'),
|
||||
|
||||
@@ -602,6 +602,22 @@ export default {
|
||||
hermesCronConfigWrapResponse: _('结果回传时附加任务名和系统提示', 'Add job name and system notice when delivering results', '結果回傳時附加任務名稱和系統提示'),
|
||||
hermesCronConfigMaxParallelJobs: _('每轮最多并发任务数', 'Max parallel jobs per tick', '每輪最多並發任務數'),
|
||||
hermesCronConfigFootnote: _('这里写入 cron.wrap_response 与 cron.max_parallel_jobs。并发数设为 0 表示不额外限制,由 Hermes 按上游默认语义写入 null;其他 cron 高级字段会保留在 raw YAML 中。', 'This writes cron.wrap_response and cron.max_parallel_jobs. Set parallel jobs to 0 for no extra cap; Hermes writes null to match upstream semantics. Other advanced cron fields stay in raw YAML.', '這裡寫入 cron.wrap_response 與 cron.max_parallel_jobs。並發數設為 0 表示不額外限制,由 Hermes 依上游預設語義寫入 null;其他 cron 進階欄位會保留在 raw YAML 中。'),
|
||||
hermesLoggingConfigTitle: _('运行日志', 'Runtime logs', '執行日誌'),
|
||||
hermesLoggingConfigDesc: _('控制 Hermes 日志级别、单文件轮转大小、保留备份数量和 Gateway 内存采样,便于长跑排障并避免日志无限膨胀。', 'Control Hermes log level, per-file rotation size, retained backups, and Gateway memory sampling for easier long-run diagnosis without unbounded logs.', '控制 Hermes 日誌級別、單檔輪轉大小、保留備份數量和 Gateway 記憶體取樣,便於長跑排障並避免日誌無限膨脹。'),
|
||||
hermesLoggingConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
hermesLoggingConfigSave: _('保存日志配置', 'Save log settings', '儲存日誌設定'),
|
||||
hermesLoggingConfigSaveSuccess: _('运行日志配置已保存,建议重启 Hermes Gateway 生效', 'Runtime log settings saved. Restart Hermes Gateway to take effect.', '執行日誌設定已儲存,建議重啟 Hermes Gateway 生效'),
|
||||
hermesLoggingConfigLoadFailed: _('加载运行日志配置失败', 'Load runtime log settings failed', '載入執行日誌設定失敗'),
|
||||
hermesLoggingConfigSaveFailed: _('保存运行日志配置失败', 'Save runtime log settings failed', '儲存執行日誌設定失敗'),
|
||||
hermesLoggingConfigLevel: _('日志级别', 'Log level', '日誌級別'),
|
||||
hermesLoggingConfigLevel_DEBUG: _('DEBUG(最详细)', 'DEBUG (most detailed)', 'DEBUG(最詳細)'),
|
||||
hermesLoggingConfigLevel_INFO: _('INFO(推荐)', 'INFO (recommended)', 'INFO(建議)'),
|
||||
hermesLoggingConfigLevel_WARNING: _('WARNING(仅警告和错误)', 'WARNING (warnings and errors only)', 'WARNING(僅警告和錯誤)'),
|
||||
hermesLoggingConfigMaxSizeMb: _('单个日志文件上限 MB', 'Max size per log file MB', '單一日誌檔上限 MB'),
|
||||
hermesLoggingConfigBackupCount: _('保留备份文件数', 'Retained backup files', '保留備份檔案數'),
|
||||
hermesLoggingConfigMemoryMonitorEnabled: _('启用 Gateway 内存采样', 'Enable Gateway memory sampling', '啟用 Gateway 記憶體取樣'),
|
||||
hermesLoggingConfigMemoryMonitorIntervalSeconds: _('内存采样间隔秒数', 'Memory sampling interval seconds', '記憶體取樣間隔秒數'),
|
||||
hermesLoggingConfigFootnote: _('这里写入 logging.* 与 logging.memory_monitor.*;其他 logging 高级字段会保留在 raw YAML 中。日志级别越低越详细,可能增加磁盘占用。', 'This writes logging.* and logging.memory_monitor.*. Other advanced logging fields stay in raw YAML. Lower log levels are more detailed and may increase disk usage.', '這裡寫入 logging.* 與 logging.memory_monitor.*;其他 logging 進階欄位會保留在 raw YAML 中。日誌級別越低越詳細,可能增加磁碟占用。'),
|
||||
hermesApprovalsConfigTitle: _('审批安全', 'Approval safety', '審批安全'),
|
||||
hermesApprovalsConfigDesc: _('控制危险命令、Cron 任务和破坏性 slash 命令的审批策略,避免无人值守长跑任务误放行高风险操作。', 'Control approval policy for dangerous commands, cron jobs, and destructive slash commands so unattended long runs do not approve risky operations by mistake.', '控制危險命令、Cron 任務和破壞性 slash 命令的審批策略,避免無人值守長跑任務誤放行高風險操作。'),
|
||||
hermesApprovalsConfigStatusReady: _('结构化配置', 'structured settings', '結構化設定'),
|
||||
|
||||
@@ -215,6 +215,19 @@ test('Hermes 配置页会暴露定时任务结构化配置字段', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('Hermes 配置页会暴露运行日志结构化配置字段', () => {
|
||||
for (const id of [
|
||||
'hm-logging-save',
|
||||
'hm-logging-level',
|
||||
'hm-logging-max-size-mb',
|
||||
'hm-logging-backup-count',
|
||||
'hm-logging-memory-monitor-enabled',
|
||||
'hm-logging-memory-monitor-interval-seconds',
|
||||
]) {
|
||||
assert.match(source, new RegExp(`id="${id}"`), `缺少 ${id}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('Hermes 配置页会暴露隐私脱敏结构化配置字段', () => {
|
||||
for (const id of [
|
||||
'hm-privacy-save',
|
||||
@@ -277,7 +290,8 @@ test('Hermes 配置页新增结构化配置不会暴露翻译 key', () => {
|
||||
key.includes('TerminalConfig') ||
|
||||
key.includes('CheckpointsConfig') ||
|
||||
key.includes('ApprovalsConfig') ||
|
||||
key.includes('CronConfig')
|
||||
key.includes('CronConfig') ||
|
||||
key.includes('LoggingConfig')
|
||||
)))
|
||||
|
||||
assert.ok(keys.size > 0, '应能提取新增结构化配置用到的 engine 翻译 key')
|
||||
|
||||
88
tests/hermes-logging-config.test.js
Normal file
88
tests/hermes-logging-config.test.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
buildHermesLoggingConfigValues,
|
||||
mergeHermesLoggingConfig,
|
||||
} from '../scripts/dev-api.js'
|
||||
|
||||
test('Hermes 运行日志配置读取会提供上游默认值', () => {
|
||||
const values = buildHermesLoggingConfigValues({})
|
||||
|
||||
assert.deepEqual(values, {
|
||||
loggingLevel: 'INFO',
|
||||
loggingMaxSizeMb: 5,
|
||||
loggingBackupCount: 3,
|
||||
loggingMemoryMonitorEnabled: true,
|
||||
loggingMemoryMonitorIntervalSeconds: 300,
|
||||
})
|
||||
})
|
||||
|
||||
test('Hermes 运行日志配置读取会回显 YAML 字段', () => {
|
||||
const values = buildHermesLoggingConfigValues({
|
||||
logging: {
|
||||
level: 'DEBUG',
|
||||
max_size_mb: 12,
|
||||
backup_count: 7,
|
||||
memory_monitor: {
|
||||
enabled: false,
|
||||
interval_seconds: 120,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(values.loggingLevel, 'DEBUG')
|
||||
assert.equal(values.loggingMaxSizeMb, 12)
|
||||
assert.equal(values.loggingBackupCount, 7)
|
||||
assert.equal(values.loggingMemoryMonitorEnabled, false)
|
||||
assert.equal(values.loggingMemoryMonitorIntervalSeconds, 120)
|
||||
})
|
||||
|
||||
test('Hermes 运行日志配置保存会保留未知字段并写入 logging', () => {
|
||||
const next = mergeHermesLoggingConfig({
|
||||
logging: {
|
||||
level: 'INFO',
|
||||
custom_flag: 'keep-logging',
|
||||
memory_monitor: {
|
||||
custom_flag: 'keep-memory-monitor',
|
||||
},
|
||||
},
|
||||
cron: { wrap_response: true },
|
||||
streaming: { enabled: true },
|
||||
}, {
|
||||
loggingLevel: 'WARNING',
|
||||
loggingMaxSizeMb: '20',
|
||||
loggingBackupCount: '5',
|
||||
loggingMemoryMonitorEnabled: true,
|
||||
loggingMemoryMonitorIntervalSeconds: '180',
|
||||
})
|
||||
|
||||
assert.deepEqual(next.cron, { wrap_response: true })
|
||||
assert.deepEqual(next.streaming, { enabled: true })
|
||||
assert.equal(next.logging.level, 'WARNING')
|
||||
assert.equal(next.logging.max_size_mb, 20)
|
||||
assert.equal(next.logging.backup_count, 5)
|
||||
assert.equal(next.logging.memory_monitor.enabled, true)
|
||||
assert.equal(next.logging.memory_monitor.interval_seconds, 180)
|
||||
assert.equal(next.logging.custom_flag, 'keep-logging')
|
||||
assert.equal(next.logging.memory_monitor.custom_flag, 'keep-memory-monitor')
|
||||
})
|
||||
|
||||
test('Hermes 运行日志配置保存会拒绝非法级别和越界值', () => {
|
||||
assert.throws(
|
||||
() => mergeHermesLoggingConfig({}, { loggingLevel: 'TRACE' }),
|
||||
/logging\.level/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesLoggingConfig({}, { loggingMaxSizeMb: '0' }),
|
||||
/logging\.max_size_mb/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesLoggingConfig({}, { loggingBackupCount: '-1' }),
|
||||
/logging\.backup_count/,
|
||||
)
|
||||
assert.throws(
|
||||
() => mergeHermesLoggingConfig({}, { loggingMemoryMonitorIntervalSeconds: '0' }),
|
||||
/logging\.memory_monitor\.interval_seconds/,
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user