chore(ui): Move some field definitions server side
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnectorConfig struct {
|
type ConnectorConfig struct {
|
||||||
@@ -35,7 +36,7 @@ type AgentConfig struct {
|
|||||||
MCPServers []agent.MCPServer `json:"mcp_servers" form:"mcp_servers"`
|
MCPServers []agent.MCPServer `json:"mcp_servers" form:"mcp_servers"`
|
||||||
|
|
||||||
Description string `json:"description" form:"description"`
|
Description string `json:"description" form:"description"`
|
||||||
// This is what needs to be part of ActionsConfig
|
|
||||||
Model string `json:"model" form:"model"`
|
Model string `json:"model" form:"model"`
|
||||||
MultimodalModel string `json:"multimodal_model" form:"multimodal_model"`
|
MultimodalModel string `json:"multimodal_model" form:"multimodal_model"`
|
||||||
APIURL string `json:"api_url" form:"api_url"`
|
APIURL string `json:"api_url" form:"api_url"`
|
||||||
@@ -61,6 +62,211 @@ type AgentConfig struct {
|
|||||||
SummaryLongTermMemory bool `json:"summary_long_term_memory" form:"summary_long_term_memory"`
|
SummaryLongTermMemory bool `json:"summary_long_term_memory" form:"summary_long_term_memory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentConfigMeta struct {
|
||||||
|
Fields []config.Field
|
||||||
|
Connectors []config.FieldGroup
|
||||||
|
Actions []config.FieldGroup
|
||||||
|
PromptBlocks []config.Field
|
||||||
|
MCPServers []config.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentConfigMeta(actionsConfig []config.FieldGroup, connectorsConfig []config.FieldGroup) AgentConfigMeta {
|
||||||
|
return AgentConfigMeta{
|
||||||
|
Fields: []config.Field{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Label: "Name",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Required: true,
|
||||||
|
Tags: config.Tags{Section: "BasicInfo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "description",
|
||||||
|
Label: "Description",
|
||||||
|
Type: "textarea",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "BasicInfo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "identity_guidance",
|
||||||
|
Label: "Identity Guidance",
|
||||||
|
Type: "textarea",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "BasicInfo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "random_identity",
|
||||||
|
Label: "Random Identity",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
Tags: config.Tags{Section: "BasicInfo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "hud",
|
||||||
|
Label: "HUD",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
Tags: config.Tags{Section: "BasicInfo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "model",
|
||||||
|
Label: "Model",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "multimodal_model",
|
||||||
|
Label: "Multimodal Model",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "api_url",
|
||||||
|
Label: "API URL",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "api_key",
|
||||||
|
Label: "API Key",
|
||||||
|
Type: "password",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "local_rag_url",
|
||||||
|
Label: "Local RAG URL",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "local_rag_api_key",
|
||||||
|
Label: "Local RAG API Key",
|
||||||
|
Type: "password",
|
||||||
|
DefaultValue: "",
|
||||||
|
Tags: config.Tags{Section: "ModelSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "enable_kb",
|
||||||
|
Label: "Enable Knowledge Base",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
Tags: config.Tags{Section: "MemorySettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "kb_results",
|
||||||
|
Label: "Knowledge Base Results",
|
||||||
|
Type: "number",
|
||||||
|
DefaultValue: 5,
|
||||||
|
Min: 1,
|
||||||
|
Step: 1,
|
||||||
|
Tags: config.Tags{Section: "MemorySettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "long_term_memory",
|
||||||
|
Label: "Long Term Memory",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
Tags: config.Tags{Section: "MemorySettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "summary_long_term_memory",
|
||||||
|
Label: "Summary Long Term Memory",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
Tags: config.Tags{Section: "MemorySettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "system_prompt",
|
||||||
|
Label: "System Prompt",
|
||||||
|
Type: "textarea",
|
||||||
|
DefaultValue: "",
|
||||||
|
HelpText: "Instructions that define the agent's behavior and capabilities",
|
||||||
|
Tags: config.Tags{Section: "PromptsGoals"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "permanent_goal",
|
||||||
|
Label: "Permanent Goal",
|
||||||
|
Type: "textarea",
|
||||||
|
DefaultValue: "",
|
||||||
|
HelpText: "Long-term objective for the agent to pursue",
|
||||||
|
Tags: config.Tags{Section: "PromptsGoals"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "standalone_job",
|
||||||
|
Label: "Standalone Job",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
HelpText: "Run as a standalone job without user interaction",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "initiate_conversations",
|
||||||
|
Label: "Initiate Conversations",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
HelpText: "Allow agent to start conversations on its own",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "enable_planning",
|
||||||
|
Label: "Enable Planning",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
HelpText: "Enable agent to create and execute plans",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "can_stop_itself",
|
||||||
|
Label: "Can Stop Itself",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
HelpText: "Allow agent to terminate its own execution",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "periodic_runs",
|
||||||
|
Label: "Periodic Runs",
|
||||||
|
Type: "text",
|
||||||
|
DefaultValue: "",
|
||||||
|
Placeholder: "10m",
|
||||||
|
HelpText: "Duration for scheduling periodic agent runs",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "enable_reasoning",
|
||||||
|
Label: "Enable Reasoning",
|
||||||
|
Type: "checkbox",
|
||||||
|
DefaultValue: false,
|
||||||
|
HelpText: "Enable agent to explain its reasoning process",
|
||||||
|
Tags: config.Tags{Section: "AdvancedSettings"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MCPServers: []config.Field{
|
||||||
|
{
|
||||||
|
Name: "url",
|
||||||
|
Label: "URL",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "API Key",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PromptBlocks: []config.Field{},
|
||||||
|
Connectors: connectorsConfig,
|
||||||
|
Actions: actionsConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Connector interface {
|
type Connector interface {
|
||||||
AgentResultCallback() func(state types.ActionState)
|
AgentResultCallback() func(state types.ActionState)
|
||||||
AgentReasoningCallback() func(state types.ActionCurrentState) bool
|
AgentReasoningCallback() func(state types.ActionCurrentState) bool
|
||||||
|
|||||||
42
pkg/config/meta.go
Normal file
42
pkg/config/meta.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type FieldType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldTypeNumber FieldType = "number"
|
||||||
|
FieldTypeText FieldType = "text"
|
||||||
|
FieldTypeTextarea FieldType = "textarea"
|
||||||
|
FieldTypeCheckbox FieldType = "checkbox"
|
||||||
|
FieldTypeSelect FieldType = "select"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tags struct {
|
||||||
|
Section string `json:"section,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldOption struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type FieldType `json:"type"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
DefaultValue any `json:"defaultValue"`
|
||||||
|
Placeholder string `json:"placeholder,omitempty"`
|
||||||
|
HelpText string `json:"helpText,omitempty"`
|
||||||
|
Required bool `json:"required,omitempty"`
|
||||||
|
Disabled bool `json:"disabled,omitempty"`
|
||||||
|
Options []FieldOption `json:"options,omitempty"`
|
||||||
|
Min float32 `json:"min,omitempty"`
|
||||||
|
Max float32 `json:"max,omitempty"`
|
||||||
|
Step float32 `json:"step,omitempty"`
|
||||||
|
Tags Tags `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldGroup struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Fields []Field `json:"fields"`
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/mudler/LocalAgent/core/action"
|
"github.com/mudler/LocalAgent/core/action"
|
||||||
"github.com/mudler/LocalAgent/core/state"
|
"github.com/mudler/LocalAgent/core/state"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/services/actions"
|
"github.com/mudler/LocalAgent/services/actions"
|
||||||
@@ -30,7 +31,7 @@ const (
|
|||||||
ActionWikipedia = "wikipedia"
|
ActionWikipedia = "wikipedia"
|
||||||
ActionBrowse = "browse"
|
ActionBrowse = "browse"
|
||||||
ActionTwitterPost = "twitter-post"
|
ActionTwitterPost = "twitter-post"
|
||||||
ActionSendMail = "send_mail"
|
ActionSendMail = "send-mail"
|
||||||
ActionGenerateImage = "generate_image"
|
ActionGenerateImage = "generate_image"
|
||||||
ActionCounter = "counter"
|
ActionCounter = "counter"
|
||||||
ActionCallAgents = "call_agents"
|
ActionCallAgents = "call_agents"
|
||||||
@@ -138,3 +139,108 @@ func Action(name string, config map[string]string, pool *state.AgentPool) (types
|
|||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ActionsConfigMeta() []config.FieldGroup {
|
||||||
|
return []config.FieldGroup{
|
||||||
|
{
|
||||||
|
Name: "search",
|
||||||
|
Label: "Search",
|
||||||
|
Fields: actions.SearchConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "generate_image",
|
||||||
|
Label: "Generate Image",
|
||||||
|
Fields: actions.GenImageConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-labeler",
|
||||||
|
Label: "GitHub Issue Labeler",
|
||||||
|
Fields: actions.GithubIssueLabelerConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-opener",
|
||||||
|
Label: "GitHub Issue Opener",
|
||||||
|
Fields: actions.GithubIssueOpenerConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-closer",
|
||||||
|
Label: "GitHub Issue Closer",
|
||||||
|
Fields: actions.GithubIssueCloserConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-commenter",
|
||||||
|
Label: "GitHub Issue Commenter",
|
||||||
|
Fields: actions.GithubIssueCommenterConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-reader",
|
||||||
|
Label: "GitHub Issue Reader",
|
||||||
|
Fields: actions.GithubIssueReaderConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issue-searcher",
|
||||||
|
Label: "GitHub Issue Search",
|
||||||
|
Fields: actions.GithubIssueSearchConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-repository-get-content",
|
||||||
|
Label: "GitHub Repository Get Content",
|
||||||
|
Fields: actions.GithubRepositoryGetContentConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-repository-create-or-update-content",
|
||||||
|
Label: "GitHub Repository Create/Update Content",
|
||||||
|
Fields: actions.GithubRepositoryCreateOrUpdateContentConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-readme",
|
||||||
|
Label: "GitHub Repository README",
|
||||||
|
Fields: actions.GithubRepositoryREADMEConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "twitter-post",
|
||||||
|
Label: "Twitter Post",
|
||||||
|
Fields: actions.TwitterPostConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "send-mail",
|
||||||
|
Label: "Send Mail",
|
||||||
|
Fields: actions.SendMailConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "shell-command",
|
||||||
|
Label: "Shell Command",
|
||||||
|
Fields: actions.ShellConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "custom",
|
||||||
|
Label: "Custom",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "scraper",
|
||||||
|
Label: "Scraper",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "wikipedia",
|
||||||
|
Label: "Wikipedia",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "browse",
|
||||||
|
Label: "Browse",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "counter",
|
||||||
|
Label: "Counter",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "call_agents",
|
||||||
|
Label: "Call Agents",
|
||||||
|
Fields: []config.Field{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
@@ -96,3 +97,32 @@ func (a *GenImageAction) Definition() types.ActionDefinition {
|
|||||||
func (a *GenImageAction) Plannable() bool {
|
func (a *GenImageAction) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenImageConfigMeta returns the metadata for GenImage action configuration fields
|
||||||
|
func GenImageConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "apiKey",
|
||||||
|
Label: "API Key",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "OpenAI API key for image generation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "apiURL",
|
||||||
|
Label: "API URL",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
DefaultValue: "https://api.openai.com/v1",
|
||||||
|
HelpText: "OpenAI API URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "model",
|
||||||
|
Label: "Model",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
DefaultValue: "dall-e-3",
|
||||||
|
HelpText: "Image generation model to use (e.g., dall-e-3)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,3 +119,36 @@ func (g *GithubIssuesCloser) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssuesCloser) Plannable() bool {
|
func (a *GithubIssuesCloser) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueCloserConfigMeta returns the metadata for GitHub Issue Closer action configuration fields
|
||||||
|
func GithubIssueCloserConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,3 +106,36 @@ func (g *GithubIssuesCommenter) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssuesCommenter) Plannable() bool {
|
func (a *GithubIssuesCommenter) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueCommenterConfigMeta returns the metadata for GitHub Issue Commenter action configuration fields
|
||||||
|
func GithubIssueCommenterConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
@@ -120,3 +121,43 @@ func (g *GithubIssuesLabeler) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssuesLabeler) Plannable() bool {
|
func (a *GithubIssuesLabeler) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueLabelerConfigMeta returns the metadata for GitHub Issue Labeler action configuration fields
|
||||||
|
func GithubIssueLabelerConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "availableLabels",
|
||||||
|
Label: "Available Labels",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Comma-separated list of available labels",
|
||||||
|
DefaultValue: "bug,enhancement",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,3 +112,36 @@ func (g *GithubIssuesOpener) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssuesOpener) Plannable() bool {
|
func (a *GithubIssuesOpener) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueOpenerConfigMeta returns the metadata for GitHub Issue Opener action configuration fields
|
||||||
|
func GithubIssueOpenerConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,3 +100,36 @@ func (g *GithubIssuesReader) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssuesReader) Plannable() bool {
|
func (a *GithubIssuesReader) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueReaderConfigMeta returns the metadata for GitHub Issue Reader action configuration fields
|
||||||
|
func GithubIssueReaderConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
@@ -108,3 +109,36 @@ func (g *GithubIssueSearch) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubIssueSearch) Plannable() bool {
|
func (a *GithubIssueSearch) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueSearchConfigMeta returns the metadata for GitHub Issue Search action configuration fields
|
||||||
|
func GithubIssueSearchConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -144,3 +145,36 @@ func (g *GithubRepositoryCreateOrUpdateContent) Definition() types.ActionDefinit
|
|||||||
func (a *GithubRepositoryCreateOrUpdateContent) Plannable() bool {
|
func (a *GithubRepositoryCreateOrUpdateContent) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubRepositoryCreateOrUpdateContentConfigMeta returns the metadata for GitHub Repository Create/Update Content action configuration fields
|
||||||
|
func GithubRepositoryCreateOrUpdateContentConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,3 +110,36 @@ func (g *GithubRepositoryGetContent) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubRepositoryGetContent) Plannable() bool {
|
func (a *GithubRepositoryGetContent) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubRepositoryGetContentConfigMeta returns the metadata for GitHub Repository Get Content action configuration fields
|
||||||
|
func GithubRepositoryGetContentConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub repository owner",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,3 +76,22 @@ func (g *GithubRepositoryREADME) Definition() types.ActionDefinition {
|
|||||||
func (a *GithubRepositoryREADME) Plannable() bool {
|
func (a *GithubRepositoryREADME) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubRepositoryREADMEConfigMeta returns the metadata for GitHub Repository README action configuration fields
|
||||||
|
func GithubRepositoryREADMEConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "GitHub API token with repository access",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customActionName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
"github.com/tmc/langchaingo/tools/duckduckgo"
|
"github.com/tmc/langchaingo/tools/duckduckgo"
|
||||||
"mvdan.cc/xurls/v2"
|
"mvdan.cc/xurls/v2"
|
||||||
@@ -89,3 +90,19 @@ func (a *SearchAction) Definition() types.ActionDefinition {
|
|||||||
func (a *SearchAction) Plannable() bool {
|
func (a *SearchAction) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchConfigMeta returns the metadata for Search action configuration fields
|
||||||
|
func SearchConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "results",
|
||||||
|
Label: "Number of Results",
|
||||||
|
Type: config.FieldTypeNumber,
|
||||||
|
DefaultValue: 1,
|
||||||
|
Min: 1,
|
||||||
|
Max: 10,
|
||||||
|
Step: 1,
|
||||||
|
HelpText: "Number of search results to return",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/smtp"
|
"net/smtp"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,3 +81,45 @@ func (a *SendMailAction) Definition() types.ActionDefinition {
|
|||||||
func (a *SendMailAction) Plannable() bool {
|
func (a *SendMailAction) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMailConfigMeta returns the metadata for SendMail action configuration fields
|
||||||
|
func SendMailConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "smtpHost",
|
||||||
|
Label: "SMTP Host",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "SMTP server host (e.g., smtp.gmail.com)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "smtpPort",
|
||||||
|
Label: "SMTP Port",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
DefaultValue: "587",
|
||||||
|
HelpText: "SMTP server port (e.g., 587)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "username",
|
||||||
|
Label: "SMTP Username",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "SMTP username/email address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "password",
|
||||||
|
Label: "SMTP Password",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "SMTP password or app password",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "email",
|
||||||
|
Label: "From Email",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "Sender email address",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
@@ -96,6 +97,43 @@ func (a *ShellAction) Definition() types.ActionDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShellConfigMeta returns the metadata for Shell action configuration fields
|
||||||
|
func ShellConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "privateKey",
|
||||||
|
Label: "Private Key",
|
||||||
|
Type: config.FieldTypeTextarea,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "SSH private key for connecting to remote servers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "user",
|
||||||
|
Label: "Default User",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Default SSH user for connecting to remote servers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "host",
|
||||||
|
Label: "Default Host",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Default host for SSH connections (e.g., hostname:port)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customName",
|
||||||
|
Label: "Custom Action Name",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Custom name for this action",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "customDescription",
|
||||||
|
Label: "Custom Description",
|
||||||
|
Type: config.FieldTypeTextarea,
|
||||||
|
HelpText: "Custom description for this action",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sshCommand(privateKey, command, user, host string) (string, error) {
|
func sshCommand(privateKey, command, user, host string) (string, error) {
|
||||||
// Create signer from private key string
|
// Create signer from private key string
|
||||||
key, err := ssh.ParsePrivateKey([]byte(privateKey))
|
key, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/services/connectors/twitter"
|
"github.com/mudler/LocalAgent/services/connectors/twitter"
|
||||||
"github.com/sashabaranov/go-openai/jsonschema"
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
@@ -62,3 +63,22 @@ func (a *PostTweetAction) Definition() types.ActionDefinition {
|
|||||||
func (a *PostTweetAction) Plannable() bool {
|
func (a *PostTweetAction) Plannable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TwitterPostConfigMeta returns the metadata for Twitter Post action configuration fields
|
||||||
|
func TwitterPostConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "Twitter API Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
HelpText: "Twitter API token for posting tweets",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "noCharacterLimit",
|
||||||
|
Label: "No Character Limit",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
HelpText: "If checked, tweets longer than the character limit will be split into multiple tweets",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/mudler/LocalAgent/services/connectors"
|
"github.com/mudler/LocalAgent/services/connectors"
|
||||||
|
|
||||||
@@ -69,3 +70,43 @@ func Connectors(a *state.AgentConfig) []state.Connector {
|
|||||||
}
|
}
|
||||||
return conns
|
return conns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConnectorsConfigMeta() []config.FieldGroup {
|
||||||
|
return []config.FieldGroup{
|
||||||
|
{
|
||||||
|
Name: "discord",
|
||||||
|
Label: "Discord",
|
||||||
|
Fields: connectors.DiscordConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "slack",
|
||||||
|
Label: "Slack",
|
||||||
|
Fields: connectors.SlackConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "telegram",
|
||||||
|
Label: "Telegram",
|
||||||
|
Fields: connectors.TelegramConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-issues",
|
||||||
|
Label: "GitHub Issues",
|
||||||
|
Fields: connectors.GithubIssueConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "github-prs",
|
||||||
|
Label: "GitHub PRs",
|
||||||
|
Fields: connectors.GithubPRConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "irc",
|
||||||
|
Label: "IRC",
|
||||||
|
Fields: connectors.IRCConfigMeta(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "twitter",
|
||||||
|
Label: "Twitter",
|
||||||
|
Fields: connectors.TwitterConfigMeta(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
)
|
)
|
||||||
@@ -34,6 +35,28 @@ func NewDiscord(config map[string]string) *Discord {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DiscordConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "Discord Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "defaultChannel",
|
||||||
|
Label: "Default Channel",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "lastMessageDuration",
|
||||||
|
Label: "Last Message Duration",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "5m",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Discord) AgentResultCallback() func(state types.ActionState) {
|
func (d *Discord) AgentResultCallback() func(state types.ActionState) {
|
||||||
return func(state types.ActionState) {
|
return func(state types.ActionState) {
|
||||||
// Send the result to the bot
|
// Send the result to the bot
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
|
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
@@ -195,3 +196,39 @@ func (g *GithubIssues) issuesService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubIssueConfigMeta returns the metadata for GitHub Issues connector configuration fields
|
||||||
|
func GithubIssueConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "replyIfNoReplies",
|
||||||
|
Label: "Reply If No Replies",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "pollInterval",
|
||||||
|
Label: "Poll Interval",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "10m",
|
||||||
|
HelpText: "How often to check for new issues (e.g., 10m, 1h)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v69/github"
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
|
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
@@ -195,3 +196,39 @@ func (g *GithubPRs) prService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GithubPRConfigMeta returns the metadata for GitHub PR connector configuration fields
|
||||||
|
func GithubPRConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "GitHub Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "repository",
|
||||||
|
Label: "Repository",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "owner",
|
||||||
|
Label: "Owner",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "replyIfNoReplies",
|
||||||
|
Label: "Reply If No Replies",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "pollInterval",
|
||||||
|
Label: "Poll Interval",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "10m",
|
||||||
|
HelpText: "How often to check for new PRs (e.g., 10m, 1h)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/mudler/LocalAgent/services/actions"
|
"github.com/mudler/LocalAgent/services/actions"
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
@@ -207,3 +208,44 @@ func (i *IRC) Start(a *agent.Agent) {
|
|||||||
// Start the IRC client in a goroutine
|
// Start the IRC client in a goroutine
|
||||||
go i.conn.Loop()
|
go i.conn.Loop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IRCConfigMeta returns the metadata for IRC connector configuration fields
|
||||||
|
func IRCConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "server",
|
||||||
|
Label: "IRC Server",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "port",
|
||||||
|
Label: "Port",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "nickname",
|
||||||
|
Label: "Nickname",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "channel",
|
||||||
|
Label: "Channel",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "alwaysReply",
|
||||||
|
Label: "Always Reply",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "lastMessageDuration",
|
||||||
|
Label: "Last Message Duration",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "5m",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/mudler/LocalAgent/pkg/xstrings"
|
"github.com/mudler/LocalAgent/pkg/xstrings"
|
||||||
"github.com/mudler/LocalAgent/services/actions"
|
"github.com/mudler/LocalAgent/services/actions"
|
||||||
@@ -784,3 +785,37 @@ func (t *Slack) Start(a *agent.Agent) {
|
|||||||
|
|
||||||
client.RunContext(a.Context())
|
client.RunContext(a.Context())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SlackConfigMeta returns the metadata for Slack connector configuration fields
|
||||||
|
func SlackConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "appToken",
|
||||||
|
Label: "App Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "botToken",
|
||||||
|
Label: "Bot Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "channelID",
|
||||||
|
Label: "Channel ID",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "alwaysReply",
|
||||||
|
Label: "Always Reply",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "lastMessageDuration",
|
||||||
|
Label: "Last Message Duration",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "5m",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/go-telegram/bot/models"
|
"github.com/go-telegram/bot/models"
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/mudler/LocalAgent/pkg/xstrings"
|
"github.com/mudler/LocalAgent/pkg/xstrings"
|
||||||
"github.com/mudler/LocalAgent/services/actions"
|
"github.com/mudler/LocalAgent/services/actions"
|
||||||
@@ -203,3 +204,27 @@ func NewTelegramConnector(config map[string]string) (*Telegram, error) {
|
|||||||
conversationTracker: NewConversationTracker[int64](duration),
|
conversationTracker: NewConversationTracker[int64](duration),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TelegramConfigMeta returns the metadata for Telegram connector configuration fields
|
||||||
|
func TelegramConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "Telegram Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "admins",
|
||||||
|
Label: "Admins",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
HelpText: "Comma-separated list of Telegram usernames that are allowed to interact with the bot",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "lastMessageDuration",
|
||||||
|
Label: "Last Message Duration",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
DefaultValue: "5m",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mudler/LocalAgent/core/agent"
|
"github.com/mudler/LocalAgent/core/agent"
|
||||||
"github.com/mudler/LocalAgent/core/types"
|
"github.com/mudler/LocalAgent/core/types"
|
||||||
|
"github.com/mudler/LocalAgent/pkg/config"
|
||||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||||
"github.com/mudler/LocalAgent/services/connectors/twitter"
|
"github.com/mudler/LocalAgent/services/connectors/twitter"
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
@@ -134,3 +135,26 @@ func (t *Twitter) run(a *agent.Agent) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TwitterConfigMeta returns the metadata for Twitter connector configuration fields
|
||||||
|
func TwitterConfigMeta() []config.Field {
|
||||||
|
return []config.Field{
|
||||||
|
{
|
||||||
|
Name: "token",
|
||||||
|
Label: "Twitter API Token",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "botUsername",
|
||||||
|
Label: "Bot Username",
|
||||||
|
Type: config.FieldTypeText,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "noCharacterLimit",
|
||||||
|
Label: "No Character Limit",
|
||||||
|
Type: config.FieldTypeCheckbox,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
12
webui/app.go
12
webui/app.go
@@ -477,3 +477,15 @@ func (a *App) CreateGroup(pool *state.AgentPool) func(c *fiber.Ctx) error {
|
|||||||
return statusJSONMessage(c, "ok")
|
return statusJSONMessage(c, "ok")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAgentConfigMeta returns the metadata for agent configuration fields
|
||||||
|
func (a *App) GetAgentConfigMeta() func(c *fiber.Ctx) error {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
// Create a new instance of AgentConfigMeta
|
||||||
|
configMeta := state.NewAgentConfigMeta(
|
||||||
|
services.ActionsConfigMeta(),
|
||||||
|
services.ConnectorsConfigMeta(),
|
||||||
|
)
|
||||||
|
return c.JSON(configMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,169 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FallbackAction from './actions/FallbackAction';
|
import ConfigForm from './ConfigForm';
|
||||||
import GithubIssueLabelerAction from './actions/GithubIssueLabelerAction';
|
|
||||||
import GithubIssueOpenerAction from './actions/GithubIssueOpenerAction';
|
|
||||||
import GithubIssueCloserAction from './actions/GithubIssueCloserAction';
|
|
||||||
import GithubIssueCommenterAction from './actions/GithubIssueCommenterAction';
|
|
||||||
import GithubRepositoryAction from './actions/GithubRepositoryAction';
|
|
||||||
import TwitterPostAction from './actions/TwitterPostAction';
|
|
||||||
import SendMailAction from './actions/SendMailAction';
|
|
||||||
import GenerateImageAction from './actions/GenerateImageAction';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActionForm component for configuring an action
|
* ActionForm component for configuring an action
|
||||||
|
* Renders action configuration forms based on field group metadata
|
||||||
*/
|
*/
|
||||||
const ActionForm = ({ actions = [], onChange, onRemove, onAdd }) => {
|
const ActionForm = ({ actions = [], onChange, onRemove, onAdd, fieldGroups = [] }) => {
|
||||||
// Available action types
|
// Debug logging
|
||||||
const actionTypes = [
|
console.log('ActionForm:', { actions, fieldGroups });
|
||||||
{ value: '', label: 'Select an action type' },
|
|
||||||
{ value: 'github-issue-labeler', label: 'GitHub Issue Labeler' },
|
// Handle action change
|
||||||
{ value: 'github-issue-opener', label: 'GitHub Issue Opener' },
|
const handleActionChange = (index, updatedAction) => {
|
||||||
{ value: 'github-issue-closer', label: 'GitHub Issue Closer' },
|
console.log('Action change:', { index, updatedAction });
|
||||||
{ value: 'github-issue-commenter', label: 'GitHub Issue Commenter' },
|
onChange(index, updatedAction);
|
||||||
{ value: 'github-repository-get-content', label: 'GitHub Repository Get Content' },
|
|
||||||
{ value: 'github-repository-create-or-update-content', label: 'GitHub Repository Create/Update Content' },
|
|
||||||
{ value: 'github-readme', label: 'GitHub Readme' },
|
|
||||||
{ value: 'twitter-post', label: 'Twitter Post' },
|
|
||||||
{ value: 'send-mail', label: 'Send Email' },
|
|
||||||
{ value: 'search', label: 'Search' },
|
|
||||||
{ value: 'github-issue-searcher', label: 'GitHub Issue Searcher' },
|
|
||||||
{ value: 'github-issue-reader', label: 'GitHub Issue Reader' },
|
|
||||||
{ value: 'scraper', label: 'Web Scraper' },
|
|
||||||
{ value: 'wikipedia', label: 'Wikipedia' },
|
|
||||||
{ value: 'browse', label: 'Browse' },
|
|
||||||
{ value: 'generate_image', label: 'Generate Image' },
|
|
||||||
{ value: 'counter', label: 'Counter' },
|
|
||||||
{ value: 'call_agents', label: 'Call Agents' },
|
|
||||||
{ value: 'shell-command', label: 'Shell Command' },
|
|
||||||
{ value: 'custom', label: 'Custom' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Parse the config JSON string to an object
|
|
||||||
const parseConfig = (action) => {
|
|
||||||
if (!action || !action.config) return {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(action.config || '{}');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error parsing action config:', error);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get a value from the config object
|
|
||||||
const getConfigValue = (action, key, defaultValue = '') => {
|
|
||||||
const config = parseConfig(action);
|
|
||||||
return config[key] !== undefined ? config[key] : defaultValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update a value in the config object
|
|
||||||
const onActionConfigChange = (index, key, value) => {
|
|
||||||
const action = actions[index];
|
|
||||||
const config = parseConfig(action);
|
|
||||||
config[key] = value;
|
|
||||||
|
|
||||||
onChange(index, {
|
|
||||||
...action,
|
|
||||||
config: JSON.stringify(config)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle action type change
|
|
||||||
const handleActionTypeChange = (index, value) => {
|
|
||||||
const action = actions[index];
|
|
||||||
onChange(index, {
|
|
||||||
...action,
|
|
||||||
name: value
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the appropriate action component based on the action type
|
|
||||||
const renderActionComponent = (action, index) => {
|
|
||||||
// Common props for all action components
|
|
||||||
const actionProps = {
|
|
||||||
index,
|
|
||||||
onActionConfigChange: (key, value) => onActionConfigChange(index, key, value),
|
|
||||||
getConfigValue: (key, defaultValue) => getConfigValue(action, key, defaultValue)
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (action.name) {
|
|
||||||
case 'github-issue-labeler':
|
|
||||||
return <GithubIssueLabelerAction {...actionProps} />;
|
|
||||||
case 'github-issue-opener':
|
|
||||||
return <GithubIssueOpenerAction {...actionProps} />;
|
|
||||||
case 'github-issue-closer':
|
|
||||||
return <GithubIssueCloserAction {...actionProps} />;
|
|
||||||
case 'github-issue-commenter':
|
|
||||||
return <GithubIssueCommenterAction {...actionProps} />;
|
|
||||||
case 'github-repository-get-content':
|
|
||||||
case 'github-repository-create-or-update-content':
|
|
||||||
case 'github-readme':
|
|
||||||
return <GithubRepositoryAction {...actionProps} />;
|
|
||||||
case 'twitter-post':
|
|
||||||
return <TwitterPostAction {...actionProps} />;
|
|
||||||
case 'send-mail':
|
|
||||||
return <SendMailAction {...actionProps} />;
|
|
||||||
case 'generate_image':
|
|
||||||
return <GenerateImageAction {...actionProps} />;
|
|
||||||
default:
|
|
||||||
return <FallbackAction {...actionProps} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render a specific action form
|
|
||||||
const renderActionForm = (action, index) => {
|
|
||||||
// Ensure action is an object with expected properties
|
|
||||||
const safeAction = action || {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={index} className="connector-item mb-4">
|
|
||||||
<div className="connector-header">
|
|
||||||
<h4>Action #{index + 1}</h4>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="remove-btn"
|
|
||||||
onClick={() => onRemove(index)}
|
|
||||||
>
|
|
||||||
<i className="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="connector-type mb-3">
|
|
||||||
<label htmlFor={`actionType${index}`}>Action Type</label>
|
|
||||||
<select
|
|
||||||
id={`actionType${index}`}
|
|
||||||
value={safeAction.name || ''}
|
|
||||||
onChange={(e) => handleActionTypeChange(index, e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
>
|
|
||||||
{actionTypes.map((type) => (
|
|
||||||
<option key={type.value} value={type.value}>
|
|
||||||
{type.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Render specific action template based on type */}
|
|
||||||
{renderActionComponent(safeAction, index)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connectors-container">
|
<ConfigForm
|
||||||
{actions && actions.map((action, index) => (
|
items={actions}
|
||||||
renderActionForm(action, index)
|
fieldGroups={fieldGroups}
|
||||||
))}
|
onChange={handleActionChange}
|
||||||
|
onRemove={onRemove}
|
||||||
<button
|
onAdd={onAdd}
|
||||||
type="button"
|
itemType="action"
|
||||||
className="add-btn"
|
typeField="name"
|
||||||
onClick={onAdd}
|
addButtonText="Add Action"
|
||||||
>
|
/>
|
||||||
<i className="fas fa-plus"></i> Add Action
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ const AgentForm = ({
|
|||||||
loading = false,
|
loading = false,
|
||||||
submitButtonText,
|
submitButtonText,
|
||||||
isGroupForm = false,
|
isGroupForm = false,
|
||||||
noFormWrapper = false
|
noFormWrapper = false,
|
||||||
|
metadata = null
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { showToast } = useOutletContext();
|
const { showToast } = useOutletContext();
|
||||||
@@ -239,70 +240,70 @@ const AgentForm = ({
|
|||||||
<div className='agent-form'>
|
<div className='agent-form'>
|
||||||
{/* Form Sections */}
|
{/* Form Sections */}
|
||||||
<div style={{ display: activeSection === 'basic-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'basic-section' ? 'block' : 'none' }}>
|
||||||
<BasicInfoSection formData={formData} handleInputChange={handleInputChange} isEdit={isEdit} isGroupForm={isGroupForm} />
|
<BasicInfoSection formData={formData} handleInputChange={handleInputChange} isEdit={isEdit} isGroupForm={isGroupForm} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'model-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'model-section' ? 'block' : 'none' }}>
|
||||||
<ModelSettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<ModelSettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'connectors-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'connectors-section' ? 'block' : 'none' }}>
|
||||||
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorNameChange={handleConnectorNameChange} handleConnectorConfigChange={handleConnectorConfigChange} />
|
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorNameChange={handleConnectorNameChange} handleConnectorConfigChange={handleConnectorConfigChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
||||||
<ActionsSection formData={formData} setFormData={setFormData} />
|
<ActionsSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'mcp-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'mcp-section' ? 'block' : 'none' }}>
|
||||||
<MCPServersSection formData={formData} handleAddMCPServer={handleAddMCPServer} handleRemoveMCPServer={handleRemoveMCPServer} handleMCPServerChange={handleMCPServerChange} />
|
<MCPServersSection formData={formData} handleAddMCPServer={handleAddMCPServer} handleRemoveMCPServer={handleRemoveMCPServer} handleMCPServerChange={handleMCPServerChange} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'memory-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'memory-section' ? 'block' : 'none' }}>
|
||||||
<MemorySettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<MemorySettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'prompts-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'prompts-section' ? 'block' : 'none' }}>
|
||||||
<PromptsGoalsSection formData={formData} handleInputChange={handleInputChange} isGroupForm={isGroupForm} />
|
<PromptsGoalsSection formData={formData} handleInputChange={handleInputChange} isGroupForm={isGroupForm} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'advanced-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'advanced-section' ? 'block' : 'none' }}>
|
||||||
<AdvancedSettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<AdvancedSettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form className="agent-form" onSubmit={handleSubmit}>
|
<form className="agent-form" onSubmit={handleSubmit} noValidate>
|
||||||
{/* Form Sections */}
|
{/* Form Sections */}
|
||||||
<div style={{ display: activeSection === 'basic-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'basic-section' ? 'block' : 'none' }}>
|
||||||
<BasicInfoSection formData={formData} handleInputChange={handleInputChange} isEdit={isEdit} isGroupForm={isGroupForm} />
|
<BasicInfoSection formData={formData} handleInputChange={handleInputChange} isEdit={isEdit} isGroupForm={isGroupForm} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'model-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'model-section' ? 'block' : 'none' }}>
|
||||||
<ModelSettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<ModelSettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'connectors-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'connectors-section' ? 'block' : 'none' }}>
|
||||||
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorNameChange={handleConnectorNameChange} handleConnectorConfigChange={handleConnectorConfigChange} />
|
<ConnectorsSection formData={formData} handleAddConnector={handleAddConnector} handleRemoveConnector={handleRemoveConnector} handleConnectorNameChange={handleConnectorNameChange} handleConnectorConfigChange={handleConnectorConfigChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'actions-section' ? 'block' : 'none' }}>
|
||||||
<ActionsSection formData={formData} setFormData={setFormData} />
|
<ActionsSection formData={formData} setFormData={setFormData} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'mcp-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'mcp-section' ? 'block' : 'none' }}>
|
||||||
<MCPServersSection formData={formData} handleAddMCPServer={handleAddMCPServer} handleRemoveMCPServer={handleRemoveMCPServer} handleMCPServerChange={handleMCPServerChange} />
|
<MCPServersSection formData={formData} handleAddMCPServer={handleAddMCPServer} handleRemoveMCPServer={handleRemoveMCPServer} handleMCPServerChange={handleMCPServerChange} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'memory-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'memory-section' ? 'block' : 'none' }}>
|
||||||
<MemorySettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<MemorySettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'prompts-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'prompts-section' ? 'block' : 'none' }}>
|
||||||
<PromptsGoalsSection formData={formData} handleInputChange={handleInputChange} isGroupForm={isGroupForm} />
|
<PromptsGoalsSection formData={formData} handleInputChange={handleInputChange} isGroupForm={isGroupForm} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: activeSection === 'advanced-section' ? 'block' : 'none' }}>
|
<div style={{ display: activeSection === 'advanced-section' ? 'block' : 'none' }}>
|
||||||
<AdvancedSettingsSection formData={formData} handleInputChange={handleInputChange} />
|
<AdvancedSettingsSection formData={formData} handleInputChange={handleInputChange} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Form Controls */}
|
{/* Form Controls */}
|
||||||
|
|||||||
146
webui/react-ui/src/components/ConfigForm.jsx
Normal file
146
webui/react-ui/src/components/ConfigForm.jsx
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import FormFieldDefinition from './common/FormFieldDefinition';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigForm - A generic component for handling configuration forms based on FieldGroups
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Array} props.items - Array of configuration items (actions, connectors, etc.)
|
||||||
|
* @param {Array} props.fieldGroups - Array of FieldGroup objects that define the available types and their fields
|
||||||
|
* @param {Function} props.onChange - Callback when an item changes
|
||||||
|
* @param {Function} props.onRemove - Callback when an item is removed
|
||||||
|
* @param {Function} props.onAdd - Callback when a new item is added
|
||||||
|
* @param {String} props.itemType - Type of items being configured ('action', 'connector', etc.)
|
||||||
|
* @param {String} props.typeField - The field name that determines the item's type (e.g., 'name' for actions, 'type' for connectors)
|
||||||
|
* @param {String} props.addButtonText - Text for the add button
|
||||||
|
*/
|
||||||
|
const ConfigForm = ({
|
||||||
|
items = [],
|
||||||
|
fieldGroups = [],
|
||||||
|
onChange,
|
||||||
|
onRemove,
|
||||||
|
onAdd,
|
||||||
|
itemType = 'item',
|
||||||
|
typeField = 'type',
|
||||||
|
addButtonText = 'Add Item'
|
||||||
|
}) => {
|
||||||
|
// Debug logging
|
||||||
|
console.log(`ConfigForm for ${itemType}:`, { items, fieldGroups });
|
||||||
|
|
||||||
|
// Generate options from fieldGroups
|
||||||
|
const typeOptions = [
|
||||||
|
{ value: '', label: `Select a ${itemType} type` },
|
||||||
|
...fieldGroups.map(group => ({
|
||||||
|
value: group.name,
|
||||||
|
label: group.label
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(`${itemType} type options:`, typeOptions);
|
||||||
|
|
||||||
|
// Parse the config JSON string to an object
|
||||||
|
const parseConfig = (item) => {
|
||||||
|
if (!item || !item.config) return {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return typeof item.config === 'string'
|
||||||
|
? JSON.parse(item.config || '{}')
|
||||||
|
: item.config;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error parsing ${itemType} config:`, error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle item type change
|
||||||
|
const handleTypeChange = (index, value) => {
|
||||||
|
const item = items[index];
|
||||||
|
onChange(index, {
|
||||||
|
...item,
|
||||||
|
[typeField]: value
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle config field change
|
||||||
|
const handleConfigChange = (index, key, value) => {
|
||||||
|
const item = items[index];
|
||||||
|
const config = parseConfig(item);
|
||||||
|
config[key] = value;
|
||||||
|
|
||||||
|
onChange(index, {
|
||||||
|
...item,
|
||||||
|
config: JSON.stringify(config)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render a specific item form
|
||||||
|
const renderItemForm = (item, index) => {
|
||||||
|
// Ensure item is an object with expected properties
|
||||||
|
const safeItem = item || {};
|
||||||
|
const itemTypeName = safeItem[typeField] || '';
|
||||||
|
|
||||||
|
// Find the field group that matches this item's type
|
||||||
|
const fieldGroup = fieldGroups.find(group => group.name === itemTypeName);
|
||||||
|
|
||||||
|
console.log(`Item ${index} type: ${itemTypeName}, Found field group:`, fieldGroup);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} className="config-item mb-4 card">
|
||||||
|
<div className="config-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem' }}>
|
||||||
|
<h4 style={{ margin: 0 }}>{itemType.charAt(0).toUpperCase() + itemType.slice(1)} #{index + 1}</h4>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="remove-btn"
|
||||||
|
onClick={() => onRemove(index)}
|
||||||
|
>
|
||||||
|
<i className="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="config-type mb-3">
|
||||||
|
<label htmlFor={`${itemType}Type${index}`}>{itemType.charAt(0).toUpperCase() + itemType.slice(1)} Type</label>
|
||||||
|
<select
|
||||||
|
id={`${itemType}Type${index}`}
|
||||||
|
value={safeItem[typeField] || ''}
|
||||||
|
onChange={(e) => handleTypeChange(index, e.target.value)}
|
||||||
|
className="form-control"
|
||||||
|
>
|
||||||
|
{typeOptions.map((type) => (
|
||||||
|
<option key={type.value} value={type.value}>
|
||||||
|
{type.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Render fields based on the selected type */}
|
||||||
|
{fieldGroup && fieldGroup.fields && (
|
||||||
|
<FormFieldDefinition
|
||||||
|
fields={fieldGroup.fields}
|
||||||
|
values={parseConfig(item)}
|
||||||
|
onChange={(key, value) => handleConfigChange(index, key, value)}
|
||||||
|
idPrefix={`${itemType}-${index}-`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-container">
|
||||||
|
{items && items.map((item, index) => (
|
||||||
|
renderItemForm(item, index)
|
||||||
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="add-btn"
|
||||||
|
onClick={onAdd}
|
||||||
|
>
|
||||||
|
<i className="fas fa-plus"></i> {addButtonText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfigForm;
|
||||||
@@ -1,137 +1,48 @@
|
|||||||
import { useState } from 'react';
|
import React from 'react';
|
||||||
|
import ConfigForm from './ConfigForm';
|
||||||
// Import connector components
|
|
||||||
import TelegramConnector from './connectors/TelegramConnector';
|
|
||||||
import SlackConnector from './connectors/SlackConnector';
|
|
||||||
import DiscordConnector from './connectors/DiscordConnector';
|
|
||||||
import GithubIssuesConnector from './connectors/GithubIssuesConnector';
|
|
||||||
import GithubPRsConnector from './connectors/GithubPRsConnector';
|
|
||||||
import IRCConnector from './connectors/IRCConnector';
|
|
||||||
import TwitterConnector from './connectors/TwitterConnector';
|
|
||||||
import FallbackConnector from './connectors/FallbackConnector';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConnectorForm component
|
* ConnectorForm component
|
||||||
* Provides specific form templates for different connector types
|
* Renders connector configuration forms based on field group metadata
|
||||||
*/
|
*/
|
||||||
function ConnectorForm({
|
function ConnectorForm({
|
||||||
connectors = [],
|
connectors = [],
|
||||||
onAddConnector,
|
onAddConnector,
|
||||||
onRemoveConnector,
|
onRemoveConnector,
|
||||||
onConnectorNameChange,
|
onConnectorNameChange,
|
||||||
onConnectorConfigChange
|
onConnectorConfigChange,
|
||||||
|
fieldGroups = []
|
||||||
}) {
|
}) {
|
||||||
const [newConfigKey, setNewConfigKey] = useState('');
|
// Debug logging
|
||||||
|
console.log('ConnectorForm:', { connectors, fieldGroups });
|
||||||
// Render a specific connector form based on its type
|
|
||||||
const renderConnectorForm = (connector, index) => {
|
// Handle connector change
|
||||||
// Ensure connector is an object with expected properties
|
const handleConnectorChange = (index, updatedConnector) => {
|
||||||
const safeConnector = connector || {};
|
console.log('Connector change:', { index, updatedConnector });
|
||||||
|
if (updatedConnector.type !== connectors[index].type) {
|
||||||
return (
|
onConnectorNameChange(index, updatedConnector.type);
|
||||||
<div key={index} className="connector-item mb-4">
|
} else {
|
||||||
<div className="connector-header">
|
onConnectorConfigChange(index, updatedConnector.config);
|
||||||
<h4>Connector #{index + 1}</h4>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="remove-btn"
|
|
||||||
onClick={() => onRemoveConnector(index)}
|
|
||||||
>
|
|
||||||
<i className="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="connector-type mb-3">
|
|
||||||
<label htmlFor={`connectorName${index}`}>Connector Type</label>
|
|
||||||
<select
|
|
||||||
id={`connectorName${index}`}
|
|
||||||
value={safeConnector.type || ''}
|
|
||||||
onChange={(e) => onConnectorNameChange(index, e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
>
|
|
||||||
<option value="">Select a connector type</option>
|
|
||||||
<option value="telegram">Telegram</option>
|
|
||||||
<option value="slack">Slack</option>
|
|
||||||
<option value="discord">Discord</option>
|
|
||||||
<option value="github-issues">GitHub Issues</option>
|
|
||||||
<option value="github-prs">GitHub PRs</option>
|
|
||||||
<option value="irc">IRC</option>
|
|
||||||
<option value="twitter">Twitter</option>
|
|
||||||
<option value="custom">Custom</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Render specific connector template based on type */}
|
|
||||||
{renderConnectorTemplate(safeConnector, index)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the appropriate form template based on connector type
|
|
||||||
const renderConnectorTemplate = (connector, index) => {
|
|
||||||
// Check if connector.type exists, if not use empty string to avoid errors
|
|
||||||
const connectorType = (connector.type || '').toLowerCase();
|
|
||||||
|
|
||||||
// Common props for all connector components
|
|
||||||
const connectorProps = {
|
|
||||||
connector,
|
|
||||||
index,
|
|
||||||
onConnectorConfigChange,
|
|
||||||
getConfigValue
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (connectorType) {
|
|
||||||
case 'telegram':
|
|
||||||
return <TelegramConnector {...connectorProps} />;
|
|
||||||
case 'slack':
|
|
||||||
return <SlackConnector {...connectorProps} />;
|
|
||||||
case 'discord':
|
|
||||||
return <DiscordConnector {...connectorProps} />;
|
|
||||||
case 'github-issues':
|
|
||||||
return <GithubIssuesConnector {...connectorProps} />;
|
|
||||||
case 'github-prs':
|
|
||||||
return <GithubPRsConnector {...connectorProps} />;
|
|
||||||
case 'irc':
|
|
||||||
return <IRCConnector {...connectorProps} />;
|
|
||||||
case 'twitter':
|
|
||||||
return <TwitterConnector {...connectorProps} />;
|
|
||||||
default:
|
|
||||||
return <FallbackConnector {...connectorProps} />;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to safely get config values
|
// Handle adding a new connector
|
||||||
const getConfigValue = (connector, key, defaultValue = '') => {
|
const handleAddConnector = () => {
|
||||||
if (!connector || !connector.config) return defaultValue;
|
console.log('Adding new connector');
|
||||||
|
onAddConnector();
|
||||||
// If config is a string (JSON), try to parse it
|
|
||||||
let config = connector.config;
|
|
||||||
if (typeof config === 'string') {
|
|
||||||
try {
|
|
||||||
config = JSON.parse(config);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error parsing config:', err);
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config[key] !== undefined ? config[key] : defaultValue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connectors-container">
|
<ConfigForm
|
||||||
{connectors && connectors.map((connector, index) => (
|
items={connectors}
|
||||||
renderConnectorForm(connector, index)
|
fieldGroups={fieldGroups}
|
||||||
))}
|
onChange={handleConnectorChange}
|
||||||
|
onRemove={onRemoveConnector}
|
||||||
<button
|
onAdd={handleAddConnector}
|
||||||
type="button"
|
itemType="connector"
|
||||||
className="add-btn"
|
typeField="type"
|
||||||
onClick={onAddConnector}
|
addButtonText="Add Connector"
|
||||||
>
|
/>
|
||||||
<i className="fas fa-plus"></i> Add Connector
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import FormFieldDefinition from '../common/FormFieldDefinition';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base action component that renders form fields based on field definitions
|
|
||||||
*
|
|
||||||
* @param {Object} props Component props
|
|
||||||
* @param {number} props.index Action index
|
|
||||||
* @param {Function} props.onActionConfigChange Handler for config changes
|
|
||||||
* @param {Function} props.getConfigValue Helper to get config values
|
|
||||||
* @param {Array} props.fields Field definitions for this action
|
|
||||||
*/
|
|
||||||
const BaseAction = ({
|
|
||||||
index,
|
|
||||||
onActionConfigChange,
|
|
||||||
getConfigValue,
|
|
||||||
fields = []
|
|
||||||
}) => {
|
|
||||||
// Create an object with all the current values
|
|
||||||
const currentValues = {};
|
|
||||||
|
|
||||||
// Pre-populate with current values or defaults
|
|
||||||
fields.forEach(field => {
|
|
||||||
currentValues[field.name] = getConfigValue(field.name, field.defaultValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle field value changes
|
|
||||||
const handleFieldChange = (name, value) => {
|
|
||||||
onActionConfigChange(name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="action-template">
|
|
||||||
<FormFieldDefinition
|
|
||||||
fields={fields}
|
|
||||||
values={currentValues}
|
|
||||||
onChange={handleFieldChange}
|
|
||||||
idPrefix={`action${index}_`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaseAction;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FallbackAction component for actions without specific configuration
|
|
||||||
*/
|
|
||||||
const FallbackAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
return (
|
|
||||||
<div className="fallback-action">
|
|
||||||
<p className="text-muted">
|
|
||||||
This action doesn't require any additional configuration.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FallbackAction;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate Image action component
|
|
||||||
*/
|
|
||||||
const GenerateImageAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Generate Image action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'apiKey',
|
|
||||||
label: 'OpenAI API Key',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'sk-...',
|
|
||||||
helpText: 'Your OpenAI API key for image generation',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'apiURL',
|
|
||||||
label: 'API URL',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'http://localai:8081',
|
|
||||||
helpText: 'OpenAI compatible API endpoint URL',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'model',
|
|
||||||
label: 'Model',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'dall-e-3',
|
|
||||||
helpText: 'Image generation model',
|
|
||||||
required: false,
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GenerateImageAction;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Issue Closer action component
|
|
||||||
*/
|
|
||||||
const GithubIssueCloserAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Issue Closer action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customActionName',
|
|
||||||
label: 'Custom Action Name (Optional)',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'close_github_issue',
|
|
||||||
helpText: 'Custom name for this action (optional)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubIssueCloserAction;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Issue Commenter action component
|
|
||||||
*/
|
|
||||||
const GithubIssueCommenterAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Issue Commenter action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: 'Owner of the repository',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: 'Name of the repository',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customActionName',
|
|
||||||
label: 'Custom Action Name (Optional)',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'comment_on_github_issue',
|
|
||||||
helpText: 'Custom name for this action (optional)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubIssueCommenterAction;
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Issue Labeler action component
|
|
||||||
*/
|
|
||||||
const GithubIssueLabelerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Issue Labeler action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'availableLabels',
|
|
||||||
label: 'Available Labels',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: 'bug,enhancement',
|
|
||||||
placeholder: 'bug,enhancement,documentation',
|
|
||||||
helpText: 'Comma-separated list of available labels',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customActionName',
|
|
||||||
label: 'Custom Action Name (Optional)',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'add_label_to_issue',
|
|
||||||
helpText: 'Custom name for this action (optional)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubIssueLabelerAction;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Issue Opener action component
|
|
||||||
*/
|
|
||||||
const GithubIssueOpenerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Issue Opener action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customActionName',
|
|
||||||
label: 'Custom Action Name (Optional)',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'open_github_issue',
|
|
||||||
helpText: 'Custom name for this action (optional)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubIssueOpenerAction;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Repository action component for repository-related actions
|
|
||||||
* Used for:
|
|
||||||
* - github-repository-get-content
|
|
||||||
* - github-repository-create-or-update-content
|
|
||||||
* - github-readme
|
|
||||||
*/
|
|
||||||
const GithubRepositoryAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Repository action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'customActionName',
|
|
||||||
label: 'Custom Action Name (Optional)',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'github_repo_action',
|
|
||||||
helpText: 'Custom name for this action (optional)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubRepositoryAction;
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SendMail action component
|
|
||||||
*/
|
|
||||||
const SendMailAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for SendMail action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'email',
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'your-email@example.com',
|
|
||||||
helpText: 'Email address to send from',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'username',
|
|
||||||
label: 'Username',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'SMTP username (often same as email)',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'password',
|
|
||||||
label: 'Password',
|
|
||||||
type: 'password',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'SMTP password or app password',
|
|
||||||
helpText: 'For Gmail, use an app password',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'smtpHost',
|
|
||||||
label: 'SMTP Host',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'smtp.gmail.com',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'smtpPort',
|
|
||||||
label: 'SMTP Port',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '587',
|
|
||||||
placeholder: '587',
|
|
||||||
helpText: 'Common ports: 587 (TLS), 465 (SSL)',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SendMailAction;
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseAction from './BaseAction';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Twitter Post action component
|
|
||||||
*/
|
|
||||||
const TwitterPostAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Twitter Post action
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'Twitter API Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter API token',
|
|
||||||
helpText: 'Twitter API token with posting permissions',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'noCharacterLimits',
|
|
||||||
label: 'Disable character limit (280 characters)',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: 'false',
|
|
||||||
helpText: 'Enable to bypass the 280 character limit check',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseAction
|
|
||||||
index={index}
|
|
||||||
onActionConfigChange={onActionConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TwitterPostAction;
|
|
||||||
@@ -4,7 +4,7 @@ import ActionForm from '../ActionForm';
|
|||||||
/**
|
/**
|
||||||
* ActionsSection component for the agent form
|
* ActionsSection component for the agent form
|
||||||
*/
|
*/
|
||||||
const ActionsSection = ({ formData, setFormData }) => {
|
const ActionsSection = ({ formData, setFormData, metadata }) => {
|
||||||
// Handle action change
|
// Handle action change
|
||||||
const handleActionChange = (index, updatedAction) => {
|
const handleActionChange = (index, updatedAction) => {
|
||||||
const updatedActions = [...(formData.actions || [])];
|
const updatedActions = [...(formData.actions || [])];
|
||||||
@@ -47,6 +47,7 @@ const ActionsSection = ({ formData, setFormData }) => {
|
|||||||
onChange={handleActionChange}
|
onChange={handleActionChange}
|
||||||
onRemove={handleActionRemove}
|
onRemove={handleActionRemove}
|
||||||
onAdd={handleAddAction}
|
onAdd={handleAddAction}
|
||||||
|
fieldGroups={metadata?.actions || []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,53 +3,20 @@ import FormFieldDefinition from '../common/FormFieldDefinition';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Advanced Settings section of the agent form
|
* Advanced Settings section of the agent form
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.formData Current form data values
|
||||||
|
* @param {Function} props.handleInputChange Handler for input changes
|
||||||
|
* @param {Object} props.metadata Field metadata from the backend
|
||||||
*/
|
*/
|
||||||
const AdvancedSettingsSection = ({ formData, handleInputChange }) => {
|
const AdvancedSettingsSection = ({ formData, handleInputChange, metadata }) => {
|
||||||
// Define field definitions for Advanced Settings section
|
// Get fields from metadata
|
||||||
const fields = [
|
const fields = metadata?.AdvancedSettingsSection || [];
|
||||||
{
|
|
||||||
name: 'max_steps',
|
|
||||||
label: 'Max Steps',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: 10,
|
|
||||||
helpText: 'Maximum number of steps the agent can take',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'max_iterations',
|
|
||||||
label: 'Max Iterations',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: 5,
|
|
||||||
helpText: 'Maximum number of iterations for each step',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'autonomous',
|
|
||||||
label: 'Autonomous Mode',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: false,
|
|
||||||
helpText: 'Allow the agent to operate autonomously',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'verbose',
|
|
||||||
label: 'Verbose Mode',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: false,
|
|
||||||
helpText: 'Enable detailed logging',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'allow_code_execution',
|
|
||||||
label: 'Allow Code Execution',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: false,
|
|
||||||
helpText: 'Allow the agent to execute code (use with caution)',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Handle field value changes
|
// Handle field value changes
|
||||||
const handleFieldChange = (name, value) => {
|
const handleFieldChange = (name, value) => {
|
||||||
// For checkboxes, convert string 'true'/'false' to boolean
|
const field = fields.find(f => f.name === name);
|
||||||
if (['autonomous', 'verbose', 'allow_code_execution'].includes(name)) {
|
if (field && field.type === 'checkbox') {
|
||||||
handleInputChange({
|
handleInputChange({
|
||||||
target: {
|
target: {
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -3,54 +3,37 @@ import FormFieldDefinition from '../common/FormFieldDefinition';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic Information section of the agent form
|
* Basic Information section of the agent form
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.formData Current form data values
|
||||||
|
* @param {Function} props.handleInputChange Handler for input changes
|
||||||
|
* @param {boolean} props.isEdit Whether the form is in edit mode
|
||||||
|
* @param {boolean} props.isGroupForm Whether the form is for a group
|
||||||
|
* @param {Object} props.metadata Field metadata from the backend
|
||||||
*/
|
*/
|
||||||
const BasicInfoSection = ({ formData, handleInputChange, isEdit, isGroupForm }) => {
|
const BasicInfoSection = ({ formData, handleInputChange, isEdit, isGroupForm, metadata }) => {
|
||||||
// In group form context, we hide the basic info section entirely
|
// In group form context, we hide the basic info section entirely
|
||||||
if (isGroupForm) {
|
if (isGroupForm) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define field definitions for Basic Information section
|
// Get fields from metadata and apply any client-side overrides
|
||||||
const fields = [
|
const fields = metadata?.BasicInfoSection?.map(field => {
|
||||||
{
|
// Special case for name field in edit mode
|
||||||
name: 'name',
|
if (field.name === 'name' && isEdit) {
|
||||||
label: 'Name',
|
return {
|
||||||
type: 'text',
|
...field,
|
||||||
defaultValue: '',
|
disabled: true,
|
||||||
required: true,
|
helpText: 'Agent name cannot be changed after creation'
|
||||||
helpText: isEdit ? 'Agent name cannot be changed after creation' : '',
|
};
|
||||||
disabled: isEdit, // This will be handled in the component
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
label: 'Description',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'identity_guidance',
|
|
||||||
label: 'Identity Guidance',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'random_identity',
|
|
||||||
label: 'Random Identity',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'hud',
|
|
||||||
label: 'HUD',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: false,
|
|
||||||
}
|
}
|
||||||
];
|
return field;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
// Handle field value changes
|
// Handle field value changes
|
||||||
const handleFieldChange = (name, value) => {
|
const handleFieldChange = (name, value) => {
|
||||||
// For checkboxes, convert string 'true'/'false' to boolean
|
const field = fields.find(f => f.name === name);
|
||||||
if (name === 'random_identity' || name === 'hud') {
|
if (field && field.type === 'checkbox') {
|
||||||
handleInputChange({
|
handleInputChange({
|
||||||
target: {
|
target: {
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ const ConnectorsSection = ({
|
|||||||
handleAddConnector,
|
handleAddConnector,
|
||||||
handleRemoveConnector,
|
handleRemoveConnector,
|
||||||
handleConnectorNameChange,
|
handleConnectorNameChange,
|
||||||
handleConnectorConfigChange
|
handleConnectorConfigChange,
|
||||||
|
metadata
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div id="connectors-section">
|
<div id="connectors-section">
|
||||||
@@ -24,6 +25,7 @@ const ConnectorsSection = ({
|
|||||||
onRemoveConnector={handleRemoveConnector}
|
onRemoveConnector={handleRemoveConnector}
|
||||||
onConnectorNameChange={handleConnectorNameChange}
|
onConnectorNameChange={handleConnectorNameChange}
|
||||||
onConnectorConfigChange={handleConnectorConfigChange}
|
onConnectorConfigChange={handleConnectorConfigChange}
|
||||||
|
fieldGroups={metadata?.connectors || []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,53 +3,35 @@ import FormFieldDefinition from '../common/FormFieldDefinition';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Memory Settings section of the agent form
|
* Memory Settings section of the agent form
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.formData Current form data values
|
||||||
|
* @param {Function} props.handleInputChange Handler for input changes
|
||||||
|
* @param {Object} props.metadata Field metadata from the backend
|
||||||
*/
|
*/
|
||||||
const MemorySettingsSection = ({ formData, handleInputChange }) => {
|
const MemorySettingsSection = ({ formData, handleInputChange, metadata }) => {
|
||||||
// Define field definitions for Memory Settings section
|
// Get fields from metadata
|
||||||
const fields = [
|
const fields = metadata?.MemorySettingsSection || [];
|
||||||
{
|
|
||||||
name: 'memory_provider',
|
|
||||||
label: 'Memory Provider',
|
|
||||||
type: 'select',
|
|
||||||
defaultValue: 'local',
|
|
||||||
options: [
|
|
||||||
{ value: 'local', label: 'Local' },
|
|
||||||
{ value: 'redis', label: 'Redis' },
|
|
||||||
{ value: 'postgres', label: 'PostgreSQL' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_collection',
|
|
||||||
label: 'Memory Collection',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'agent_memories',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_url',
|
|
||||||
label: 'Memory URL',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'redis://localhost:6379',
|
|
||||||
helpText: 'Connection URL for Redis or PostgreSQL',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_window_size',
|
|
||||||
label: 'Memory Window Size',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: 10,
|
|
||||||
helpText: 'Number of recent messages to include in context window',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Handle field value changes
|
// Handle field value changes
|
||||||
const handleFieldChange = (name, value) => {
|
const handleFieldChange = (name, value) => {
|
||||||
handleInputChange({
|
const field = fields.find(f => f.name === name);
|
||||||
target: {
|
if (field && field.type === 'checkbox') {
|
||||||
name,
|
handleInputChange({
|
||||||
value
|
target: {
|
||||||
}
|
name,
|
||||||
});
|
type: 'checkbox',
|
||||||
|
checked: value === 'true'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleInputChange({
|
||||||
|
target: {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,60 +3,35 @@ import FormFieldDefinition from '../common/FormFieldDefinition';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Model Settings section of the agent form
|
* Model Settings section of the agent form
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.formData Current form data values
|
||||||
|
* @param {Function} props.handleInputChange Handler for input changes
|
||||||
|
* @param {Object} props.metadata Field metadata from the backend
|
||||||
*/
|
*/
|
||||||
const ModelSettingsSection = ({ formData, handleInputChange }) => {
|
const ModelSettingsSection = ({ formData, handleInputChange, metadata }) => {
|
||||||
// Define field definitions for Model Settings section
|
// Get fields from metadata
|
||||||
const fields = [
|
const fields = metadata?.ModelSettingsSection || [];
|
||||||
{
|
|
||||||
name: 'model',
|
|
||||||
label: 'Model',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'multimodal_model',
|
|
||||||
label: 'Multimodal Model',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'api_url',
|
|
||||||
label: 'API URL',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'api_key',
|
|
||||||
label: 'API Key',
|
|
||||||
type: 'password',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'temperature',
|
|
||||||
label: 'Temperature',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: 0.7,
|
|
||||||
min: 0,
|
|
||||||
max: 2,
|
|
||||||
step: 0.1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'max_tokens',
|
|
||||||
label: 'Max Tokens',
|
|
||||||
type: 'number',
|
|
||||||
defaultValue: 2000,
|
|
||||||
min: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Handle field value changes
|
// Handle field value changes
|
||||||
const handleFieldChange = (name, value) => {
|
const handleFieldChange = (name, value) => {
|
||||||
handleInputChange({
|
const field = fields.find(f => f.name === name);
|
||||||
target: {
|
if (field && field.type === 'checkbox') {
|
||||||
name,
|
handleInputChange({
|
||||||
value
|
target: {
|
||||||
}
|
name,
|
||||||
});
|
type: 'checkbox',
|
||||||
|
checked: value === 'true'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleInputChange({
|
||||||
|
target: {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,64 +3,47 @@ import FormFieldDefinition from '../common/FormFieldDefinition';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts & Goals section of the agent form
|
* Prompts & Goals section of the agent form
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.formData Current form data values
|
||||||
|
* @param {Function} props.handleInputChange Handler for input changes
|
||||||
|
* @param {boolean} props.isGroupForm Whether the form is for a group
|
||||||
|
* @param {Object} props.metadata Field metadata from the backend
|
||||||
*/
|
*/
|
||||||
const PromptsGoalsSection = ({ formData, handleInputChange, isGroupForm }) => {
|
const PromptsGoalsSection = ({ formData, handleInputChange, isGroupForm, metadata }) => {
|
||||||
// Define field definitions for Prompts & Goals section
|
// Get fields based on metadata and form context
|
||||||
const getFields = () => {
|
const getFields = () => {
|
||||||
// Base fields that are always shown
|
if (!metadata?.PromptsGoalsSection) {
|
||||||
const baseFields = [
|
return [];
|
||||||
{
|
|
||||||
name: 'goals',
|
|
||||||
label: 'Goals',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
helpText: 'Define the agent\'s goals (one per line)',
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'constraints',
|
|
||||||
label: 'Constraints',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
helpText: 'Define the agent\'s constraints (one per line)',
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'tools',
|
|
||||||
label: 'Tools',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
helpText: 'Define the agent\'s tools (one per line)',
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Only include system_prompt field if not in group form context
|
|
||||||
if (!isGroupForm) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'system_prompt',
|
|
||||||
label: 'System Prompt',
|
|
||||||
type: 'textarea',
|
|
||||||
defaultValue: '',
|
|
||||||
helpText: 'Instructions that define the agent\'s behavior',
|
|
||||||
rows: 5,
|
|
||||||
},
|
|
||||||
...baseFields
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseFields;
|
// If in group form, filter out system_prompt
|
||||||
|
if (isGroupForm) {
|
||||||
|
return metadata.PromptsGoalsSection.filter(field => field.name !== 'system_prompt');
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.PromptsGoalsSection;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle field value changes
|
// Handle field value changes
|
||||||
const handleFieldChange = (name, value) => {
|
const handleFieldChange = (name, value) => {
|
||||||
handleInputChange({
|
const field = getFields().find(f => f.name === name);
|
||||||
target: {
|
if (field && field.type === 'checkbox') {
|
||||||
name,
|
handleInputChange({
|
||||||
value
|
target: {
|
||||||
}
|
name,
|
||||||
});
|
type: 'checkbox',
|
||||||
|
checked: value === 'true'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleInputChange({
|
||||||
|
target: {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ const FormField = ({
|
|||||||
helpText = '',
|
helpText = '',
|
||||||
options = [],
|
options = [],
|
||||||
required = false,
|
required = false,
|
||||||
|
min = 0,
|
||||||
|
max = 2**31,
|
||||||
|
step = 1,
|
||||||
}) => {
|
}) => {
|
||||||
// Create label with required indicator
|
// Create label with required indicator
|
||||||
const labelWithIndicator = required ? (
|
const labelWithIndicator = required ? (
|
||||||
@@ -86,6 +89,25 @@ const FormField = ({
|
|||||||
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
case 'number':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={id}>{labelWithIndicator}</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id={id}
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={placeholder}
|
||||||
|
required={required}
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
step={step}
|
||||||
|
/>
|
||||||
|
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import FormFieldDefinition from '../common/FormFieldDefinition';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base connector component that renders form fields based on field definitions
|
|
||||||
*
|
|
||||||
* @param {Object} props Component props
|
|
||||||
* @param {Object} props.connector Connector data
|
|
||||||
* @param {number} props.index Connector index
|
|
||||||
* @param {Function} props.onConnectorConfigChange Handler for config changes
|
|
||||||
* @param {Function} props.getConfigValue Helper to get config values
|
|
||||||
* @param {Array} props.fields Field definitions for this connector
|
|
||||||
*/
|
|
||||||
const BaseConnector = ({
|
|
||||||
connector,
|
|
||||||
index,
|
|
||||||
onConnectorConfigChange,
|
|
||||||
getConfigValue,
|
|
||||||
fields = []
|
|
||||||
}) => {
|
|
||||||
// Create an object with all the current values
|
|
||||||
const currentValues = {};
|
|
||||||
|
|
||||||
// Pre-populate with current values or defaults
|
|
||||||
fields.forEach(field => {
|
|
||||||
currentValues[field.name] = getConfigValue(connector, field.name, field.defaultValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle field value changes
|
|
||||||
const handleFieldChange = (name, value) => {
|
|
||||||
onConnectorConfigChange(index, name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="connector-template">
|
|
||||||
<FormFieldDefinition
|
|
||||||
fields={fields}
|
|
||||||
values={currentValues}
|
|
||||||
onChange={handleFieldChange}
|
|
||||||
idPrefix={`connector${index}_`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaseConnector;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discord connector template
|
|
||||||
*/
|
|
||||||
const DiscordConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Discord connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'Discord Bot Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Bot token from Discord Developer Portal',
|
|
||||||
helpText: 'Get this from the Discord Developer Portal',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'defaultChannel',
|
|
||||||
label: 'Default Channel',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: '123456789012345678',
|
|
||||||
helpText: 'Channel ID to always answer even if not mentioned',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DiscordConnector;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fallback connector template for unknown connector types
|
|
||||||
*/
|
|
||||||
const FallbackConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
const [newConfigKey, setNewConfigKey] = useState('');
|
|
||||||
|
|
||||||
// Parse config if it's a string
|
|
||||||
let parsedConfig = connector.config;
|
|
||||||
if (typeof parsedConfig === 'string') {
|
|
||||||
try {
|
|
||||||
parsedConfig = JSON.parse(parsedConfig);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error parsing config:', err);
|
|
||||||
parsedConfig = {};
|
|
||||||
}
|
|
||||||
} else if (!parsedConfig) {
|
|
||||||
parsedConfig = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle adding a new custom field
|
|
||||||
const handleAddCustomField = () => {
|
|
||||||
if (newConfigKey) {
|
|
||||||
onConnectorConfigChange(index, newConfigKey, '');
|
|
||||||
setNewConfigKey('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="connector-template">
|
|
||||||
{/* Individual field inputs */}
|
|
||||||
{parsedConfig && Object.entries(parsedConfig).map(([key, value]) => (
|
|
||||||
<div key={key} className="form-group mb-3">
|
|
||||||
<label htmlFor={`connector-${index}-${key}`}>{key}</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`connector-${index}-${key}`}
|
|
||||||
className="form-control"
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, key, e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Add custom configuration field */}
|
|
||||||
<div className="add-config-field mt-4">
|
|
||||||
<h5>Add Custom Configuration Field</h5>
|
|
||||||
<div className="input-group mb-3">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="New config key"
|
|
||||||
className="form-control"
|
|
||||||
value={newConfigKey}
|
|
||||||
onChange={(e) => setNewConfigKey(e.target.value)}
|
|
||||||
onKeyPress={(e) => e.key === 'Enter' && handleAddCustomField()}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-outline-primary"
|
|
||||||
onClick={handleAddCustomField}
|
|
||||||
>
|
|
||||||
<i className="fas fa-plus"></i> Add Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FallbackConnector;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub Issues connector template
|
|
||||||
*/
|
|
||||||
const GithubIssuesConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub Issues connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Personal Access Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'replyIfNoReplies',
|
|
||||||
label: 'Reply Behavior',
|
|
||||||
type: 'select',
|
|
||||||
defaultValue: 'false',
|
|
||||||
options: [
|
|
||||||
{ value: 'false', label: 'Reply to all issues' },
|
|
||||||
{ value: 'true', label: 'Only reply to issues with no comments' },
|
|
||||||
],
|
|
||||||
helpText: '',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pollInterval',
|
|
||||||
label: 'Poll Interval',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '10m',
|
|
||||||
placeholder: '10m',
|
|
||||||
helpText: 'How often to check for new issues (e.g., 10m, 1h)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubIssuesConnector;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub PRs connector template
|
|
||||||
*/
|
|
||||||
const GithubPRsConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for GitHub PRs connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'GitHub Personal Access Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'ghp_...',
|
|
||||||
helpText: 'Personal access token with repo scope',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'owner',
|
|
||||||
label: 'Repository Owner',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'username or organization',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'repository',
|
|
||||||
label: 'Repository Name',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'repository-name',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'replyIfNoReplies',
|
|
||||||
label: 'Reply Behavior',
|
|
||||||
type: 'select',
|
|
||||||
defaultValue: 'false',
|
|
||||||
options: [
|
|
||||||
{ value: 'false', label: 'Reply to all PRs' },
|
|
||||||
{ value: 'true', label: 'Only reply to PRs with no comments' },
|
|
||||||
],
|
|
||||||
helpText: '',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pollInterval',
|
|
||||||
label: 'Poll Interval',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '10m',
|
|
||||||
placeholder: '10m',
|
|
||||||
helpText: 'How often to check for new PRs (e.g., 10m, 1h)',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GithubPRsConnector;
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IRC connector template
|
|
||||||
*/
|
|
||||||
const IRCConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for IRC connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'server',
|
|
||||||
label: 'IRC Server',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'irc.libera.chat',
|
|
||||||
helpText: 'IRC server address',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'port',
|
|
||||||
label: 'Port',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '6667',
|
|
||||||
placeholder: '6667',
|
|
||||||
helpText: 'IRC server port',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nickname',
|
|
||||||
label: 'Nickname',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'MyAgentBot',
|
|
||||||
helpText: 'Bot nickname',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'channel',
|
|
||||||
label: 'Channel',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: '#channel1',
|
|
||||||
helpText: 'Channel to join',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'alwaysReply',
|
|
||||||
label: 'Always Reply',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: 'false',
|
|
||||||
helpText: 'If checked, the agent will reply to all messages in the channel',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IRCConnector;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slack connector template
|
|
||||||
*/
|
|
||||||
const SlackConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Slack connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'appToken',
|
|
||||||
label: 'Slack App Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'xapp-...',
|
|
||||||
helpText: 'App-level token starting with xapp-',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'botToken',
|
|
||||||
label: 'Slack Bot Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'xoxb-...',
|
|
||||||
helpText: 'Bot token starting with xoxb-',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'channelID',
|
|
||||||
label: 'Slack Channel ID',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'C1234567890',
|
|
||||||
helpText: 'Optional: Specific channel ID to join',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'alwaysReply',
|
|
||||||
label: 'Always Reply',
|
|
||||||
type: 'checkbox',
|
|
||||||
defaultValue: 'false',
|
|
||||||
helpText: 'If checked, the agent will reply to all messages in the channel',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SlackConnector;
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Telegram connector template
|
|
||||||
*/
|
|
||||||
const TelegramConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Telegram connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'token',
|
|
||||||
label: 'Telegram Bot Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
|
|
||||||
helpText: 'Get this from @BotFather on Telegram',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TelegramConnector;
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseConnector from './BaseConnector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Twitter connector template
|
|
||||||
*/
|
|
||||||
const TwitterConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
|
||||||
// Field definitions for Twitter connector
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'apiKey',
|
|
||||||
label: 'API Key',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter API Key',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'apiSecret',
|
|
||||||
label: 'API Secret',
|
|
||||||
type: 'password',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter API Secret',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'accessToken',
|
|
||||||
label: 'Access Token',
|
|
||||||
type: 'text',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter Access Token',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'accessSecret',
|
|
||||||
label: 'Access Token Secret',
|
|
||||||
type: 'password',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter Access Token Secret',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'bearerToken',
|
|
||||||
label: 'Bearer Token',
|
|
||||||
type: 'password',
|
|
||||||
defaultValue: '',
|
|
||||||
placeholder: 'Twitter Bearer Token',
|
|
||||||
helpText: '',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseConnector
|
|
||||||
connector={connector}
|
|
||||||
index={index}
|
|
||||||
onConnectorConfigChange={onConnectorConfigChange}
|
|
||||||
getConfigValue={getConfigValue}
|
|
||||||
fields={fields}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TwitterConnector;
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useParams, useOutletContext, useNavigate } from 'react-router-dom';
|
import { useParams, useOutletContext, useNavigate } from 'react-router-dom';
|
||||||
import { useAgent } from '../hooks/useAgent';
|
import { useAgent } from '../hooks/useAgent';
|
||||||
|
import { agentApi } from '../utils/api';
|
||||||
import AgentForm from '../components/AgentForm';
|
import AgentForm from '../components/AgentForm';
|
||||||
|
|
||||||
function AgentSettings() {
|
function AgentSettings() {
|
||||||
const { name } = useParams();
|
const { name } = useParams();
|
||||||
const { showToast } = useOutletContext();
|
const { showToast } = useOutletContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [metadata, setMetadata] = useState(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
@@ -47,9 +49,28 @@ function AgentSettings() {
|
|||||||
deleteAgent
|
deleteAgent
|
||||||
} = useAgent(name);
|
} = useAgent(name);
|
||||||
|
|
||||||
|
// Fetch metadata on component mount
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchMetadata = async () => {
|
||||||
|
try {
|
||||||
|
// Fetch metadata from the dedicated endpoint
|
||||||
|
const response = await agentApi.getAgentConfigMetadata();
|
||||||
|
if (response) {
|
||||||
|
setMetadata(response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching metadata:', error);
|
||||||
|
// Continue without metadata, the form will use default fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchMetadata();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Load agent data when component mounts
|
// Load agent data when component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (agent) {
|
if (agent) {
|
||||||
|
// Set form data from agent config
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
...agent,
|
...agent,
|
||||||
@@ -162,6 +183,7 @@ function AgentSettings() {
|
|||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
submitButtonText="Save Changes"
|
submitButtonText="Save Changes"
|
||||||
|
metadata={metadata}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useNavigate, useOutletContext } from 'react-router-dom';
|
import { useNavigate, useOutletContext } from 'react-router-dom';
|
||||||
import { agentApi } from '../utils/api';
|
import { agentApi } from '../utils/api';
|
||||||
import AgentForm from '../components/AgentForm';
|
import AgentForm from '../components/AgentForm';
|
||||||
@@ -7,6 +7,7 @@ function CreateAgent() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { showToast } = useOutletContext();
|
const { showToast } = useOutletContext();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [metadata, setMetadata] = useState(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
@@ -37,6 +38,24 @@ function CreateAgent() {
|
|||||||
avatar_style: 'default',
|
avatar_style: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch metadata on component mount
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchMetadata = async () => {
|
||||||
|
try {
|
||||||
|
// Fetch metadata from the dedicated endpoint
|
||||||
|
const response = await agentApi.getAgentConfigMetadata();
|
||||||
|
if (response) {
|
||||||
|
setMetadata(response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching metadata:', error);
|
||||||
|
// Continue without metadata, the form will use default fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchMetadata();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -80,6 +99,7 @@ function CreateAgent() {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
submitButtonText="Create Agent"
|
submitButtonText="Create Agent"
|
||||||
isEdit={false}
|
isEdit={false}
|
||||||
|
metadata={metadata}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
45
webui/react-ui/src/utils/api.js
vendored
45
webui/react-ui/src/utils/api.js
vendored
@@ -42,6 +42,51 @@ export const agentApi = {
|
|||||||
return handleResponse(response);
|
return handleResponse(response);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get agent configuration metadata
|
||||||
|
getAgentConfigMetadata: async () => {
|
||||||
|
const response = await fetch(buildUrl(API_CONFIG.endpoints.agentConfigMetadata), {
|
||||||
|
headers: API_CONFIG.headers
|
||||||
|
});
|
||||||
|
const metadata = await handleResponse(response);
|
||||||
|
|
||||||
|
// Process metadata to group by section
|
||||||
|
if (metadata) {
|
||||||
|
const groupedMetadata = {};
|
||||||
|
|
||||||
|
// Handle Fields - Group by section
|
||||||
|
if (metadata.Fields) {
|
||||||
|
metadata.Fields.forEach(field => {
|
||||||
|
const section = field.tags?.section || 'Other';
|
||||||
|
const sectionKey = `${section}Section`; // Add "Section" postfix
|
||||||
|
|
||||||
|
if (!groupedMetadata[sectionKey]) {
|
||||||
|
groupedMetadata[sectionKey] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedMetadata[sectionKey].push(field);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass through connectors and actions field groups directly
|
||||||
|
// Make sure to assign the correct metadata to each section
|
||||||
|
if (metadata.Connectors) {
|
||||||
|
console.log("Original Connectors metadata:", metadata.Connectors);
|
||||||
|
groupedMetadata.connectors = metadata.Connectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.Actions) {
|
||||||
|
console.log("Original Actions metadata:", metadata.Actions);
|
||||||
|
groupedMetadata.actions = metadata.Actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Processed metadata:", groupedMetadata);
|
||||||
|
|
||||||
|
return groupedMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
},
|
||||||
|
|
||||||
// Create a new agent
|
// Create a new agent
|
||||||
createAgent: async (config) => {
|
createAgent: async (config) => {
|
||||||
const response = await fetch(buildUrl(API_CONFIG.endpoints.createAgent), {
|
const response = await fetch(buildUrl(API_CONFIG.endpoints.createAgent), {
|
||||||
|
|||||||
1
webui/react-ui/src/utils/config.js
vendored
1
webui/react-ui/src/utils/config.js
vendored
@@ -20,6 +20,7 @@ export const API_CONFIG = {
|
|||||||
// Agent endpoints
|
// Agent endpoints
|
||||||
agents: '/api/agents',
|
agents: '/api/agents',
|
||||||
agentConfig: (name) => `/api/agent/${name}/config`,
|
agentConfig: (name) => `/api/agent/${name}/config`,
|
||||||
|
agentConfigMetadata: '/api/agent/config/metadata',
|
||||||
createAgent: '/create',
|
createAgent: '/create',
|
||||||
deleteAgent: (name) => `/delete/${name}`,
|
deleteAgent: (name) => `/delete/${name}`,
|
||||||
pauseAgent: (name) => `/pause/${name}`,
|
pauseAgent: (name) => `/pause/${name}`,
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
|
|||||||
// New API endpoints for getting and updating agent configuration
|
// New API endpoints for getting and updating agent configuration
|
||||||
webapp.Get("/api/agent/:name/config", app.GetAgentConfig(pool))
|
webapp.Get("/api/agent/:name/config", app.GetAgentConfig(pool))
|
||||||
webapp.Put("/api/agent/:name/config", app.UpdateAgentConfig(pool))
|
webapp.Put("/api/agent/:name/config", app.UpdateAgentConfig(pool))
|
||||||
|
|
||||||
|
// Metadata endpoint for agent configuration fields
|
||||||
|
webapp.Get("/api/agent/config/metadata", app.GetAgentConfigMeta())
|
||||||
|
|
||||||
webapp.Post("/action/:name/run", app.ExecuteAction(pool))
|
webapp.Post("/action/:name/run", app.ExecuteAction(pool))
|
||||||
webapp.Get("/actions", app.ListActions())
|
webapp.Get("/actions", app.ListActions())
|
||||||
@@ -190,13 +193,13 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
|
|||||||
}
|
}
|
||||||
statuses[a] = !agent.Paused()
|
statuses[a] = !agent.Paused()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"Agents": agents,
|
"Agents": agents,
|
||||||
"AgentCount": len(agents),
|
"AgentCount": len(agents),
|
||||||
"Actions": len(services.AvailableActions),
|
"Actions": len(services.AvailableActions),
|
||||||
"Connectors": len(services.AvailableConnectors),
|
"Connectors": len(services.AvailableConnectors),
|
||||||
"Status": statuses,
|
"Status": statuses,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -206,9 +209,9 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
|
|||||||
if history == nil {
|
if history == nil {
|
||||||
history = &state.Status{ActionResults: []types.ActionState{}}
|
history = &state.Status{ActionResults: []types.ActionState{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"Name": c.Params("name"),
|
"Name": c.Params("name"),
|
||||||
"History": Reverse(history.Results()),
|
"History": Reverse(history.Results()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user