execute: add perl support (shebang detection, @ARGV injection, pipe support)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-04-14 13:07:03 +02:00
parent e5e94e57fe
commit 3f0a77619b
3 changed files with 20 additions and 5 deletions

View File

@ -53,6 +53,8 @@ func BuildPipeline(steps []PipeStep) (string, bool, error) {
switch language {
case "python3":
parts = append(parts, fmt.Sprintf("( python3 -c $'%s' )", ansiCEscape(code)))
case "perl":
parts = append(parts, fmt.Sprintf("( perl -e $'%s' )", ansiCEscape(code)))
default:
parts = append(parts, fmt.Sprintf("(\n%s\n)", code))
}

View File

@ -82,7 +82,7 @@ Examples:
Usage:
- Scripts located in: ` + ToolsPath() + `
- Supported languages: bash, python3 (detected from shebang)
- Supported languages: bash, python3, perl (detected from shebang)
- Use for named scripts, not inline shell commands
- Default timeout: 30 seconds
@ -249,8 +249,10 @@ func (e *Server) Execute(ctx context.Context, code, language string, timeout int
interpreter = []string{"bash", "-c", code}
case "python3", "python":
interpreter = []string{"python3", "-c", code}
case "perl":
interpreter = []string{"perl", "-e", code}
default:
return "", fmt.Errorf("unsupported language: %s (supported: bash, python3)", language)
return "", fmt.Errorf("unsupported language: %s (supported: bash, python3, perl)", language)
}
wrapped := sandbox.WrapCommand(cfg, interpreter, workDir)
cmd = exec.CommandContext(ctx, wrapped[0], wrapped[1:]...)

View File

@ -28,12 +28,17 @@ func ToolsPath() string {
}
// detectLanguage infers the script language from the shebang line.
// Returns "python3" for Python scripts, "bash" for everything else.
// Returns "python3", "perl", or "bash".
func detectLanguage(code string) string {
line, _, _ := strings.Cut(code, "\n")
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#!") && strings.Contains(line, "python") {
return "python3"
if strings.HasPrefix(line, "#!") {
if strings.Contains(line, "python") {
return "python3"
}
if strings.Contains(line, "perl") {
return "perl"
}
}
return "bash"
}
@ -47,6 +52,12 @@ func injectArgs(language, name string, args []string, code string) string {
quoted[i] = fmt.Sprintf("%q", a)
}
return fmt.Sprintf("import sys\nsys.argv = [%q, %s]\n%s", name, strings.Join(quoted, ", "), code)
case "perl":
quoted := make([]string, len(args))
for i, a := range args {
quoted[i] = "'" + strings.ReplaceAll(a, "'", "\\'") + "'"
}
return fmt.Sprintf("@ARGV = (%s);\n%s", strings.Join(quoted, ", "), code)
default: // bash
escaped := make([]string, len(args))
for i, a := range args {