Dev (#164)
* chore(release): stop publishing legacy Windows update packages * feat(agents): support OpenCode Desktop across platforms Detect CLI and desktop installations on Windows, macOS, and Linux, and add separate launch and restart controls. Closes #156 * fix(codex): align model display names and show total session count - preserve runtime model identity in generated catalogs - count distinct sessions across database and rollout sources - update session count labels in all supported languages * fix(codex): keep config and auth files in sync - generate auth.json for API key configuration - preserve and validate OAuth credentials - include auth.json in transactional updates and rollback - retain compatibility with legacy Codex configuration states * fix(cpa-gui): update version to 0.2.59 in Cargo files * fix(codex): preserve OAuth mode after closing configuration * feat(config): add TLS management and protocol-aware local URLs - add TLS controls to advanced settings - persist certificate and private key paths - restart the core after TLS changes - switch WebUI, API and management URLs between HTTP and HTTPS - synchronize agent configurations and detection with the active protocol * refactor(config): reorganize network, routing, and TLS settings - split network settings and session routing into separate tabs - move TLS configuration into network settings - preserve TLS restart and HTTPS URL synchronization behavior * feat(config): add independent network settings and custom listen IP * fix(navigation): allow access to usage records while core is stopped * refactor(home): show a single API URL based on the configured listen IP * feat(config): add file pickers for TLS certificate and private key
This commit is contained in:
1 parent
c3cd57bd8e
commit
2b9ceb11fb
21 files changed
+1355
-261
No files matched your search
@@ -582,12 +582,14 @@ pub(crate) async fn fetch_agent_models(
|
||||
if port == 0 {
|
||||
return Err("内核端口无效".to_string());
|
||||
}
|
||||
let tls_enabled = managed_core_tls_enabled();
|
||||
let client = reqwest::Client::builder()
|
||||
.connect_timeout(Duration::from_secs(3))
|
||||
.timeout(Duration::from_secs(15))
|
||||
.danger_accept_invalid_certs(tls_enabled)
|
||||
.build()
|
||||
.map_err(|error| format!("创建模型列表客户端失败: {error}"))?;
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let base_url = managed_core_loopback_origin(port);
|
||||
let endpoints = [
|
||||
format!("{base_url}/v1/models"),
|
||||
format!("{base_url}/models"),
|
||||
@@ -633,12 +635,14 @@ pub(crate) async fn fetch_codex_runtime_models(
|
||||
if port == 0 {
|
||||
return Err("内核端口无效".to_string());
|
||||
}
|
||||
let tls_enabled = managed_core_tls_enabled();
|
||||
let client = reqwest::Client::builder()
|
||||
.connect_timeout(Duration::from_secs(3))
|
||||
.timeout(Duration::from_secs(15))
|
||||
.danger_accept_invalid_certs(tls_enabled)
|
||||
.build()
|
||||
.map_err(|error| format!("创建 Codex 模型列表客户端失败: {error}"))?;
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let base_url = managed_core_loopback_origin(port);
|
||||
let endpoints = [
|
||||
format!("{base_url}/v1/models"),
|
||||
format!("{base_url}/models"),
|
||||
|
||||
@@ -42,7 +42,7 @@ pub(crate) fn build_agent_updates_with_oauth(
|
||||
claude_desktop_model_mappings,
|
||||
} = options;
|
||||
let paths = agent_config_paths(client, home);
|
||||
let root_base = format!("http://127.0.0.1:{port}");
|
||||
let root_base = managed_core_loopback_origin(port);
|
||||
let openai_base = format!("{root_base}/v1");
|
||||
match client {
|
||||
AgentClient::ClaudeCode => {
|
||||
|
||||
@@ -356,7 +356,7 @@ pub(crate) fn inspect_pi_provider_status(
|
||||
let object = root
|
||||
.as_object()
|
||||
.ok_or_else(|| "Pi CLIProxyAPI 配置根节点必须是 JSON 对象".to_string())?;
|
||||
let expected_base_url = format!("http://127.0.0.1:{port}");
|
||||
let expected_base_url = managed_core_loopback_origin(port);
|
||||
credentials_match = object
|
||||
.get("baseUrl")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -491,7 +491,7 @@ pub(crate) fn repair_pi_provider_inner(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let base_url = format!("http://127.0.0.1:{port}");
|
||||
let base_url = managed_core_loopback_origin(port);
|
||||
let rendered = build_pi_provider_config(existing.as_deref(), &base_url, api_key)?;
|
||||
if existing.as_deref() != Some(rendered.as_str()) {
|
||||
write_bytes_atomically(&config_path, rendered.as_bytes())?;
|
||||
@@ -1365,7 +1365,31 @@ pub(crate) fn agent_has_managed_marker(
|
||||
|
||||
pub(crate) fn is_managed_agent_base_url(value: &str) -> bool {
|
||||
let value = value.trim().to_ascii_lowercase();
|
||||
value.starts_with("http://127.0.0.1:") || value.starts_with("http://localhost:")
|
||||
if value.starts_with("http://127.0.0.1:")
|
||||
|| value.starts_with("http://localhost:")
|
||||
|| value.starts_with("https://127.0.0.1:")
|
||||
|| value.starts_with("https://localhost:")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
let Ok(settings) = read_installed_core_config_settings() else {
|
||||
return false;
|
||||
};
|
||||
let host = core_connect_host(&settings.host);
|
||||
let host = if host.contains(':') {
|
||||
format!("[{host}]")
|
||||
} else {
|
||||
host
|
||||
};
|
||||
value.starts_with(&format!("http://{host}:"))
|
||||
|| value.starts_with(&format!("https://{host}:"))
|
||||
}
|
||||
#[cfg(test)]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inspect_managed_toml_model_marker(
|
||||
@@ -2767,7 +2791,7 @@ pub(crate) fn inspect_claude_agent_config(
|
||||
)
|
||||
.map_err(|error| format!("解析 Claude Code 配置失败: {error}"))?;
|
||||
let env = root.get("env").and_then(serde_json::Value::as_object);
|
||||
let expected_base = format!("http://127.0.0.1:{port}");
|
||||
let expected_base = managed_core_loopback_origin(port);
|
||||
let configured = env
|
||||
.and_then(|env| env.get("ANTHROPIC_BASE_URL"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -3100,7 +3124,7 @@ pub(crate) fn inspect_claude_desktop_agent_config(
|
||||
let threep = read_agent_json_or_empty(&paths[1], "Claude Desktop 3P 配置")?;
|
||||
let profile = read_agent_json_or_empty(&paths[2], "Claude Desktop 网关配置")?;
|
||||
let meta = read_agent_json_or_empty(&paths[3], "Claude Desktop 配置索引")?;
|
||||
let expected_base = format!("http://127.0.0.1:{port}");
|
||||
let expected_base = managed_core_loopback_origin(port);
|
||||
let configured = normal
|
||||
.get("deploymentMode")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -3191,7 +3215,7 @@ pub(crate) fn inspect_codex_agent_config(
|
||||
&fs::read_to_string(path).map_err(|error| format!("读取 Codex 配置失败: {error}"))?,
|
||||
)
|
||||
.map_err(|error| format!("解析 Codex 配置失败: {error}"))?;
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let provider = root
|
||||
.get("model_providers")
|
||||
.and_then(toml::Value::as_table)
|
||||
@@ -3264,7 +3288,7 @@ pub(crate) fn inspect_opencode_agent_config(
|
||||
let provider = root
|
||||
.get("provider")
|
||||
.and_then(|providers| providers.get(MANAGED_AGENT_PROVIDER_ID));
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let configured = provider
|
||||
.and_then(|provider| provider.get("options"))
|
||||
.and_then(|options| options.get("baseURL"))
|
||||
@@ -3301,7 +3325,7 @@ pub(crate) fn inspect_zcode_agent_config(
|
||||
let provider = root
|
||||
.get("provider")
|
||||
.and_then(|providers| providers.get(MANAGED_AGENT_PROVIDER_ID));
|
||||
let expected_base = format!("http://127.0.0.1:{port}");
|
||||
let expected_base = managed_core_loopback_origin(port);
|
||||
let api_format_matches = provider
|
||||
.and_then(|provider| provider.get("apiFormat"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -3363,7 +3387,7 @@ pub(crate) fn inspect_kimi_code_agent_config(
|
||||
&fs::read_to_string(path).map_err(|error| format!("读取 Kimi Code 配置失败: {error}"))?,
|
||||
)
|
||||
.map_err(|error| format!("解析 Kimi Code 配置失败: {error}"))?;
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let provider = root
|
||||
.get("providers")
|
||||
.and_then(toml::Value::as_table)
|
||||
@@ -3420,7 +3444,7 @@ pub(crate) fn inspect_grok_build_agent_config(
|
||||
&fs::read_to_string(path).map_err(|error| format!("读取 Grok Build 配置失败: {error}"))?,
|
||||
)
|
||||
.map_err(|error| format!("解析 Grok Build 配置失败: {error}"))?;
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let prefix = format!("{MANAGED_AGENT_PROVIDER_ID}/");
|
||||
let selected = root
|
||||
.get("models")
|
||||
@@ -3474,7 +3498,7 @@ pub(crate) fn inspect_openclaw_agent_config(
|
||||
.get("models")
|
||||
.and_then(|models| models.get("providers"))
|
||||
.and_then(|providers| providers.get(MANAGED_AGENT_PROVIDER_ID));
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let configured = provider
|
||||
.and_then(|provider| provider.get("baseUrl"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
@@ -3518,7 +3542,7 @@ pub(crate) fn inspect_hermes_agent_config(
|
||||
== Some(MANAGED_AGENT_PROVIDER_ID)
|
||||
})
|
||||
});
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let configured = provider
|
||||
.and_then(|provider| provider.get("base_url"))
|
||||
.and_then(serde_yaml::Value::as_str)
|
||||
@@ -3574,7 +3598,7 @@ pub(crate) fn inspect_deepseek_harness_config(
|
||||
.map(str::to_string)
|
||||
})
|
||||
.flatten();
|
||||
let expected_base = format!("http://127.0.0.1:{port}/v1");
|
||||
let expected_base = format!("{}/v1", managed_core_loopback_origin(port));
|
||||
let credential = yaml_mapping_value(&credentials, DEEPSEEK_HARNESS_CREDENTIAL)
|
||||
.and_then(serde_norway::Value::as_str);
|
||||
let model_is_published = model.as_deref().is_some_and(|selected| {
|
||||
|
||||
@@ -689,7 +689,7 @@ pub(crate) fn fresh_agent_contents_with_oauth(
|
||||
claude_desktop_model_mappings,
|
||||
..
|
||||
} = options;
|
||||
let root_base = format!("http://127.0.0.1:{port}");
|
||||
let root_base = managed_core_loopback_origin(port);
|
||||
let openai_base = format!("{root_base}/v1");
|
||||
match client {
|
||||
AgentClient::ClaudeCode => Ok(vec![build_claude_agent_config(
|
||||
|
||||
Reference in new issue
Block a user