This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git

commit 6582ca032bb4d9c1e12b4e90b3fe3b7918e384a4
Author: Yang Luo <[email protected]>
AuthorDate: Fri Sep 4 20:25:53 2026 +0800

    fix(agents): let a link name any agent and adopt an existing Hermes plugin
---
 agentpatch/claude_desktop.go | 18 ++++++++++++++++++
 agentpatch/hermes_agent.go   | 10 +++++-----
 object/import_link.go        | 26 +++++++++++++++++---------
 3 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/agentpatch/claude_desktop.go b/agentpatch/claude_desktop.go
index c758900..7cc0985 100644
--- a/agentpatch/claude_desktop.go
+++ b/agentpatch/claude_desktop.go
@@ -146,6 +146,9 @@ func (p claudeDesktopPatcher) Status(target Target) 
(Status, error) {
        if !exists || !hasClaudeDesktopServer(config) {
                return Status{Detail: "MCP recorder is not registered"}, nil
        }
+       if !isCurrentClaudeDesktopServer(config) {
+               return Status{Detail: "MCP recorder needs refresh"}, nil
+       }
        if runtime.GOOS != "windows" {
                return Status{Patched: true, Detail: "MCP recorder registered; 
restart Claude Desktop to apply it"}, nil
        }
@@ -211,6 +214,21 @@ func hasClaudeDesktopServer(config map[string]any) bool {
        return stringIndex(args, mcpserver.Subcommand) >= 0 && 
strings.Contains(strings.Join(args, " "), "--records-url")
 }
 
+// isCurrentClaudeDesktopServer narrows hasClaudeDesktopServer to an entry this
+// Gateway would write now. The broad test stays what Unpatch removes by.
+func isCurrentClaudeDesktopServer(config map[string]any) bool {
+       servers, ok := objectAt(config["mcpServers"])
+       if !ok {
+               return false
+       }
+       entry, ok := objectAt(servers[claudeDesktopServerName])
+       if !ok {
+               return false
+       }
+       command, _ := entry["command"].(string)
+       return argsAreCurrent(command, stringArray(entry["args"]))
+}
+
 func readJSONObject(changes *ChangeSet, path string) (map[string]any, error) {
        data, err := changes.ReadFile(path)
        if err != nil {
diff --git a/agentpatch/hermes_agent.go b/agentpatch/hermes_agent.go
index b97d6de..3f3704a 100644
--- a/agentpatch/hermes_agent.go
+++ b/agentpatch/hermes_agent.go
@@ -89,16 +89,16 @@ func (p hermesPatcher) Patch(target Target) error {
        if err != nil {
                return err
        }
-       applied := IsApplied(target)
        if info, statErr := os.Stat(layout.pluginDir); statErr == nil {
                if !info.IsDir() {
                        return fmt.Errorf("%s exists and is not a directory", 
layout.pluginDir)
                }
-               if !applied {
-                       return fmt.Errorf("%s already exists and is not owned 
by this Gateway patch state", layout.pluginDir)
-               }
+               // What the directory holds decides this, not whether this 
Gateway's own
+               // state remembers writing it. A reinstall or a move to another 
data
+               // directory leaves a previous Gateway's observer behind, and 
refusing
+               // that one left Hermes unmonitorable with nothing to do about 
it.
                if hermesPluginForeign(layout.pluginDir) {
-                       return fmt.Errorf("%s no longer contains a 
Gateway-owned observer; refusing to refresh it", layout.pluginDir)
+                       return fmt.Errorf("%s holds an observer that is not 
Gateway's; remove that directory to let Gateway install its own", 
layout.pluginDir)
                }
        } else if !os.IsNotExist(statErr) {
                return statErr
diff --git a/object/import_link.go b/object/import_link.go
index 97d6b97..bd9f3a0 100644
--- a/object/import_link.go
+++ b/object/import_link.go
@@ -21,6 +21,8 @@ import (
        "net/url"
        "strings"
        "unicode/utf8"
+
+       "github.com/apache/casbin-gateway/agent"
 )
 
 // The resources an "add this to Gateway" link can carry. The names are CC
@@ -33,22 +35,25 @@ const (
        ImportResourceSkill    = "skill"
 )
 
-// importLinkAgents maps an app a link names to the agents on this host that
-// read that app's own configuration. Gateway manages agents CC Switch does not
-// and the other way round, so an app with nothing here is reported back rather
-// than silently dropped: the page says which part of the link it cannot land.
+// importLinkAgents translates the app names CC Switch links use into Gateway's
+// own agent ids. Only the ones that differ are here: an app a link names by an
+// id Gateway already knows resolves on its own, so every agent in the 
catalogue
+// can be the target of a link without an entry of its own.
 //
 // Two ids appear where the same files are read under both — the Codex CLI and
-// ChatGPT Desktop share ~/.codex, opencode and its desktop app share
-// ~/.config/opencode — because the listing keeps one entry per location, under
-// whichever of the two was found first.
+// ChatGPT Desktop share ~/.codex, Cursor and its CLI share ~/.cursor, opencode
+// and its desktop app share ~/.config/opencode — because the listing keeps one
+// entry per location, under whichever of the two was found first.
 var importLinkAgents = map[string][]string{
        "claude":   {"claude-code"},
        "codex":    {"codex-cli", "codex"},
+       "cursor":   {"cursor", "cursor-agent"},
        "gemini":   {"gemini-cli"},
-       "opencode": {"opencode", "opencode-desktop"},
-       "openclaw": {"openclaw"},
        "hermes":   {"hermes-agent"},
+       "iflow":    {"iflow-cli"},
+       "kimi":     {"kimi-code"},
+       "opencode": {"opencode", "opencode-desktop"},
+       "qwen":     {"qwen-code"},
 }
 
 // ImportLink is what one link carries, read and handed to the page. Nothing in
@@ -242,6 +247,9 @@ func linkTargets(apps []string) (targets []string, unknown 
[]string) {
                        continue
                }
                agentIds, known := importLinkAgents[app]
+               if !known && agent.IsKnownAgentId(app) {
+                       agentIds, known = []string{app}, true
+               }
                if !known {
                        unknown = append(unknown, app)
                        continue


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to