5c807d76a0
Extracted from Cyrene main repo (backend/pkg/plugins + backend/plugin-manager). Contains SDK interfaces (Plugin/Tool/HostAPI), 13 built-in plugins, ToolRegistry with call log ring buffer, and Plugin Manager REST API service. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
176 lines
4.9 KiB
Go
176 lines
4.9 KiB
Go
package random
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
mathrand "math/rand"
|
|
"strings"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
|
|
)
|
|
|
|
type RandomPlugin struct{ sdk.BasePlugin }
|
|
|
|
func (p *RandomPlugin) Metadata() sdk.PluginMetadata {
|
|
return sdk.PluginMetadata{
|
|
Name: "random", DisplayName: "Random Generator", Version: "1.0.0",
|
|
Description: "Random generation: numbers, UUIDs, secure passwords, pick/shuffle",
|
|
Category: "utility", Author: sdk.PluginAuthor{Name: "Cyrene Team"},
|
|
}
|
|
}
|
|
|
|
func (p *RandomPlugin) Tools() []sdk.Tool { return []sdk.Tool{&RandomTool{}} }
|
|
|
|
type RandomTool struct{ sdk.BaseTool }
|
|
|
|
func (t *RandomTool) Definition() sdk.ToolDefinition {
|
|
return sdk.ToolDefinition{
|
|
ID: "random", Name: "random", DisplayName: "Random Generator",
|
|
Description: "Random generation. Random numbers, UUID v4, secure passwords, pick from list, shuffle list.",
|
|
Category: "utility", Complexity: sdk.ComplexitySimple,
|
|
Parameters: map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"action": map[string]interface{}{"type": "string", "enum": []string{"number", "uuid", "password", "pick", "shuffle"}},
|
|
"min": map[string]interface{}{"type": "number"},
|
|
"max": map[string]interface{}{"type": "number"},
|
|
"length": map[string]interface{}{"type": "number"},
|
|
"items": map[string]interface{}{"type": "array", "items": map[string]interface{}{"type": "string"}},
|
|
"count": map[string]interface{}{"type": "number"},
|
|
},
|
|
"required": []string{"action"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (t *RandomTool) Validate(args map[string]interface{}) error {
|
|
if _, ok := args["action"]; !ok {
|
|
return fmt.Errorf("missing required parameter: action")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *RandomTool) Execute(_ context.Context, args map[string]interface{}) (*sdk.ToolResult, error) {
|
|
action, _ := args["action"].(string)
|
|
|
|
switch action {
|
|
case "number":
|
|
min := getIntArg(args, "min", 0)
|
|
max := getIntArg(args, "max", 100)
|
|
n, err := rand.Int(rand.Reader, big.NewInt(int64(max-min+1)))
|
|
if err != nil {
|
|
return &sdk.ToolResult{ToolName: "random", Success: false, Error: err.Error()}, nil
|
|
}
|
|
return &sdk.ToolResult{ToolName: "random", Success: true,
|
|
Output: fmt.Sprintf("%d", int(n.Int64())+min)}, nil
|
|
|
|
case "uuid":
|
|
uuid := make([]byte, 16)
|
|
rand.Read(uuid)
|
|
uuid[6] = (uuid[6] & 0x0f) | 0x40
|
|
uuid[8] = (uuid[8] & 0x3f) | 0x80
|
|
return &sdk.ToolResult{ToolName: "random", Success: true,
|
|
Output: fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])}, nil
|
|
|
|
case "password":
|
|
length := getIntArg(args, "length", 16)
|
|
if length < 4 {
|
|
length = 4
|
|
}
|
|
if length > 128 {
|
|
length = 128
|
|
}
|
|
upper := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
lower := "abcdefghijklmnopqrstuvwxyz"
|
|
digits := "0123456789"
|
|
symbols := "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
|
all := upper + lower + digits + symbols
|
|
bytes := make([]byte, length)
|
|
for i := range bytes {
|
|
idx, _ := rand.Int(rand.Reader, big.NewInt(int64(len(all))))
|
|
bytes[i] = all[idx.Int64()]
|
|
}
|
|
return &sdk.ToolResult{ToolName: "random", Success: true, Output: string(bytes)}, nil
|
|
|
|
case "pick":
|
|
items := getStringSliceArg(args, "items")
|
|
if len(items) == 0 {
|
|
return &sdk.ToolResult{ToolName: "random", Success: false, Error: "items list is empty"}, nil
|
|
}
|
|
count := getIntArg(args, "count", 1)
|
|
if count > len(items) {
|
|
count = len(items)
|
|
}
|
|
indices := shuffledIndices(len(items))
|
|
picked := make([]string, count)
|
|
for i := 0; i < count; i++ {
|
|
picked[i] = items[indices[i]]
|
|
}
|
|
return &sdk.ToolResult{ToolName: "random", Success: true, Output: strings.Join(picked, ", ")}, nil
|
|
|
|
case "shuffle":
|
|
items := getStringSliceArg(args, "items")
|
|
indices := shuffledIndices(len(items))
|
|
shuffled := make([]string, len(items))
|
|
for i, idx := range indices {
|
|
shuffled[i] = items[idx]
|
|
}
|
|
return &sdk.ToolResult{ToolName: "random", Success: true, Output: strings.Join(shuffled, ", ")}, nil
|
|
}
|
|
return &sdk.ToolResult{ToolName: "random", Success: false, Error: "unknown action: " + action}, nil
|
|
}
|
|
|
|
func getIntArg(args map[string]interface{}, key string, defaultVal int) int {
|
|
v, ok := args[key]
|
|
if !ok {
|
|
return defaultVal
|
|
}
|
|
switch n := v.(type) {
|
|
case float64:
|
|
return int(n)
|
|
case int:
|
|
return n
|
|
case int64:
|
|
return int(n)
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func getStringSliceArg(args map[string]interface{}, key string) []string {
|
|
v, ok := args[key]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
switch s := v.(type) {
|
|
case []string:
|
|
return s
|
|
case []interface{}:
|
|
result := make([]string, len(s))
|
|
for i, item := range s {
|
|
result[i] = fmt.Sprint(item)
|
|
}
|
|
return result
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func shuffledIndices(n int) []int {
|
|
indices := make([]int, n)
|
|
for i := range indices {
|
|
indices[i] = i
|
|
}
|
|
for i := n - 1; i > 0; i-- {
|
|
jBig, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
|
|
if err != nil {
|
|
j := mathrand.Intn(i + 1)
|
|
indices[i], indices[j] = indices[j], indices[i]
|
|
continue
|
|
}
|
|
j := int(jBig.Int64())
|
|
indices[i], indices[j] = indices[j], indices[i]
|
|
}
|
|
return indices
|
|
}
|