pub(crate) fn compose_url(addr: Option<&str>, port: Option<u16>) -> Option<String> {
fn ensure_scheme(a: &str) -> String {
if a.starts_with("http://") || a.starts_with("https://") {
a.to_string()
} else {
format!("http://{a}")
}
}
match (addr, port) {
(Some(a), Some(p)) => Some(format!("{}:{p}", ensure_scheme(a).trim_end_matches('/'))),
(Some(a), None) => Some(ensure_scheme(a)),
(None, Some(p)) => Some(format!("http://127.0.0.1:{p}")),
(None, None) => None,
}
}
fn env(name: &str) -> Option<String> {
std::env::var(name).ok()
}
pub fn build_http_client(
config: &mut objectiveai_sdk::filesystem::config::Config,
) -> objectiveai_sdk::HttpClient {
let address = env("OBJECTIVEAI_ADDRESS").or_else(|| {
let api = config.api();
compose_url(api.get_address(), api.get_port())
});
let authorization = env("OBJECTIVEAI_AUTHORIZATION")
.or_else(|| config.api().get_objectiveai_authorization().map(String::from));
let user_agent = env("USER_AGENT")
.or_else(|| config.api().get_user_agent().map(String::from));
let x_title = env("X_TITLE")
.or_else(|| config.api().get_x_title().map(String::from));
let http_referer = env("HTTP_REFERER")
.or_else(|| config.api().get_http_referer().map(String::from));
let x_github_authorization = env("GITHUB_AUTHORIZATION")
.or_else(|| config.api().get_github_authorization().map(String::from));
let x_openrouter_authorization = env("OPENROUTER_AUTHORIZATION")
.or_else(|| config.api().get_openrouter_authorization().map(String::from));
let x_mcp_authorization: Option<std::collections::HashMap<String, String>> =
env("MCP_AUTHORIZATION")
.and_then(|v| serde_json::from_str(&v).ok())
.or_else(|| {
config
.api()
.get_mcp_authorization()
.map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
});
let x_viewer_signature = env("VIEWER_SIGNATURE")
.or_else(|| config.viewer().get_signature().map(String::from));
let x_viewer_address = env("VIEWER_ADDRESS").or_else(|| {
let viewer = config.viewer();
compose_url(viewer.get_address(), viewer.get_port())
});
let x_commit_author_name = env("COMMIT_AUTHOR_NAME")
.or_else(|| config.api().get_commit_author_name().map(String::from));
let x_commit_author_email = env("COMMIT_AUTHOR_EMAIL")
.or_else(|| config.api().get_commit_author_email().map(String::from));
let agent_id = env("OBJECTIVEAI_AGENT_ID");
objectiveai_sdk::HttpClient::new(
reqwest::Client::new(),
address,
authorization,
user_agent,
x_title,
http_referer,
x_github_authorization,
x_openrouter_authorization,
x_mcp_authorization,
x_viewer_signature,
x_viewer_address,
x_commit_author_name,
x_commit_author_email,
agent_id,
)
}
pub fn build_viewer_client(
config: &mut objectiveai_sdk::filesystem::config::Config,
) -> objectiveai_sdk::http::viewer::Client {
let address = env("VIEWER_ADDRESS").or_else(|| {
let viewer = config.viewer();
compose_url(viewer.get_address(), viewer.get_port())
});
let signature = env("VIEWER_SIGNATURE")
.or_else(|| config.viewer().get_signature().map(String::from));
objectiveai_sdk::http::viewer::Client::new(
reqwest::Client::new(),
address,
signature,
std::time::Duration::from_millis(100),
std::time::Duration::from_millis(100),
0.5,
2.0,
std::time::Duration::from_secs(2),
std::time::Duration::from_secs(5),
)
}
#[cfg(test)]
mod tests {
use super::compose_url;
#[test]
fn compose_url_cases() {
assert_eq!(compose_url(None, None), None);
assert_eq!(
compose_url(Some("127.0.0.1"), Some(8080)).as_deref(),
Some("http://127.0.0.1:8080"),
);
assert_eq!(
compose_url(Some("::1"), Some(8080)).as_deref(),
Some("http://::1:8080"),
);
assert_eq!(
compose_url(Some("::1"), None).as_deref(),
Some("http://::1"),
);
assert_eq!(
compose_url(Some("api.x"), None).as_deref(),
Some("http://api.x"),
);
assert_eq!(
compose_url(Some("https://api.x"), None).as_deref(),
Some("https://api.x"),
);
assert_eq!(
compose_url(Some("https://api.x"), Some(443)).as_deref(),
Some("https://api.x:443"),
);
assert_eq!(
compose_url(Some("https://api.x/"), Some(443)).as_deref(),
Some("https://api.x:443"),
);
assert_eq!(
compose_url(None, Some(8080)).as_deref(),
Some("http://127.0.0.1:8080"),
);
}
}