mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-29 11:19:42 +02:00
Complete rewrite and re-architecture Osmedeus Engine in v5
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/j3ssie/osmedeus/v5/internal/config"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/database"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/functions"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/template"
|
||||
"github.com/j3ssie/osmedeus/v5/internal/terminal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
evalScript string
|
||||
evalTarget string
|
||||
evalParams []string
|
||||
evalStdin bool
|
||||
evalFunctionName string
|
||||
funcSearchFilter string
|
||||
funcColumnWidth int
|
||||
funcShowExample bool
|
||||
)
|
||||
|
||||
// functionCmd is the parent command for function operations
|
||||
var functionCmd = &cobra.Command{
|
||||
Use: "function",
|
||||
Aliases: []string{"func"},
|
||||
Short: "Execute and test utility functions",
|
||||
Long: UsageFunction(),
|
||||
}
|
||||
|
||||
// functionEvalCmd evaluates a script with template rendering and function execution
|
||||
var functionEvalCmd = &cobra.Command{
|
||||
Use: "eval",
|
||||
Aliases: []string{"e"},
|
||||
Short: "Evaluate a script with template rendering and function execution",
|
||||
Long: UsageFunctionEval(),
|
||||
RunE: runFunctionEval,
|
||||
}
|
||||
|
||||
// functionListCmd lists all available functions
|
||||
var functionListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List all available utility functions",
|
||||
RunE: runFunctionList,
|
||||
}
|
||||
|
||||
func init() {
|
||||
functionEvalCmd.Flags().StringVarP(&evalScript, "eval", "e", "", "script to evaluate")
|
||||
functionEvalCmd.Flags().StringVarP(&evalTarget, "target", "t", "", "target value for {{target}} variable")
|
||||
functionEvalCmd.Flags().StringArrayVar(&evalParams, "params", nil, "additional parameters (key=value format)")
|
||||
functionEvalCmd.Flags().BoolVar(&evalStdin, "stdin", false, "read script from stdin")
|
||||
functionEvalCmd.Flags().StringVarP(&evalFunctionName, "function", "f", "", "function name to call (remaining args become function arguments)")
|
||||
|
||||
functionListCmd.Flags().StringVarP(&funcSearchFilter, "search", "s", "", "filter functions by name or description")
|
||||
functionListCmd.Flags().IntVar(&funcColumnWidth, "width", 0, "max column width (wraps lines instead of truncating)")
|
||||
functionListCmd.Flags().BoolVar(&funcShowExample, "example", false, "show example usage below each function description")
|
||||
|
||||
functionCmd.AddCommand(functionEvalCmd)
|
||||
functionCmd.AddCommand(functionListCmd)
|
||||
}
|
||||
|
||||
func runFunctionEval(cmd *cobra.Command, args []string) error {
|
||||
printer := terminal.NewPrinter()
|
||||
|
||||
// Connect to database for db_* functions (skip if --disable-db is set)
|
||||
if !disableDB {
|
||||
cfg := config.Get()
|
||||
if cfg != nil {
|
||||
// Try to connect to database - don't fail if DB not available
|
||||
if _, dbErr := database.Connect(cfg); dbErr != nil {
|
||||
if verbose {
|
||||
printer.Info("Database connection warning: %s", dbErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine script source: -f flag > positional arg > -e flag > stdin
|
||||
var script string
|
||||
|
||||
// Handle -f/--function flag: build script from function name + positional args
|
||||
if evalFunctionName != "" {
|
||||
var quotedArgs []string
|
||||
for _, arg := range args {
|
||||
quotedArgs = append(quotedArgs, fmt.Sprintf("%q", arg))
|
||||
}
|
||||
script = fmt.Sprintf("%s(%s)", evalFunctionName, strings.Join(quotedArgs, ", "))
|
||||
} else if len(args) > 0 && args[0] != "-" {
|
||||
if len(args) > 1 {
|
||||
// Multiple args: treat first as function name, rest as arguments
|
||||
// e.g., "func_name arg1 arg2" → "func_name("arg1", "arg2")"
|
||||
var quotedArgs []string
|
||||
for _, arg := range args[1:] {
|
||||
quotedArgs = append(quotedArgs, fmt.Sprintf("%q", arg))
|
||||
}
|
||||
script = fmt.Sprintf("%s(%s)", args[0], strings.Join(quotedArgs, ", "))
|
||||
} else {
|
||||
// Single arg: use as-is (could be full expression or function name with no args)
|
||||
script = args[0]
|
||||
}
|
||||
} else if evalScript != "" {
|
||||
// Script provided via -e flag
|
||||
script = evalScript
|
||||
} else if evalStdin || (len(args) > 0 && args[0] == "-") {
|
||||
// Read script from stdin
|
||||
data, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
printer.Error("Failed to read from stdin: %s", err)
|
||||
return fmt.Errorf("failed to read from stdin: %w", err)
|
||||
}
|
||||
script = strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
if script == "" {
|
||||
return fmt.Errorf("no script provided: use positional argument, -e flag, or --stdin")
|
||||
}
|
||||
|
||||
// Use the resolved script
|
||||
evalScript = script
|
||||
|
||||
// 1. Build context with target and params
|
||||
ctx := make(map[string]interface{})
|
||||
if evalTarget != "" {
|
||||
ctx["target"] = evalTarget
|
||||
}
|
||||
|
||||
for _, p := range evalParams {
|
||||
parts := strings.SplitN(p, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
ctx[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Render template variables ({{target}}, etc.)
|
||||
templateEngine := template.NewEngine()
|
||||
renderedScript, err := templateEngine.Render(evalScript, ctx)
|
||||
if err != nil {
|
||||
printer.Error("Template rendering failed: %s", err)
|
||||
return fmt.Errorf("template rendering failed: %w", err)
|
||||
}
|
||||
|
||||
// Show rendered script if different from original (verbose mode)
|
||||
if verbose && renderedScript != evalScript {
|
||||
printer.Info("Rendered script: %s", renderedScript)
|
||||
}
|
||||
|
||||
// 3. Execute as JavaScript using Otto runtime
|
||||
registry := functions.NewRegistry()
|
||||
result, err := registry.Execute(renderedScript, ctx)
|
||||
if err != nil {
|
||||
printer.Error("Execution failed: %s", err)
|
||||
return fmt.Errorf("execution failed: %w", err)
|
||||
}
|
||||
|
||||
// 4. Print result
|
||||
if result != nil {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runFunctionList(cmd *cobra.Command, args []string) error {
|
||||
printer := terminal.NewPrinter()
|
||||
|
||||
printer.Section("Available Utility Functions")
|
||||
fmt.Println()
|
||||
|
||||
// Use positional arg as search if --search not provided
|
||||
if funcSearchFilter == "" && len(args) > 0 {
|
||||
funcSearchFilter = args[0]
|
||||
}
|
||||
|
||||
// Get function registry and category order
|
||||
registry := functions.FunctionRegistry()
|
||||
categories := functions.CategoryOrder()
|
||||
|
||||
// Build rows for all functions
|
||||
var rows [][]string
|
||||
searchLower := strings.ToLower(funcSearchFilter)
|
||||
|
||||
for _, cat := range categories {
|
||||
if funcs, ok := registry[cat.Key]; ok {
|
||||
for _, fn := range funcs {
|
||||
// Apply search filter if specified (matches name, description, or category)
|
||||
if funcSearchFilter != "" {
|
||||
nameLower := strings.ToLower(fn.Name)
|
||||
descLower := strings.ToLower(fn.Description)
|
||||
catLower := strings.ToLower(cat.Title)
|
||||
if !strings.Contains(nameLower, searchLower) &&
|
||||
!strings.Contains(descLower, searchLower) &&
|
||||
!strings.Contains(catLower, searchLower) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Build description, optionally with example
|
||||
desc := fn.Description
|
||||
if funcShowExample && fn.Example != "" {
|
||||
desc = fn.Description + "\n" + terminal.Gray("e.g. "+fn.Example)
|
||||
}
|
||||
rows = append(rows, []string{
|
||||
terminal.Yellow(cat.ShortTitle),
|
||||
terminal.Cyan(fn.Signature),
|
||||
desc,
|
||||
terminal.Magenta(fn.ReturnType),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
if funcSearchFilter != "" {
|
||||
printer.Info("No functions matching '%s'", funcSearchFilter)
|
||||
} else {
|
||||
printer.Info("No functions available")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if funcSearchFilter != "" {
|
||||
printer.Info("Found %d function(s) matching '%s':", len(rows), funcSearchFilter)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
headers := []string{"Category", "Function", "Description", "Returns"}
|
||||
if funcColumnWidth > 0 {
|
||||
printMarkdownTableWithWidth(headers, rows, funcColumnWidth)
|
||||
} else {
|
||||
printMarkdownTable(headers, rows)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user