mirror of
https://github.com/ollama/ollama.git
synced 2026-04-26 10:45:57 +02:00
update app - as of 5fba33e
This commit is contained in:
137
app/tools/bash.go
Normal file
137
app/tools/bash.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BashCommand executes non-destructive bash commands
|
||||
type BashCommand struct{}
|
||||
|
||||
func (b *BashCommand) Name() string {
|
||||
return "bash_command"
|
||||
}
|
||||
|
||||
func (b *BashCommand) Description() string {
|
||||
return "Execute non-destructive bash commands safely"
|
||||
}
|
||||
|
||||
func (b *BashCommand) Prompt() string {
|
||||
return `For bash commands:
|
||||
1. Only use safe, non-destructive commands like: ls, pwd, echo, cat, grep, ps, df, du, find, which, whoami, date, uptime, uname, wc, head, tail, sort, uniq
|
||||
2. For searching files and content:
|
||||
- Use grep -r "keyword" . to recursively search for keywords in files
|
||||
- Use find . -name "*keyword*" to search for files by name
|
||||
- Use find . -type f -exec grep "keyword" {} \; to search file contents
|
||||
3. Never use dangerous flags like --delete, --remove, -rf, -fr, --modify, --write, --exec
|
||||
4. Commands will timeout after 30 seconds by default
|
||||
5. Always check command output for errors and handle them appropriately
|
||||
6. Before running any commands:
|
||||
- Use ls to understand directory structure
|
||||
- Use cat/head/tail to inspect file contents
|
||||
- Plan your search strategy based on the context`
|
||||
}
|
||||
|
||||
func (b *BashCommand) Schema() map[string]any {
|
||||
schemaBytes := []byte(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string",
|
||||
"description": "The bash command to execute"
|
||||
},
|
||||
"timeout_seconds": {
|
||||
"type": "integer",
|
||||
"description": "Maximum execution time in seconds (default: 30)",
|
||||
"default": 30
|
||||
}
|
||||
},
|
||||
"required": ["command"]
|
||||
}`)
|
||||
var schema map[string]any
|
||||
if err := json.Unmarshal(schemaBytes, &schema); err != nil {
|
||||
return nil
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func (b *BashCommand) Execute(ctx context.Context, args map[string]any) (any, error) {
|
||||
// Extract command
|
||||
cmd, ok := args["command"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("command parameter is required and must be a string")
|
||||
}
|
||||
|
||||
// Get optional timeout
|
||||
timeoutSeconds := 30
|
||||
if t, ok := args["timeout_seconds"].(float64); ok {
|
||||
timeoutSeconds = int(t)
|
||||
}
|
||||
|
||||
// List of allowed commands (exact matches or prefixes)
|
||||
allowedCommands := []string{
|
||||
"ls", "pwd", "echo", "cat", "grep",
|
||||
"ps", "df", "du", "find", "which",
|
||||
"whoami", "date", "uptime", "uname",
|
||||
"wc", "head", "tail", "sort", "uniq",
|
||||
}
|
||||
|
||||
// Split the command to get the base command
|
||||
cmdParts := strings.Fields(cmd)
|
||||
if len(cmdParts) == 0 {
|
||||
return nil, fmt.Errorf("empty command")
|
||||
}
|
||||
baseCmd := cmdParts[0]
|
||||
|
||||
// Check if the command is allowed
|
||||
allowed := false
|
||||
for _, allowedCmd := range allowedCommands {
|
||||
if baseCmd == allowedCmd {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
return nil, fmt.Errorf("command not in allowed list: %s", baseCmd)
|
||||
}
|
||||
|
||||
// Additional safety checks for arguments
|
||||
dangerousFlags := []string{
|
||||
"--delete", "--remove", "-rf", "-fr",
|
||||
"--modify", "--write", "--exec",
|
||||
}
|
||||
|
||||
cmdLower := strings.ToLower(cmd)
|
||||
for _, flag := range dangerousFlags {
|
||||
if strings.Contains(cmdLower, flag) {
|
||||
return nil, fmt.Errorf("command contains dangerous flag: %s", flag)
|
||||
}
|
||||
}
|
||||
|
||||
// Create command with timeout
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute command
|
||||
execCmd := exec.CommandContext(ctx, "bash", "-c", cmd)
|
||||
output, err := execCmd.CombinedOutput()
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return nil, fmt.Errorf("command timed out after %d seconds", timeoutSeconds)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("command execution failed: %w", err)
|
||||
}
|
||||
|
||||
// Return result directly as a map
|
||||
return map[string]any{
|
||||
"command": cmd,
|
||||
"output": string(output),
|
||||
"success": true,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user