(
&self,
args: serde_json::Value,
ctx: ToolContext,
)
| 80 | } |
| 81 | |
| 82 | async fn execute( |
| 83 | &self, |
| 84 | args: serde_json::Value, |
| 85 | ctx: ToolContext, |
| 86 | ) -> Result<ToolResult, ToolError> { |
| 87 | let command: String = args["command"] |
| 88 | .as_str() |
| 89 | .ok_or_else(|| ToolError::InvalidArguments("command is required".into()))? |
| 90 | .to_string(); |
| 91 | |
| 92 | let timeout_ms: u64 = args["timeout"].as_u64().unwrap_or(DEFAULT_TIMEOUT_MS); |
| 93 | |
| 94 | let workdir: String = args["workdir"] |
| 95 | .as_str() |
| 96 | .map(|s| s.to_string()) |
| 97 | .unwrap_or_else(|| ctx.directory.clone()); |
| 98 | |
| 99 | let description: String = args["description"] |
| 100 | .as_str() |
| 101 | .ok_or_else(|| ToolError::InvalidArguments("description is required".into()))? |
| 102 | .to_string(); |
| 103 | |
| 104 | let title = description.clone(); |
| 105 | |
| 106 | let mut env_vars = std::collections::HashMap::new(); |
| 107 | for (key, value) in std::env::vars() { |
| 108 | env_vars.insert(key, value); |
| 109 | } |
| 110 | if let Some(extra_env) = ctx.extra.get("env") { |
| 111 | if let Some(env_obj) = extra_env.as_object() { |
| 112 | for (key, value) in env_obj { |
| 113 | if let Some(val_str) = value.as_str() { |
| 114 | env_vars.insert(key.clone(), val_str.to_string()); |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // Plugin hook: shell.env — let plugins inject environment variables |
| 121 | let mut hook_ctx = HookContext::new(HookEvent::ShellEnv) |
| 122 | .with_session(&ctx.session_id) |
| 123 | .with_data("cwd", serde_json::json!(&workdir)); |
| 124 | if let Some(call_id) = &ctx.call_id { |
| 125 | hook_ctx = hook_ctx.with_data("call_id", serde_json::json!(call_id)); |
| 126 | } |
| 127 | let env_hook_outputs = opencode_plugin::trigger_collect(hook_ctx).await; |
| 128 | for output in env_hook_outputs { |
| 129 | let Some(payload) = output.payload.as_ref() else { |
| 130 | continue; |
| 131 | }; |
| 132 | let Some(object) = payload |
| 133 | .get("output") |
| 134 | .and_then(|value| value.as_object()) |
| 135 | .or_else(|| payload.as_object()) |
| 136 | else { |
| 137 | continue; |
| 138 | }; |
| 139 | let Some(env) = object.get("env").and_then(|value| value.as_object()) else { |
nothing calls this directly
no test coverage detected