Files
aiops/backend/pkg/agentcli/tools/ssh_execute.go
T
Logan 1acb0c49af feat: Agent CLI 与终端审计回放全面优化
- Agent CLI: 多轮对话、SSH 长连接、流式推理、Evolver 按用户隔离、历史侧栏
- 终端审计: 回放播放器、命令书签、筛选分页导出、命令输出统计、录制落盘修复
- 修复 session_commands 表迁移、Kill 竞态导致录制丢失、无录制会话回放提示
- 审计历史页只读(移除删除),移除回放弹框录制搜索
2026-07-01 12:13:53 +08:00

106 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ssh_execute.go - 远程命令执行工具 // Logan.Li
// 复用 aiops 既有 pkg/remoteexec(Go 原生 SSH),凭据由 controller 从 model.Server 构建后注入。
// 任务级复用 PersistentClient 长连接,避免每条命令重建 SSH 握手。
// 实现 DynamicRiskTool:依据具体命令动态评估风险,决定是否触发用户确认。
package tools
import (
"context"
"fmt"
"strings"
"time"
"ezops/pkg/remoteexec"
)
// SSHExecuteTool 远程命令执行工具 // Logan.Li
type SSHExecuteTool struct {
client *remoteexec.PersistentClient // 任务级 SSH 长连接
timeout time.Duration // 默认命令超时
}
// NewSSHExecuteTool 创建远程执行工具(内部建立 SSH 长连接) // Logan.Li
func NewSSHExecuteTool(cfg remoteexec.SSHConfig, timeout time.Duration) (*SSHExecuteTool, error) {
if timeout <= 0 {
timeout = 60 * time.Second
}
client, err := remoteexec.NewPersistentClient(cfg)
if err != nil {
return nil, fmt.Errorf("SSH 长连接建立失败: %w", err)
}
return &SSHExecuteTool{client: client, timeout: timeout}, nil
}
// Close 释放 SSH 长连接 // Logan.Li
func (t *SSHExecuteTool) Close() error {
if t.client == nil {
return nil
}
return t.client.Close()
}
// Name 工具名 // Logan.Li
func (t *SSHExecuteTool) Name() string { return "ssh_execute" }
// Description 给 LLM 的工具说明 // Logan.Li
func (t *SSHExecuteTool) Description() string {
return "在远程目标服务器上执行 shell 命令并返回合并的 stdout+stderr 输出与退出码。" +
"用于排障(查看日志/进程/磁盘/网络)、安装与配置软件、重启服务等。" +
"高危命令(rm -rf /、mkfs、reboot 等)需用户确认。"
}
// Parameters JSON Schema 参数定义 // Logan.Li
func (t *SSHExecuteTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"command": map[string]interface{}{
"type": "string",
"description": "要执行的 shell 命令,例如 df -h、systemctl status nginx、tail -n 100 /var/log/messages",
},
"timeout": map[string]interface{}{
"type": "integer",
"description": "命令超时秒数(默认 60)",
},
},
"required": []string{"command"},
}
}
// RiskLevel 静态风险等级(高危) // Logan.Li
func (t *SSHExecuteTool) RiskLevel() RiskLevel { return RiskHigh }
// RiskLevelForArgs 依据命令动态评估风险 // Logan.Li
func (t *SSHExecuteTool) RiskLevelForArgs(args map[string]interface{}) RiskLevel {
return AssessBashRisk(StringParam(args, "command"))
}
// Execute 执行远程命令 // Logan.Li
func (t *SSHExecuteTool) Execute(ctx context.Context, args map[string]interface{}) (*Result, error) {
command := strings.TrimSpace(StringParam(args, "command"))
if command == "" {
return &Result{Success: false, Error: "参数 command 不能为空"}, nil
}
timeout := t.timeout
if sec := IntParam(args, "timeout"); sec > 0 {
timeout = time.Duration(sec) * time.Second
}
res, err := t.client.Exec(command, timeout)
if err != nil {
return &Result{Success: false, Error: fmt.Sprintf("SSH 连接失败: %v", err)}, nil
}
output := res.Output
if !strings.HasSuffix(output, "\n") && output != "" {
output += "\n"
}
output += fmt.Sprintf("[exit_code=%d]", res.ExitCode)
return &Result{
Success: res.Success,
Output: output,
Error: res.Error,
}, nil
}