fix(cli): confine agent runtime paths

This commit is contained in:
ZacharyZhang-NY committed 2026-07-19 16:26:00 -04:00
1 parent c9d1075585
commit 7bf84a35e0
10 files changed
+399 -51

No files matched your search

+74 -30
View File
@@ -92,16 +92,13 @@ func cmdInstall(args []string) {
}
// Generate guard script (PreToolUse — freeze enforcement)
guardScriptPath, err := guardScriptPathForAgent(*agentID)
agentDirectory, err := plugins.PrepareAgentRuntimeDirectory(*agentID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
guardScriptPath := filepath.Join(agentDirectory, "guard.js")
guardScript := plugins.GenerateGuardScript(*agent, *agentID)
if err := os.MkdirAll(filepath.Dir(guardScriptPath), 0755); err != nil {
fmt.Fprintf(os.Stderr, "Error creating guard script directory: %v\n", err)
os.Exit(1)
}
if err := os.WriteFile(guardScriptPath, []byte(guardScript), 0755); err != nil {
fmt.Fprintf(os.Stderr, "Error writing guard script: %v\n", err)
os.Exit(1)
@@ -149,13 +146,38 @@ func cmdUninstall(args []string) {
os.Exit(1)
}
runtimeRoot, err := plugins.AgentRuntimeRoot()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
if _, err := plugins.RequirePhysicalDirectory(runtimeRoot); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Resolve agentID: if not given, scan ~/.elydora/*/config.json for matching agent_name
resolvedAgentID := *agentID
if resolvedAgentID == "" {
resolvedAgentID = findAgentIDByName(*agent)
if resolvedAgentID == "" {
fmt.Fprintf(os.Stderr, "Warning: could not find agent ID for %q in ~/.elydora/*/config.json\n", *agent)
resolvedAgentID, err = findAgentIDByName(*agent)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
if resolvedAgentID == "" {
fmt.Fprintf(os.Stderr, "Error: could not find agent ID for %q in ~/.elydora/*/config.json; pass --agent-id explicitly\n", *agent)
os.Exit(1)
}
}
agentDirectory, err := plugins.ResolveAgentRuntimeDirectory(resolvedAgentID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
agentDirectoryExists, err := plugins.RequirePhysicalDirectory(agentDirectory)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
if err := plugin.Uninstall(resolvedAgentID); err != nil {
@@ -164,49 +186,71 @@ func cmdUninstall(args []string) {
}
// Remove the per-agent directory
if resolvedAgentID != "" {
home, err := os.UserHomeDir()
if err == nil {
agentDir := filepath.Join(home, ".elydora", resolvedAgentID)
if err := os.RemoveAll(agentDir); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to remove agent directory %s: %v\n", agentDir, err)
} else {
fmt.Printf(" Removed agent directory: %s\n", agentDir)
}
if agentDirectoryExists {
if err := os.RemoveAll(agentDirectory); err != nil {
fmt.Fprintf(os.Stderr, "Error removing agent directory %s: %v\n", agentDirectory, err)
os.Exit(1)
}
fmt.Printf(" Removed agent directory: %s\n", agentDirectory)
}
}
// findAgentIDByName scans ~/.elydora/*/config.json for a config whose agent_name
// matches the given name and returns the directory name (agent ID).
func findAgentIDByName(agentName string) string {
home, err := os.UserHomeDir()
func findAgentIDByName(agentName string) (string, error) {
elydoraDir, err := plugins.AgentRuntimeRoot()
if err != nil {
return ""
return "", err
}
exists, err := plugins.RequirePhysicalDirectory(elydoraDir)
if err != nil {
return "", err
}
if !exists {
return "", nil
}
elydoraDir := filepath.Join(home, ".elydora")
entries, err := os.ReadDir(elydoraDir)
if err != nil {
return ""
return "", fmt.Errorf("read Elydora runtime root %s: %w", elydoraDir, err)
}
foundAgentID := ""
for _, entry := range entries {
if !entry.IsDir() {
if entry.Type()&os.ModeSymlink != 0 || !entry.IsDir() {
continue
}
configPath := filepath.Join(elydoraDir, entry.Name(), "config.json")
data, err := os.ReadFile(configPath)
if err != nil {
if os.IsNotExist(err) {
continue
}
if err != nil {
return "", fmt.Errorf("read agent config %s: %w", configPath, err)
}
var config map[string]interface{}
if err := json.Unmarshal(data, &config); err != nil {
continue
return "", fmt.Errorf("parse agent config %s: %w", configPath, err)
}
if name, _ := config["agent_name"].(string); name == agentName {
return entry.Name()
storedAgentID, ok := config["agent_id"].(string)
if !ok || storedAgentID == "" {
return "", fmt.Errorf("agent config %s has an invalid agent_id", configPath)
}
if storedAgentID != entry.Name() {
return "", fmt.Errorf("agent config %s crosses its runtime directory", configPath)
}
if _, err := plugins.ResolveAgentRuntimeDirectory(storedAgentID); err != nil {
return "", fmt.Errorf("validate agent config %s: %w", configPath, err)
}
if foundAgentID != "" {
return "", fmt.Errorf(
"multiple %q agent runtimes found; pass --agent-id explicitly",
agentName,
)
}
foundAgentID = storedAgentID
}
}
return ""
return foundAgentID, nil
}
// ---------------------------------------------------------------------------
@@ -285,9 +329,9 @@ func sortedAgentNames() []string {
// guardScriptPathForAgent returns the path to ~/.elydora/<agentId>/guard.js.
func guardScriptPathForAgent(agentId string) (string, error) {
home, err := os.UserHomeDir()
agentDirectory, err := plugins.ResolveAgentRuntimeDirectory(agentId)
if err != nil {
return "", fmt.Errorf("resolve home directory: %w", err)
return "", err
}
return filepath.Join(home, ".elydora", agentId, "guard.js"), nil
return filepath.Join(agentDirectory, "guard.js"), nil
}