use std::pin::Pin;
use envconfig::Envconfig;
use futures::Stream;
use objectiveai_sdk::cli::command::{ResponseItem, parse_request};
use crate::context::Context;
use crate::error::Error;
#[cfg(windows)]
pub fn clear_stdio_inheritance() {
use windows_sys::Win32::Foundation::{HANDLE_FLAG_INHERIT, SetHandleInformation};
use windows_sys::Win32::System::Console::{
GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
};
unsafe {
for std_id in [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE] {
let h = GetStdHandle(std_id);
if !h.is_null() && h as isize != -1 {
let _ = SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0);
}
}
}
}
#[derive(Envconfig)]
struct EnvConfigBuilder {
#[envconfig(from = "CONFIG_SET_FORBIDDEN")]
config_set_forbidden: Option<String>,
#[envconfig(from = "CONFIG_BASE_DIR")]
config_base_dir: Option<String>,
#[envconfig(from = "COMMIT_AUTHOR_NAME")]
commit_author_name: Option<String>,
#[envconfig(from = "COMMIT_AUTHOR_EMAIL")]
commit_author_email: Option<String>,
#[envconfig(from = "GITHUB_AUTHORIZATION")]
github_authorization: Option<String>,
#[envconfig(from = "OBJECTIVEAI_AGENT_INSTANCE_HIERARCHY")]
agent_instance_hierarchy: Option<String>,
#[envconfig(from = "OBJECTIVEAI_AGENT_ID")]
agent_id: Option<String>,
#[envconfig(from = "OBJECTIVEAI_AGENT_FULL_ID")]
agent_full_id: Option<String>,
#[envconfig(from = "OBJECTIVEAI_AGENT_REMOTE")]
agent_remote: Option<String>,
#[envconfig(from = "OBJECTIVEAI_RESPONSE_ID")]
response_id: Option<String>,
#[envconfig(from = "OBJECTIVEAI_RESPONSE_IDS")]
response_ids: Option<String>,
#[envconfig(from = "MCP_SESSION_ID")]
mcp_session_id: Option<String>,
#[envconfig(from = "OBJECTIVEAI_PLUGIN_OWNER")]
plugin_owner: Option<String>,
#[envconfig(from = "OBJECTIVEAI_PLUGIN_REPOSITORY")]
plugin_repository: Option<String>,
#[envconfig(from = "OBJECTIVEAI_PLUGIN_VERSION")]
plugin_version: Option<String>,
}
impl EnvConfigBuilder {
pub fn build(self) -> ConfigBuilder {
fn parse_bool(s: &str) -> bool {
let v = s.trim();
!v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("false")
}
ConfigBuilder {
config_set_forbidden: self.config_set_forbidden.map(|s| parse_bool(&s)),
config_base_dir: self.config_base_dir,
commit_author_name: self.commit_author_name,
commit_author_email: self.commit_author_email,
github_authorization: self.github_authorization,
agent_instance_hierarchy: self.agent_instance_hierarchy,
agent_id: self.agent_id,
agent_full_id: self.agent_full_id,
agent_remote: self.agent_remote,
response_id: self.response_id,
response_ids: self.response_ids,
mcp_session_id: self.mcp_session_id,
plugin_owner: self.plugin_owner,
plugin_repository: self.plugin_repository,
plugin_version: self.plugin_version,
}
}
}
#[derive(Default)]
pub struct ConfigBuilder {
pub config_set_forbidden: Option<bool>,
pub config_base_dir: Option<String>,
pub commit_author_name: Option<String>,
pub commit_author_email: Option<String>,
pub github_authorization: Option<String>,
pub agent_instance_hierarchy: Option<String>,
pub agent_id: Option<String>,
pub agent_full_id: Option<String>,
pub agent_remote: Option<String>,
pub response_id: Option<String>,
pub response_ids: Option<String>,
pub mcp_session_id: Option<String>,
pub plugin_owner: Option<String>,
pub plugin_repository: Option<String>,
pub plugin_version: Option<String>,
}
impl Envconfig for ConfigBuilder {
#[allow(deprecated)]
fn init() -> Result<Self, envconfig::Error> {
EnvConfigBuilder::init().map(|e| e.build())
}
fn init_from_env() -> Result<Self, envconfig::Error> {
EnvConfigBuilder::init_from_env().map(|e| e.build())
}
fn init_from_hashmap(
hashmap: &std::collections::HashMap<String, String>,
) -> Result<Self, envconfig::Error> {
EnvConfigBuilder::init_from_hashmap(hashmap).map(|e| e.build())
}
}
impl ConfigBuilder {
pub fn build(self) -> Config {
Config {
config_set_forbidden: self.config_set_forbidden.unwrap_or(false),
config_base_dir: self.config_base_dir,
commit_author_name: self.commit_author_name,
commit_author_email: self.commit_author_email,
github_authorization: self.github_authorization,
agent_instance_hierarchy: self
.agent_instance_hierarchy
.unwrap_or_else(|| "cli".to_string()),
agent_id: self.agent_id,
agent_full_id: self.agent_full_id,
agent_remote: self.agent_remote,
response_id: self.response_id,
response_ids: self.response_ids,
mcp_session_id: self.mcp_session_id,
plugin_owner: self.plugin_owner,
plugin_repository: self.plugin_repository,
plugin_version: self.plugin_version,
}
}
}
#[derive(Debug, Clone)]
pub struct Config {
pub config_set_forbidden: bool,
pub config_base_dir: Option<String>,
pub commit_author_name: Option<String>,
pub commit_author_email: Option<String>,
pub github_authorization: Option<String>,
pub agent_instance_hierarchy: String,
pub agent_id: Option<String>,
pub agent_full_id: Option<String>,
pub agent_remote: Option<String>,
pub response_id: Option<String>,
pub response_ids: Option<String>,
pub mcp_session_id: Option<String>,
pub plugin_owner: Option<String>,
pub plugin_repository: Option<String>,
pub plugin_version: Option<String>,
}
pub type RunStream = Pin<Box<dyn Stream<Item = Result<ResponseItem, Error>> + Send>>;
pub fn load_config() -> Config {
ConfigBuilder::init_from_env().unwrap_or_default().build()
}
pub fn is_informational(e: &clap::Error) -> bool {
use clap::error::ErrorKind;
matches!(
e.kind(),
ErrorKind::DisplayHelp
| ErrorKind::DisplayVersion
| ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
)
}
pub async fn run(
args: Vec<String>,
ctx: Option<Context>,
) -> Result<RunStream, Error> {
#[cfg(windows)]
clear_stdio_inheritance();
let ctx = match ctx {
Some(c) => c,
None => Context::new(load_config()).await?,
};
let request = parse_request(args.get(1..).unwrap_or_default()).map_err(|e| match e {
objectiveai_sdk::cli::command::ParseError::Clap(e) => Error::ClapParse(e),
objectiveai_sdk::cli::command::ParseError::FromArgs(e) => Error::FromArgs(e),
})?;
crate::command::command::execute(&ctx, request).await
}