179 lines
4.3 KiB
Go
179 lines
4.3 KiB
Go
package actions
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/mudler/LocalAGI/core/types"
|
|
"github.com/mudler/LocalAGI/pkg/config"
|
|
"github.com/sashabaranov/go-openai/jsonschema"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
func NewShell(config map[string]string) *ShellAction {
|
|
return &ShellAction{
|
|
privateKey: config["privateKey"],
|
|
user: config["user"],
|
|
host: config["host"],
|
|
customName: config["customName"],
|
|
customDescription: config["customDescription"],
|
|
}
|
|
}
|
|
|
|
type ShellAction struct {
|
|
privateKey string
|
|
user, host string
|
|
customName string
|
|
customDescription string
|
|
}
|
|
|
|
func (a *ShellAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) {
|
|
result := struct {
|
|
Command string `json:"command"`
|
|
Host string `json:"host"`
|
|
User string `json:"user"`
|
|
}{}
|
|
err := params.Unmarshal(&result)
|
|
if err != nil {
|
|
fmt.Printf("error: %v", err)
|
|
|
|
return types.ActionResult{}, err
|
|
}
|
|
|
|
if a.host != "" && a.user != "" {
|
|
result.Host = a.host
|
|
result.User = a.user
|
|
}
|
|
|
|
output, err := sshCommand(a.privateKey, result.Command, result.User, result.Host)
|
|
if err != nil {
|
|
return types.ActionResult{}, err
|
|
}
|
|
|
|
return types.ActionResult{Result: output}, nil
|
|
}
|
|
|
|
func (a *ShellAction) Definition() types.ActionDefinition {
|
|
name := "shell"
|
|
description := "Run a shell command on a remote server."
|
|
if a.customName != "" {
|
|
name = a.customName
|
|
}
|
|
if a.customDescription != "" {
|
|
description = a.customDescription
|
|
}
|
|
if a.host != "" && a.user != "" {
|
|
return types.ActionDefinition{
|
|
Name: types.ActionDefinitionName(name),
|
|
Description: description,
|
|
Properties: map[string]jsonschema.Definition{
|
|
"command": {
|
|
Type: jsonschema.String,
|
|
Description: "The command to run on the remote server.",
|
|
},
|
|
},
|
|
Required: []string{"command"},
|
|
}
|
|
}
|
|
return types.ActionDefinition{
|
|
Name: types.ActionDefinitionName(name),
|
|
Description: description,
|
|
Properties: map[string]jsonschema.Definition{
|
|
"command": {
|
|
Type: jsonschema.String,
|
|
Description: "The command to run on the remote server.",
|
|
},
|
|
"host": {
|
|
Type: jsonschema.String,
|
|
Description: "The host of the remote server. e.g. ip:port",
|
|
},
|
|
"user": {
|
|
Type: jsonschema.String,
|
|
Description: "The user to connect to the remote server.",
|
|
},
|
|
},
|
|
Required: []string{"command", "host", "user"},
|
|
}
|
|
}
|
|
|
|
// ShellConfigMeta returns the metadata for Shell action configuration fields
|
|
func ShellConfigMeta() []config.Field {
|
|
return []config.Field{
|
|
{
|
|
Name: "privateKey",
|
|
Label: "Private Key",
|
|
Type: config.FieldTypeTextarea,
|
|
Required: true,
|
|
HelpText: "SSH private key for connecting to remote servers",
|
|
},
|
|
{
|
|
Name: "user",
|
|
Label: "Default User",
|
|
Type: config.FieldTypeText,
|
|
HelpText: "Default SSH user for connecting to remote servers",
|
|
},
|
|
{
|
|
Name: "host",
|
|
Label: "Default Host",
|
|
Type: config.FieldTypeText,
|
|
HelpText: "Default host for SSH connections (e.g., hostname:port)",
|
|
},
|
|
{
|
|
Name: "customName",
|
|
Label: "Custom Action Name",
|
|
Type: config.FieldTypeText,
|
|
HelpText: "Custom name for this action",
|
|
},
|
|
{
|
|
Name: "customDescription",
|
|
Label: "Custom Description",
|
|
Type: config.FieldTypeTextarea,
|
|
HelpText: "Custom description for this action",
|
|
},
|
|
}
|
|
}
|
|
|
|
func sshCommand(privateKey, command, user, host string) (string, error) {
|
|
// Create signer from private key string
|
|
key, err := ssh.ParsePrivateKey([]byte(privateKey))
|
|
if err != nil {
|
|
log.Fatalf("failed to parse private key: %v", err)
|
|
}
|
|
|
|
// SSH client configuration
|
|
config := &ssh.ClientConfig{
|
|
User: user,
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.PublicKeys(key),
|
|
},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
}
|
|
|
|
// Connect to SSH server
|
|
client, err := ssh.Dial("tcp", host, config)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to dial: %v", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
// Open a new session
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create session: %v", err)
|
|
}
|
|
defer session.Close()
|
|
|
|
// Run a command
|
|
output, err := session.CombinedOutput(command)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to run: %v", err)
|
|
}
|
|
|
|
return string(output), nil
|
|
}
|
|
|
|
func (a *ShellAction) Plannable() bool {
|
|
return true
|
|
}
|