Initial commit: Cyrene Plugins SDK + Plugin Manager
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>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
|
||||
)
|
||||
|
||||
type DatetimePlugin struct{ sdk.BasePlugin }
|
||||
|
||||
func (p *DatetimePlugin) Metadata() sdk.PluginMetadata {
|
||||
return sdk.PluginMetadata{
|
||||
Name: "datetime", DisplayName: "Date & Time", Version: "1.0.0",
|
||||
Description: "Date/time utilities: now, format, arithmetic, diff, timezone list",
|
||||
Category: "utility", Author: sdk.PluginAuthor{Name: "Cyrene Team"},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *DatetimePlugin) Tools() []sdk.Tool { return []sdk.Tool{&DatetimeTool{}} }
|
||||
|
||||
type DatetimeTool struct{ sdk.BaseTool }
|
||||
|
||||
func (t *DatetimeTool) Definition() sdk.ToolDefinition {
|
||||
return sdk.ToolDefinition{
|
||||
ID: "datetime", Name: "datetime", DisplayName: "Date & Time",
|
||||
Description: "Date/time utility. Get current time, format dates, date arithmetic, date diff, list timezones.",
|
||||
Category: "utility", Complexity: sdk.ComplexitySimple,
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"action": map[string]interface{}{"type": "string", "enum": []string{"now", "format", "add", "diff", "timezone_list"}},
|
||||
"format": map[string]interface{}{"type": "string"},
|
||||
"timezone": map[string]interface{}{"type": "string"},
|
||||
"date": map[string]interface{}{"type": "string"},
|
||||
"duration": map[string]interface{}{"type": "string"},
|
||||
"date2": map[string]interface{}{"type": "string"},
|
||||
},
|
||||
"required": []string{"action"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *DatetimeTool) Validate(args map[string]interface{}) error {
|
||||
if _, ok := args["action"]; !ok {
|
||||
return fmt.Errorf("missing required parameter: action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DatetimeTool) Execute(_ context.Context, args map[string]interface{}) (*sdk.ToolResult, error) {
|
||||
action, _ := args["action"].(string)
|
||||
tzStr, _ := args["timezone"].(string)
|
||||
loc, _ := parseLocation(tzStr)
|
||||
now := time.Now().In(loc)
|
||||
|
||||
switch action {
|
||||
case "now":
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: true,
|
||||
Output: fmt.Sprintf("Current time: %s (unix: %d, zone: %s)", now.Format(time.RFC3339), now.Unix(), loc.String())}, nil
|
||||
|
||||
case "format":
|
||||
dateStr, _ := args["date"].(string)
|
||||
format, _ := args["format"].(string)
|
||||
if format == "" {
|
||||
format = time.RFC3339
|
||||
}
|
||||
parsed, err := parseDate(dateStr, loc)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: true,
|
||||
Output: fmt.Sprintf("Formatted: %s", parsed.Format(format))}, nil
|
||||
|
||||
case "add":
|
||||
dateStr, _ := args["date"].(string)
|
||||
durStr, _ := args["duration"].(string)
|
||||
base := now
|
||||
if dateStr != "" {
|
||||
var err error
|
||||
base, err = parseDate(dateStr, loc)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
}
|
||||
result, err := addDuration(base, durStr)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: true,
|
||||
Output: fmt.Sprintf("%s + %s = %s", base.Format(time.RFC3339), durStr, result.Format(time.RFC3339))}, nil
|
||||
|
||||
case "diff":
|
||||
d1, _ := args["date"].(string)
|
||||
d2, _ := args["date2"].(string)
|
||||
t1, err := parseDate(d1, loc)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
t2, err := parseDate(d2, loc)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
diff := t2.Sub(t1)
|
||||
if diff < 0 {
|
||||
diff = -diff
|
||||
}
|
||||
days := int(diff.Hours()) / 24
|
||||
hours := int(diff.Hours()) % 24
|
||||
minutes := int(diff.Minutes()) % 60
|
||||
seconds := int(diff.Seconds()) % 60
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: true,
|
||||
Output: fmt.Sprintf("Difference: %d days, %d hours, %d minutes, %d seconds", days, hours, minutes, seconds)}, nil
|
||||
|
||||
case "timezone_list":
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: true,
|
||||
Output: "Common timezones: UTC, Asia/Shanghai, Asia/Tokyo, Asia/Seoul, Asia/Singapore, Asia/Kolkata, Asia/Dubai, Europe/London, Europe/Paris, Europe/Moscow, America/New_York, America/Chicago, America/Los_Angeles, America/Sao_Paulo, Australia/Sydney, Pacific/Auckland, Africa/Cairo, Africa/Lagos"}, nil
|
||||
|
||||
default:
|
||||
return &sdk.ToolResult{ToolName: "datetime", Success: false,
|
||||
Error: fmt.Sprintf("unknown action: %s", action)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func parseLocation(tz string) (*time.Location, error) {
|
||||
if tz == "" {
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
if err != nil {
|
||||
return time.UTC, nil
|
||||
}
|
||||
return loc, nil
|
||||
}
|
||||
return time.LoadLocation(tz)
|
||||
}
|
||||
|
||||
func parseDate(s string, loc *time.Location) (time.Time, error) {
|
||||
formats := []string{time.RFC3339, "2006-01-02T15:04:05", "2006-01-02 15:04:05", "2006-01-02", "2006/01/02"}
|
||||
for _, f := range formats {
|
||||
if t, err := time.ParseInLocation(f, s, loc); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("cannot parse date: %s", s)
|
||||
}
|
||||
|
||||
func addDuration(t time.Time, durStr string) (time.Time, error) {
|
||||
durStr = strings.TrimSpace(durStr)
|
||||
if durStr == "" {
|
||||
return t, nil
|
||||
}
|
||||
// Handle months and years
|
||||
if strings.Contains(durStr, "M") || strings.Contains(durStr, "y") {
|
||||
months := 0
|
||||
years := 0
|
||||
if strings.Contains(durStr, "y") {
|
||||
fmt.Sscanf(durStr, "%dy", &years)
|
||||
}
|
||||
if strings.Contains(durStr, "M") {
|
||||
fmt.Sscanf(durStr, "%dM", &months)
|
||||
}
|
||||
return t.AddDate(years, months, 0), nil
|
||||
}
|
||||
d, err := time.ParseDuration(durStr)
|
||||
if err != nil {
|
||||
return t, fmt.Errorf("invalid duration: %s", durStr)
|
||||
}
|
||||
return t.Add(d), nil
|
||||
}
|
||||
Reference in New Issue
Block a user