fix(brain): resolve direct AX findings
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
a422eb1b6b
commit
ed842122a2
4 changed files with 101 additions and 9 deletions
|
|
@ -7,7 +7,6 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
|
|
@ -33,16 +32,15 @@ var _ coremcp.Subsystem = (*DirectSubsystem)(nil)
|
|||
// sub := brain.NewDirect()
|
||||
// sub.RegisterTools(server)
|
||||
func NewDirect() *DirectSubsystem {
|
||||
apiURL := os.Getenv("CORE_BRAIN_URL")
|
||||
apiURL := core.Env("CORE_BRAIN_URL")
|
||||
if apiURL == "" {
|
||||
apiURL = "https://api.lthn.sh"
|
||||
}
|
||||
|
||||
apiKey := os.Getenv("CORE_BRAIN_KEY")
|
||||
apiKey := core.Env("CORE_BRAIN_KEY")
|
||||
keyPath := ""
|
||||
if apiKey == "" {
|
||||
home, _ := os.UserHomeDir()
|
||||
keyPath = brainKeyPath(home)
|
||||
keyPath = brainKeyPath(brainHomeDir())
|
||||
if keyPath != "" {
|
||||
if r := fs.Read(keyPath); r.OK {
|
||||
apiKey = core.Trim(r.Value.(string))
|
||||
|
|
@ -104,6 +102,13 @@ func brainKeyPath(home string) string {
|
|||
return core.JoinPath(core.TrimSuffix(home, "/"), ".claude", "brain.key")
|
||||
}
|
||||
|
||||
func brainHomeDir() string {
|
||||
if home := core.Env("CORE_HOME"); home != "" {
|
||||
return home
|
||||
}
|
||||
return core.Env("DIR_HOME")
|
||||
}
|
||||
|
||||
func (s *DirectSubsystem) apiCall(ctx context.Context, method, path string, body any) (map[string]any, error) {
|
||||
if s.apiKey == "" {
|
||||
return nil, core.E("brain.apiCall", "no API key (set CORE_BRAIN_KEY or create ~/.claude/brain.key)", nil)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ package brain
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// newTestDirect returns a DirectSubsystem wired to the given test server.
|
||||
|
|
@ -59,7 +59,7 @@ func TestNewDirect_Good_KeyFromFile(t *testing.T) {
|
|||
t.Setenv("CORE_BRAIN_KEY", "")
|
||||
|
||||
tmpHome := t.TempDir()
|
||||
t.Setenv("HOME", tmpHome)
|
||||
t.Setenv("CORE_HOME", tmpHome)
|
||||
keyDir := filepath.Join(tmpHome, ".claude")
|
||||
require.True(t, fs.EnsureDir(keyDir).OK)
|
||||
require.True(t, fs.Write(filepath.Join(keyDir, "brain.key"), " file-key-456 \n").OK)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package brain
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"dappco.re/go/core/api"
|
||||
"dappco.re/go/core/api/pkg/provider"
|
||||
|
|
@ -14,6 +15,11 @@ import (
|
|||
|
||||
// BrainProvider wraps the brain Subsystem as a service provider with REST
|
||||
// endpoints. It delegates to the same IDE bridge that the MCP tools use.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// provider := brain.NewProvider(bridge, hub)
|
||||
// provider.RegisterRoutes(router.Group("/api/brain"))
|
||||
type BrainProvider struct {
|
||||
bridge *ide.Bridge
|
||||
hub *ws.Hub
|
||||
|
|
@ -294,13 +300,23 @@ func (p *BrainProvider) list(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
limit := 0
|
||||
if rawLimit := c.Query("limit"); rawLimit != "" {
|
||||
parsedLimit, err := strconv.Atoi(rawLimit)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Fail("invalid_limit", "limit must be an integer"))
|
||||
return
|
||||
}
|
||||
limit = parsedLimit
|
||||
}
|
||||
|
||||
err := p.bridge.Send(ide.BridgeMessage{
|
||||
Type: "brain_list",
|
||||
Data: map[string]any{
|
||||
"project": c.Query("project"),
|
||||
"type": c.Query("type"),
|
||||
"agent_id": c.Query("agent_id"),
|
||||
"limit": c.Query("limit"),
|
||||
"limit": limit,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ import (
|
|||
// -- Input/Output types -------------------------------------------------------
|
||||
|
||||
// RememberInput is the input for brain_remember.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// input := brain.RememberInput{
|
||||
// Content: "Use core.Env for system paths.",
|
||||
// Type: "convention",
|
||||
// }
|
||||
type RememberInput struct {
|
||||
Content string `json:"content"`
|
||||
Type string `json:"type"`
|
||||
|
|
@ -25,6 +32,13 @@ type RememberInput struct {
|
|||
}
|
||||
|
||||
// RememberOutput is the output for brain_remember.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// output := brain.RememberOutput{
|
||||
// Success: true,
|
||||
// MemoryID: "mem_123",
|
||||
// }
|
||||
type RememberOutput struct {
|
||||
Success bool `json:"success"`
|
||||
MemoryID string `json:"memoryId,omitempty"`
|
||||
|
|
@ -32,6 +46,13 @@ type RememberOutput struct {
|
|||
}
|
||||
|
||||
// RecallInput is the input for brain_recall.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// input := brain.RecallInput{
|
||||
// Query: "core.Env conventions",
|
||||
// TopK: 5,
|
||||
// }
|
||||
type RecallInput struct {
|
||||
Query string `json:"query"`
|
||||
TopK int `json:"top_k,omitempty"`
|
||||
|
|
@ -39,6 +60,13 @@ type RecallInput struct {
|
|||
}
|
||||
|
||||
// RecallFilter holds optional filter criteria for brain_recall.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// filter := brain.RecallFilter{
|
||||
// Project: "agent",
|
||||
// Type: "convention",
|
||||
// }
|
||||
type RecallFilter struct {
|
||||
Project string `json:"project,omitempty"`
|
||||
Type any `json:"type,omitempty"`
|
||||
|
|
@ -47,6 +75,13 @@ type RecallFilter struct {
|
|||
}
|
||||
|
||||
// RecallOutput is the output for brain_recall.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// output := brain.RecallOutput{
|
||||
// Success: true,
|
||||
// Count: 1,
|
||||
// }
|
||||
type RecallOutput struct {
|
||||
Success bool `json:"success"`
|
||||
Count int `json:"count"`
|
||||
|
|
@ -54,6 +89,14 @@ type RecallOutput struct {
|
|||
}
|
||||
|
||||
// Memory is a single memory entry returned by recall or list.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// memory := brain.Memory{
|
||||
// ID: "mem_123",
|
||||
// Type: "convention",
|
||||
// Content: "Use core.Env for system paths.",
|
||||
// }
|
||||
type Memory struct {
|
||||
ID string `json:"id"`
|
||||
AgentID string `json:"agent_id"`
|
||||
|
|
@ -69,12 +112,26 @@ type Memory struct {
|
|||
}
|
||||
|
||||
// ForgetInput is the input for brain_forget.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// input := brain.ForgetInput{
|
||||
// ID: "mem_123",
|
||||
// Reason: "superseded",
|
||||
// }
|
||||
type ForgetInput struct {
|
||||
ID string `json:"id"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// ForgetOutput is the output for brain_forget.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// output := brain.ForgetOutput{
|
||||
// Success: true,
|
||||
// Forgotten: "mem_123",
|
||||
// }
|
||||
type ForgetOutput struct {
|
||||
Success bool `json:"success"`
|
||||
Forgotten string `json:"forgotten"`
|
||||
|
|
@ -82,6 +139,13 @@ type ForgetOutput struct {
|
|||
}
|
||||
|
||||
// ListInput is the input for brain_list.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// input := brain.ListInput{
|
||||
// Project: "agent",
|
||||
// Limit: 20,
|
||||
// }
|
||||
type ListInput struct {
|
||||
Project string `json:"project,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
|
|
@ -90,6 +154,13 @@ type ListInput struct {
|
|||
}
|
||||
|
||||
// ListOutput is the output for brain_list.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// output := brain.ListOutput{
|
||||
// Success: true,
|
||||
// Count: 2,
|
||||
// }
|
||||
type ListOutput struct {
|
||||
Success bool `json:"success"`
|
||||
Count int `json:"count"`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue