feat: Secret injection as environment variables into agent tools
This commit is contained in:
+11
-1
@@ -5,7 +5,9 @@ use crate::{
|
|||||||
function::{run_llm_function, Functions},
|
function::{run_llm_function, Functions},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::vault::SECRET_RE;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use fancy_regex::Captures;
|
||||||
use inquire::{validator::Validation, Text};
|
use inquire::{validator::Validation, Text};
|
||||||
use rust_embed::Embed;
|
use rust_embed::Embed;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -30,6 +32,7 @@ pub struct Agent {
|
|||||||
functions: Functions,
|
functions: Functions,
|
||||||
rag: Option<Arc<Rag>>,
|
rag: Option<Arc<Rag>>,
|
||||||
model: Model,
|
model: Model,
|
||||||
|
vault: GlobalVault,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Agent {
|
impl Agent {
|
||||||
@@ -195,6 +198,7 @@ impl Agent {
|
|||||||
functions,
|
functions,
|
||||||
rag,
|
rag,
|
||||||
model,
|
model,
|
||||||
|
vault: Arc::clone(&config.read().vault),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +329,13 @@ impl Agent {
|
|||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
(
|
(
|
||||||
format!("LLM_AGENT_VAR_{}", normalize_env_name(k)),
|
format!("LLM_AGENT_VAR_{}", normalize_env_name(k)),
|
||||||
v.clone(),
|
SECRET_RE
|
||||||
|
.replace(v, |caps: &Captures| {
|
||||||
|
self.vault
|
||||||
|
.get_secret(caps[1].trim(), false)
|
||||||
|
.unwrap_or(v.clone())
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
+14
-10
@@ -24,7 +24,7 @@ use crate::utils::*;
|
|||||||
use crate::mcp::{
|
use crate::mcp::{
|
||||||
McpRegistry, MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX,
|
McpRegistry, MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX,
|
||||||
};
|
};
|
||||||
use crate::vault::{create_vault_password_file, interpolate_secrets, Vault};
|
use crate::vault::{create_vault_password_file, interpolate_secrets, GlobalVault, Vault};
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@@ -159,7 +159,6 @@ pub struct Config {
|
|||||||
pub left_prompt: Option<String>,
|
pub left_prompt: Option<String>,
|
||||||
pub right_prompt: Option<String>,
|
pub right_prompt: Option<String>,
|
||||||
|
|
||||||
pub serve_addr: Option<String>,
|
|
||||||
pub user_agent: Option<String>,
|
pub user_agent: Option<String>,
|
||||||
pub save_shell_history: bool,
|
pub save_shell_history: bool,
|
||||||
pub sync_models_url: Option<String>,
|
pub sync_models_url: Option<String>,
|
||||||
@@ -167,7 +166,7 @@ pub struct Config {
|
|||||||
pub clients: Vec<ClientConfig>,
|
pub clients: Vec<ClientConfig>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub vault: Vault,
|
pub vault: GlobalVault,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub macro_flag: bool,
|
pub macro_flag: bool,
|
||||||
@@ -244,7 +243,6 @@ impl Default for Config {
|
|||||||
left_prompt: None,
|
left_prompt: None,
|
||||||
right_prompt: None,
|
right_prompt: None,
|
||||||
|
|
||||||
serve_addr: None,
|
|
||||||
user_agent: None,
|
user_agent: None,
|
||||||
save_shell_history: true,
|
save_shell_history: true,
|
||||||
sync_models_url: None,
|
sync_models_url: None,
|
||||||
@@ -317,7 +315,9 @@ impl Config {
|
|||||||
|
|
||||||
let (parsed_config, missing_secrets) = interpolate_secrets(&content, &vault);
|
let (parsed_config, missing_secrets) = interpolate_secrets(&content, &vault);
|
||||||
if !missing_secrets.is_empty() && !info_flag {
|
if !missing_secrets.is_empty() && !info_flag {
|
||||||
debug!("Global config references secrets that are missing from the vault: {missing_secrets:?}");
|
debug!(
|
||||||
|
"Global config references secrets that are missing from the vault: {missing_secrets:?}"
|
||||||
|
);
|
||||||
return Err(anyhow!(formatdoc!(
|
return Err(anyhow!(formatdoc!(
|
||||||
"
|
"
|
||||||
Global config file references secrets that are missing from the vault: {:?}
|
Global config file references secrets that are missing from the vault: {:?}
|
||||||
@@ -341,7 +341,7 @@ impl Config {
|
|||||||
|
|
||||||
config.working_mode = working_mode;
|
config.working_mode = working_mode;
|
||||||
config.info_flag = info_flag;
|
config.info_flag = info_flag;
|
||||||
config.vault = vault;
|
config.vault = Arc::new(vault);
|
||||||
|
|
||||||
Agent::install_builtin_agents()?;
|
Agent::install_builtin_agents()?;
|
||||||
|
|
||||||
@@ -769,7 +769,9 @@ impl Config {
|
|||||||
if let Some(servers) = value.as_ref() {
|
if let Some(servers) = value.as_ref() {
|
||||||
if let Some(registry) = &config.read().mcp_registry {
|
if let Some(registry) = &config.read().mcp_registry {
|
||||||
if registry.list_configured_servers().is_empty() {
|
if registry.list_configured_servers().is_empty() {
|
||||||
bail!("No MCP servers are configured. Please configure MCP servers first before setting 'use_mcp_servers'.");
|
bail!(
|
||||||
|
"No MCP servers are configured. Please configure MCP servers first before setting 'use_mcp_servers'."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !servers.split(',').all(|s| {
|
if !servers.split(',').all(|s| {
|
||||||
@@ -778,7 +780,9 @@ impl Config {
|
|||||||
.contains(&s.trim().to_string())
|
.contains(&s.trim().to_string())
|
||||||
|| s == "all"
|
|| s == "all"
|
||||||
}) {
|
}) {
|
||||||
bail!("Some of the specified MCP servers in 'use_mcp_servers' are configured. Please check your MCP server configuration.");
|
bail!(
|
||||||
|
"Some of the specified MCP servers in 'use_mcp_servers' are configured. Please check your MCP server configuration."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2562,8 +2566,8 @@ impl Config {
|
|||||||
None => String::new(),
|
None => String::new(),
|
||||||
};
|
};
|
||||||
let output = format!(
|
let output = format!(
|
||||||
"# CHAT: {summary} [{now}]{scope}\n{raw_input}\n--------\n{tool_calls}{output}\n--------\n\n",
|
"# CHAT: {summary} [{now}]{scope}\n{raw_input}\n--------\n{tool_calls}{output}\n--------\n\n",
|
||||||
);
|
);
|
||||||
file.write_all(output.as_bytes())
|
file.write_all(output.as_bytes())
|
||||||
.with_context(|| "Failed to save message")
|
.with_context(|| "Failed to save message")
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -12,16 +12,18 @@ use fancy_regex::Regex;
|
|||||||
use gman::providers::local::LocalProvider;
|
use gman::providers::local::LocalProvider;
|
||||||
use gman::providers::SecretProvider;
|
use gman::providers::SecretProvider;
|
||||||
use inquire::{required, Password, PasswordDisplayMode};
|
use inquire::{required, Password, PasswordDisplayMode};
|
||||||
use std::sync::LazyLock;
|
use std::sync::{Arc, LazyLock};
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
|
||||||
static SECRET_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{\{(.+)}}").unwrap());
|
pub static SECRET_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{\{(.+)}}").unwrap());
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Vault {
|
pub struct Vault {
|
||||||
local_provider: LocalProvider,
|
local_provider: LocalProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type GlobalVault = Arc<Vault>;
|
||||||
|
|
||||||
impl Vault {
|
impl Vault {
|
||||||
pub fn init_bare() -> Self {
|
pub fn init_bare() -> Self {
|
||||||
let vault_password_file = Config::default().vault_password_file();
|
let vault_password_file = Config::default().vault_password_file();
|
||||||
|
|||||||
Reference in New Issue
Block a user