fix(composio): align v3 execute path and honor configured entity_id (#322)
This commit is contained in:
parent
a403b5f5b1
commit
23b0f360c2
6 changed files with 86 additions and 32 deletions
|
|
@ -315,6 +315,8 @@ native_webdriver_url = "http://127.0.0.1:9515" # WebDriver endpoint (chromedrive
|
||||||
|
|
||||||
[composio]
|
[composio]
|
||||||
enabled = false # opt-in: 1000+ OAuth apps via composio.dev
|
enabled = false # opt-in: 1000+ OAuth apps via composio.dev
|
||||||
|
# api_key = "cmp_..." # optional: stored encrypted when [secrets].encrypt = true
|
||||||
|
entity_id = "default" # default user_id for Composio tool calls
|
||||||
|
|
||||||
[identity]
|
[identity]
|
||||||
format = "openclaw" # "openclaw" (default, markdown files) or "aieos" (JSON)
|
format = "openclaw" # "openclaw" (default, markdown files) or "aieos" (JSON)
|
||||||
|
|
|
||||||
|
|
@ -583,16 +583,20 @@ pub async fn run(
|
||||||
tracing::info!(backend = mem.name(), "Memory initialized");
|
tracing::info!(backend = mem.name(), "Memory initialized");
|
||||||
|
|
||||||
// ── Tools (including memory tools) ────────────────────────────
|
// ── Tools (including memory tools) ────────────────────────────
|
||||||
let composio_key = if config.composio.enabled {
|
let (composio_key, composio_entity_id) = if config.composio.enabled {
|
||||||
config.composio.api_key.as_deref()
|
(
|
||||||
|
config.composio.api_key.as_deref(),
|
||||||
|
Some(config.composio.entity_id.as_str()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
let tools_registry = tools::all_tools_with_runtime(
|
let tools_registry = tools::all_tools_with_runtime(
|
||||||
&security,
|
&security,
|
||||||
runtime,
|
runtime,
|
||||||
mem.clone(),
|
mem.clone(),
|
||||||
composio_key,
|
composio_key,
|
||||||
|
composio_entity_id,
|
||||||
&config.browser,
|
&config.browser,
|
||||||
&config.http_request,
|
&config.http_request,
|
||||||
&config.workspace_dir,
|
&config.workspace_dir,
|
||||||
|
|
@ -670,7 +674,7 @@ pub async fn run(
|
||||||
if config.composio.enabled {
|
if config.composio.enabled {
|
||||||
tool_descs.push((
|
tool_descs.push((
|
||||||
"composio",
|
"composio",
|
||||||
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). Use action='list' to discover, 'execute' to run, 'connect' to OAuth.",
|
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). Use action='list' to discover, 'execute' to run (optionally with connected_account_id), 'connect' to OAuth.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
tool_descs.push((
|
tool_descs.push((
|
||||||
|
|
|
||||||
|
|
@ -715,16 +715,20 @@ pub async fn start_channels(config: Config) -> Result<()> {
|
||||||
config.api_key.as_deref(),
|
config.api_key.as_deref(),
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
let composio_key = if config.composio.enabled {
|
let (composio_key, composio_entity_id) = if config.composio.enabled {
|
||||||
config.composio.api_key.as_deref()
|
(
|
||||||
|
config.composio.api_key.as_deref(),
|
||||||
|
Some(config.composio.entity_id.as_str()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
let tools_registry = Arc::new(tools::all_tools_with_runtime(
|
let tools_registry = Arc::new(tools::all_tools_with_runtime(
|
||||||
&security,
|
&security,
|
||||||
runtime,
|
runtime,
|
||||||
Arc::clone(&mem),
|
Arc::clone(&mem),
|
||||||
composio_key,
|
composio_key,
|
||||||
|
composio_entity_id,
|
||||||
&config.browser,
|
&config.browser,
|
||||||
&config.http_request,
|
&config.http_request,
|
||||||
&config.workspace_dir,
|
&config.workspace_dir,
|
||||||
|
|
@ -774,7 +778,7 @@ pub async fn start_channels(config: Config) -> Result<()> {
|
||||||
if config.composio.enabled {
|
if config.composio.enabled {
|
||||||
tool_descs.push((
|
tool_descs.push((
|
||||||
"composio",
|
"composio",
|
||||||
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). Use action='list' to discover, 'execute' to run, 'connect' to OAuth.",
|
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). Use action='list' to discover, 'execute' to run (optionally with connected_account_id), 'connect' to OAuth.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
tool_descs.push((
|
tool_descs.push((
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,13 @@ pub async fn run_gateway(host: &str, port: u16, config: Config) -> Result<()> {
|
||||||
&config.workspace_dir,
|
&config.workspace_dir,
|
||||||
));
|
));
|
||||||
|
|
||||||
let composio_key = if config.composio.enabled {
|
let (composio_key, composio_entity_id) = if config.composio.enabled {
|
||||||
config.composio.api_key.as_deref()
|
(
|
||||||
|
config.composio.api_key.as_deref(),
|
||||||
|
Some(config.composio.entity_id.as_str()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let tools_registry = Arc::new(tools::all_tools_with_runtime(
|
let tools_registry = Arc::new(tools::all_tools_with_runtime(
|
||||||
|
|
@ -262,6 +265,7 @@ pub async fn run_gateway(host: &str, port: u16, config: Config) -> Result<()> {
|
||||||
runtime,
|
runtime,
|
||||||
Arc::clone(&mem),
|
Arc::clone(&mem),
|
||||||
composio_key,
|
composio_key,
|
||||||
|
composio_entity_id,
|
||||||
&config.browser,
|
&config.browser,
|
||||||
&config.http_request,
|
&config.http_request,
|
||||||
&config.workspace_dir,
|
&config.workspace_dir,
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,15 @@ const COMPOSIO_API_BASE_V3: &str = "https://backend.composio.dev/api/v3";
|
||||||
/// A tool that proxies actions to the Composio managed tool platform.
|
/// A tool that proxies actions to the Composio managed tool platform.
|
||||||
pub struct ComposioTool {
|
pub struct ComposioTool {
|
||||||
api_key: String,
|
api_key: String,
|
||||||
|
default_entity_id: String,
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComposioTool {
|
impl ComposioTool {
|
||||||
pub fn new(api_key: &str) -> Self {
|
pub fn new(api_key: &str, default_entity_id: Option<&str>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
api_key: api_key.to_string(),
|
api_key: api_key.to_string(),
|
||||||
|
default_entity_id: normalize_entity_id(default_entity_id.unwrap_or("default")),
|
||||||
client: Client::builder()
|
client: Client::builder()
|
||||||
.timeout(std::time::Duration::from_secs(60))
|
.timeout(std::time::Duration::from_secs(60))
|
||||||
.connect_timeout(std::time::Duration::from_secs(10))
|
.connect_timeout(std::time::Duration::from_secs(10))
|
||||||
|
|
@ -59,9 +61,9 @@ impl ComposioTool {
|
||||||
let url = format!("{COMPOSIO_API_BASE_V3}/tools");
|
let url = format!("{COMPOSIO_API_BASE_V3}/tools");
|
||||||
let mut req = self.client.get(&url).header("x-api-key", &self.api_key);
|
let mut req = self.client.get(&url).header("x-api-key", &self.api_key);
|
||||||
|
|
||||||
req = req.query(&[("limit", 200_u16)]);
|
req = req.query(&[("limit", "200")]);
|
||||||
if let Some(app) = app_name {
|
if let Some(app) = app_name.map(str::trim).filter(|app| !app.is_empty()) {
|
||||||
req = req.query(&[("toolkit_slug", app)]);
|
req = req.query(&[("toolkits", app), ("toolkit_slug", app)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = req.send().await?;
|
let resp = req.send().await?;
|
||||||
|
|
@ -110,11 +112,12 @@ impl ComposioTool {
|
||||||
action_name: &str,
|
action_name: &str,
|
||||||
params: serde_json::Value,
|
params: serde_json::Value,
|
||||||
entity_id: Option<&str>,
|
entity_id: Option<&str>,
|
||||||
|
connected_account_id: Option<&str>,
|
||||||
) -> anyhow::Result<serde_json::Value> {
|
) -> anyhow::Result<serde_json::Value> {
|
||||||
let tool_slug = normalize_tool_slug(action_name);
|
let tool_slug = normalize_tool_slug(action_name);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.execute_action_v3(&tool_slug, params.clone(), entity_id)
|
.execute_action_v3(&tool_slug, params.clone(), entity_id, connected_account_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => Ok(result),
|
Ok(result) => Ok(result),
|
||||||
|
|
@ -132,8 +135,16 @@ impl ComposioTool {
|
||||||
tool_slug: &str,
|
tool_slug: &str,
|
||||||
params: serde_json::Value,
|
params: serde_json::Value,
|
||||||
entity_id: Option<&str>,
|
entity_id: Option<&str>,
|
||||||
|
connected_account_id: Option<&str>,
|
||||||
) -> anyhow::Result<serde_json::Value> {
|
) -> anyhow::Result<serde_json::Value> {
|
||||||
let url = format!("{COMPOSIO_API_BASE_V3}/tools/execute/{tool_slug}");
|
let url = if let Some(connected_account_id) = connected_account_id
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|id| !id.is_empty())
|
||||||
|
{
|
||||||
|
format!("{COMPOSIO_API_BASE_V3}/tools/{tool_slug}/execute/{connected_account_id}")
|
||||||
|
} else {
|
||||||
|
format!("{COMPOSIO_API_BASE_V3}/tools/{tool_slug}/execute")
|
||||||
|
};
|
||||||
|
|
||||||
let mut body = json!({
|
let mut body = json!({
|
||||||
"arguments": params,
|
"arguments": params,
|
||||||
|
|
@ -355,7 +366,7 @@ impl Tool for ComposioTool {
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). \
|
"Execute actions on 1000+ apps via Composio (Gmail, Notion, GitHub, Slack, etc.). \
|
||||||
Use action='list' to see available actions, action='execute' with action_name/tool_slug and params, \
|
Use action='list' to see available actions, action='execute' with action_name/tool_slug, params, and optional connected_account_id, \
|
||||||
or action='connect' with app/auth_config_id to get OAuth URL."
|
or action='connect' with app/auth_config_id to get OAuth URL."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,11 +397,15 @@ impl Tool for ComposioTool {
|
||||||
},
|
},
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Entity/user ID for multi-user setups (defaults to 'default')"
|
"description": "Entity/user ID for multi-user setups (defaults to composio.entity_id from config)"
|
||||||
},
|
},
|
||||||
"auth_config_id": {
|
"auth_config_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Optional Composio v3 auth config id for connect flow"
|
"description": "Optional Composio v3 auth config id for connect flow"
|
||||||
|
},
|
||||||
|
"connected_account_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Optional connected account ID for execute flow when a specific account is required"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["action"]
|
"required": ["action"]
|
||||||
|
|
@ -406,7 +421,7 @@ impl Tool for ComposioTool {
|
||||||
let entity_id = args
|
let entity_id = args
|
||||||
.get("entity_id")
|
.get("entity_id")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.unwrap_or("default");
|
.unwrap_or(self.default_entity_id.as_str());
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
"list" => {
|
"list" => {
|
||||||
|
|
@ -459,9 +474,11 @@ impl Tool for ComposioTool {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let params = args.get("params").cloned().unwrap_or(json!({}));
|
let params = args.get("params").cloned().unwrap_or(json!({}));
|
||||||
|
let connected_account_id =
|
||||||
|
args.get("connected_account_id").and_then(|v| v.as_str());
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.execute_action(action_name, params, Some(entity_id))
|
.execute_action(action_name, params, Some(entity_id), connected_account_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
|
|
@ -521,6 +538,15 @@ impl Tool for ComposioTool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize_entity_id(entity_id: &str) -> String {
|
||||||
|
let trimmed = entity_id.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
"default".to_string()
|
||||||
|
} else {
|
||||||
|
trimmed.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_tool_slug(action_name: &str) -> String {
|
fn normalize_tool_slug(action_name: &str) -> String {
|
||||||
action_name.trim().replace('_', "-").to_ascii_lowercase()
|
action_name.trim().replace('_', "-").to_ascii_lowercase()
|
||||||
}
|
}
|
||||||
|
|
@ -668,20 +694,20 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn composio_tool_has_correct_name() {
|
fn composio_tool_has_correct_name() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
assert_eq!(tool.name(), "composio");
|
assert_eq!(tool.name(), "composio");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn composio_tool_has_description() {
|
fn composio_tool_has_description() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
assert!(!tool.description().is_empty());
|
assert!(!tool.description().is_empty());
|
||||||
assert!(tool.description().contains("1000+"));
|
assert!(tool.description().contains("1000+"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn composio_tool_schema_has_required_fields() {
|
fn composio_tool_schema_has_required_fields() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let schema = tool.parameters_schema();
|
let schema = tool.parameters_schema();
|
||||||
assert!(schema["properties"]["action"].is_object());
|
assert!(schema["properties"]["action"].is_object());
|
||||||
assert!(schema["properties"]["action_name"].is_object());
|
assert!(schema["properties"]["action_name"].is_object());
|
||||||
|
|
@ -689,13 +715,14 @@ mod tests {
|
||||||
assert!(schema["properties"]["params"].is_object());
|
assert!(schema["properties"]["params"].is_object());
|
||||||
assert!(schema["properties"]["app"].is_object());
|
assert!(schema["properties"]["app"].is_object());
|
||||||
assert!(schema["properties"]["auth_config_id"].is_object());
|
assert!(schema["properties"]["auth_config_id"].is_object());
|
||||||
|
assert!(schema["properties"]["connected_account_id"].is_object());
|
||||||
let required = schema["required"].as_array().unwrap();
|
let required = schema["required"].as_array().unwrap();
|
||||||
assert!(required.contains(&json!("action")));
|
assert!(required.contains(&json!("action")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn composio_tool_spec_roundtrip() {
|
fn composio_tool_spec_roundtrip() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let spec = tool.spec();
|
let spec = tool.spec();
|
||||||
assert_eq!(spec.name, "composio");
|
assert_eq!(spec.name, "composio");
|
||||||
assert!(spec.parameters.is_object());
|
assert!(spec.parameters.is_object());
|
||||||
|
|
@ -705,14 +732,14 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn execute_missing_action_returns_error() {
|
async fn execute_missing_action_returns_error() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let result = tool.execute(json!({})).await;
|
let result = tool.execute(json!({})).await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn execute_unknown_action_returns_error() {
|
async fn execute_unknown_action_returns_error() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let result = tool.execute(json!({"action": "unknown"})).await.unwrap();
|
let result = tool.execute(json!({"action": "unknown"})).await.unwrap();
|
||||||
assert!(!result.success);
|
assert!(!result.success);
|
||||||
assert!(result.error.as_ref().unwrap().contains("Unknown action"));
|
assert!(result.error.as_ref().unwrap().contains("Unknown action"));
|
||||||
|
|
@ -720,14 +747,14 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn execute_without_action_name_returns_error() {
|
async fn execute_without_action_name_returns_error() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let result = tool.execute(json!({"action": "execute"})).await;
|
let result = tool.execute(json!({"action": "execute"})).await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn connect_without_target_returns_error() {
|
async fn connect_without_target_returns_error() {
|
||||||
let tool = ComposioTool::new("test-key");
|
let tool = ComposioTool::new("test-key", None);
|
||||||
let result = tool.execute(json!({"action": "connect"})).await;
|
let result = tool.execute(json!({"action": "connect"})).await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
@ -788,6 +815,12 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalize_entity_id_falls_back_to_default_when_blank() {
|
||||||
|
assert_eq!(normalize_entity_id(" "), "default");
|
||||||
|
assert_eq!(normalize_entity_id("workspace-user"), "workspace-user");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_tool_slug_supports_legacy_action_name() {
|
fn normalize_tool_slug_supports_legacy_action_name() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ pub fn default_tools_with_runtime(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create full tool registry including memory tools and optional Composio
|
/// Create full tool registry including memory tools and optional Composio
|
||||||
#[allow(clippy::implicit_hasher)]
|
#[allow(clippy::implicit_hasher, clippy::too_many_arguments)]
|
||||||
pub fn all_tools(
|
pub fn all_tools(
|
||||||
security: &Arc<SecurityPolicy>,
|
security: &Arc<SecurityPolicy>,
|
||||||
memory: Arc<dyn Memory>,
|
memory: Arc<dyn Memory>,
|
||||||
composio_key: Option<&str>,
|
composio_key: Option<&str>,
|
||||||
|
composio_entity_id: Option<&str>,
|
||||||
browser_config: &crate::config::BrowserConfig,
|
browser_config: &crate::config::BrowserConfig,
|
||||||
http_config: &crate::config::HttpRequestConfig,
|
http_config: &crate::config::HttpRequestConfig,
|
||||||
workspace_dir: &std::path::Path,
|
workspace_dir: &std::path::Path,
|
||||||
|
|
@ -76,6 +77,7 @@ pub fn all_tools(
|
||||||
Arc::new(NativeRuntime::new()),
|
Arc::new(NativeRuntime::new()),
|
||||||
memory,
|
memory,
|
||||||
composio_key,
|
composio_key,
|
||||||
|
composio_entity_id,
|
||||||
browser_config,
|
browser_config,
|
||||||
http_config,
|
http_config,
|
||||||
workspace_dir,
|
workspace_dir,
|
||||||
|
|
@ -86,12 +88,13 @@ pub fn all_tools(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create full tool registry including memory tools and optional Composio.
|
/// Create full tool registry including memory tools and optional Composio.
|
||||||
#[allow(clippy::implicit_hasher)]
|
#[allow(clippy::implicit_hasher, clippy::too_many_arguments)]
|
||||||
pub fn all_tools_with_runtime(
|
pub fn all_tools_with_runtime(
|
||||||
security: &Arc<SecurityPolicy>,
|
security: &Arc<SecurityPolicy>,
|
||||||
runtime: Arc<dyn RuntimeAdapter>,
|
runtime: Arc<dyn RuntimeAdapter>,
|
||||||
memory: Arc<dyn Memory>,
|
memory: Arc<dyn Memory>,
|
||||||
composio_key: Option<&str>,
|
composio_key: Option<&str>,
|
||||||
|
composio_entity_id: Option<&str>,
|
||||||
browser_config: &crate::config::BrowserConfig,
|
browser_config: &crate::config::BrowserConfig,
|
||||||
http_config: &crate::config::HttpRequestConfig,
|
http_config: &crate::config::HttpRequestConfig,
|
||||||
workspace_dir: &std::path::Path,
|
workspace_dir: &std::path::Path,
|
||||||
|
|
@ -146,7 +149,7 @@ pub fn all_tools_with_runtime(
|
||||||
|
|
||||||
if let Some(key) = composio_key {
|
if let Some(key) = composio_key {
|
||||||
if !key.is_empty() {
|
if !key.is_empty() {
|
||||||
tools.push(Box::new(ComposioTool::new(key)));
|
tools.push(Box::new(ComposioTool::new(key, composio_entity_id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,6 +209,7 @@ mod tests {
|
||||||
&security,
|
&security,
|
||||||
mem,
|
mem,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
&browser,
|
&browser,
|
||||||
&http,
|
&http,
|
||||||
tmp.path(),
|
tmp.path(),
|
||||||
|
|
@ -242,6 +246,7 @@ mod tests {
|
||||||
&security,
|
&security,
|
||||||
mem,
|
mem,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
&browser,
|
&browser,
|
||||||
&http,
|
&http,
|
||||||
tmp.path(),
|
tmp.path(),
|
||||||
|
|
@ -379,6 +384,7 @@ mod tests {
|
||||||
&security,
|
&security,
|
||||||
mem,
|
mem,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
&browser,
|
&browser,
|
||||||
&http,
|
&http,
|
||||||
tmp.path(),
|
tmp.path(),
|
||||||
|
|
@ -409,6 +415,7 @@ mod tests {
|
||||||
&security,
|
&security,
|
||||||
mem,
|
mem,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
&browser,
|
&browser,
|
||||||
&http,
|
&http,
|
||||||
tmp.path(),
|
tmp.path(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue