Files
Elydora-Go-SDK/cmd/elydora/plugins/grok_io.go
T

182 lines
4.7 KiB
Go

package plugins
import (
"fmt"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
)
func grokHomeDirectory() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("resolve home directory: %w", err)
}
return home, nil
}
func grokRuntimeRoot() (string, error) {
home, err := grokHomeDirectory()
if err != nil {
return "", err
}
return filepath.Join(home, ".elydora"), nil
}
func grokConfigPath() (string, error) {
home, err := grokHomeDirectory()
if err != nil {
return "", err
}
grokHome := os.Getenv("GROK_HOME")
if grokHome == "" {
grokHome = filepath.Join(home, ".grok")
}
// #nosec G703 -- GROK_HOME is an explicit local-user configuration path.
return filepath.Join(grokHome, "hooks", grokConfigFile), nil
}
func readGrokConfig() (grokDocument, error) {
configPath, err := grokConfigPath()
if err != nil {
return grokDocument{}, err
}
root, exists, err := readHookJSONObject(configPath, "Grok hooks config")
if err != nil {
return grokDocument{}, err
}
hooks, err := readGrokHooks(root)
if err != nil {
return grokDocument{}, err
}
return grokDocument{exists: exists, configPath: configPath, root: root, hooks: hooks}, nil
}
func quoteGrokWindowsArgument(value string) string {
return `"` + strings.ReplaceAll(value, `"`, `\"`) + `"`
}
func buildGrokCommand(runtimePath, scriptPath string) string {
if runtime.GOOS == "windows" {
return quoteGrokWindowsArgument(runtimePath) + " " + quoteGrokWindowsArgument(scriptPath)
}
return quotePOSIXArgument(runtimePath) + " " + quotePOSIXArgument(scriptPath)
}
func buildGrokHandler(command string) map[string]any {
return map[string]any{
"type": "command",
"command": command,
"timeout": grokHookTimeout,
}
}
func buildGrokGroup(handler map[string]any) grokGroup {
return grokGroup{object: map[string]any{}, handlers: []map[string]any{handler}}
}
func managedGrokIDs(groups []grokGroup, scriptName, runtimeRoot string) map[string]string {
result := map[string]string{}
for _, group := range groups {
if _, hasMatcher := group.object["matcher"]; hasMatcher {
continue
}
for _, handler := range group.handlers {
agentID, managed := managedGrokAgentID(handler, scriptName, runtimeRoot)
if managed {
key := agentID
if runtime.GOOS == "windows" {
key = strings.ToLower(key)
}
result[key] = agentID
}
}
}
return result
}
func grokRuntimeContracts(hooks grokHooks, runtimeRoot string) []grokRuntimeContract {
guards := managedGrokIDs(hooks["PreToolUse"], grokGuardScript, runtimeRoot)
audits := managedGrokIDs(hooks["PostToolUse"], grokAuditScript, runtimeRoot)
keys := make([]string, 0, len(guards))
for key := range guards {
if _, exists := audits[key]; exists {
keys = append(keys, key)
}
}
sort.Strings(keys)
contracts := make([]grokRuntimeContract, 0, len(keys))
for _, key := range keys {
agentID := guards[key]
contracts = append(contracts, grokRuntimeContract{
agentID: agentID,
guardPath: filepath.Join(runtimeRoot, agentID, grokGuardScript),
auditPath: filepath.Join(runtimeRoot, agentID, grokAuditScript),
})
}
return contracts
}
func grokRuntimeScriptsExist(contracts []grokRuntimeContract, runtimeRoot string) (bool, error) {
entries, err := os.ReadDir(runtimeRoot)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("read Elydora runtime directory at %s: %w", runtimeRoot, err)
}
for _, contract := range contracts {
var entryName string
for _, entry := range entries {
if entry.IsDir() && sameGrokAgentID(entry.Name(), contract.agentID) {
entryName = entry.Name()
break
}
}
if entryName == "" {
continue
}
agentDirectory := filepath.Join(runtimeRoot, entryName)
guardPath := filepath.Join(agentDirectory, grokGuardScript)
auditPath := filepath.Join(agentDirectory, grokAuditScript)
if !sameGrokPath(guardPath, contract.guardPath) ||
!sameGrokPath(auditPath, contract.auditPath) {
continue
}
configPath := filepath.Join(agentDirectory, "config.json")
config, exists, err := readHookJSONObject(configPath, "Elydora runtime config")
if err != nil {
return false, err
}
if !exists {
continue
}
agentName, ok := config["agent_name"].(string)
if !ok {
return false, fmt.Errorf(
`Elydora runtime config at %s field "agent_name" must be a string`,
configPath,
)
}
if agentName != grokAgentKey {
continue
}
guardExists, err := regularFileExists(
guardPath, "Elydora guard runtime",
)
if err != nil {
return false, err
}
auditExists, err := regularFileExists(
auditPath, "Elydora audit runtime",
)
if err != nil {
return false, err
}
return guardExists && auditExists, nil
}
return false, nil
}