claude-code: act on the review of the nox-mesh plugin
Refuse paths with empty, dot or parent segments and a file that is also a directory; take an item only when its key says what it is; write the view under its lock; expand nodes all and check node names; merge the plugin's entries into the operator's own; render every file past one that fails; in the home, never take over the person's file, never write through a symbolic link, keep a deleted file deleted, keep the kind's directory; and refuse settings that would deny the console or the marketplace.
This commit is contained in:
@@ -41,6 +41,11 @@ func strArg(a map[string]any, k string) string {
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
// targetNodes reads the `nodes` argument: absent is this node, "all" every node running claude-code, else a list.
|
||||
func targetNodes(a map[string]any) ([]string, error) {
|
||||
return ExpandNodes(nodesOf(a["nodes"]), nodesRunningMe)
|
||||
}
|
||||
|
||||
// scopeOf reads the `scope` argument; absent is the mesh, which is what a registration is most often for.
|
||||
func scopeOf(a map[string]any) string {
|
||||
if s := strArg(a, "scope"); s != "" {
|
||||
@@ -76,7 +81,7 @@ func filesOf(kind, name string, a map[string]any) (map[string]string, error) {
|
||||
|
||||
func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
scopeArg := str(`mesh (every node; the default), node (the nodes given, or this one) or home (the operator account's own ~/.claude on the nodes given, or this one)`)
|
||||
nodesArg := str(`for the node and home scopes: a comma-separated list of nodes; absent is this node`)
|
||||
nodesArg := str(`for the node and home scopes: "all" for every node running claude-code, or a comma-separated list; absent is this node`)
|
||||
var out []stdio.Tool
|
||||
for _, k := range kindTools {
|
||||
k := k
|
||||
@@ -112,14 +117,22 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
}
|
||||
it := Item{Kind: k.kind, Name: name, Scope: scopeOf(a), Files: files,
|
||||
Event: strArg(a, "event"), Matcher: strArg(a, "matcher"), Command: strArg(a, "command")}
|
||||
return Register(p, it, nodesOf(a["nodes"]), false, state, view, writeManaged)
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Register(p, it, nodes, false, state, view, writeManaged)
|
||||
}},
|
||||
stdio.Tool{Name: "claude_code_" + k.tool + "_unregister",
|
||||
Description: "Remove a " + k.kind + " registered through this module, at its scope. At home, only what the mesh placed is removed, and not if it was changed by hand since.",
|
||||
Input: map[string]any{"name": str("the name"), "scope": scopeArg, "nodes": nodesArg},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
it := Item{Kind: k.kind, Name: strArg(a, "name"), Scope: scopeOf(a)}
|
||||
return Register(p, it, nodesOf(a["nodes"]), true, state, view, writeManaged)
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Register(p, it, nodes, true, state, view, writeManaged)
|
||||
}},
|
||||
)
|
||||
}
|
||||
@@ -160,7 +173,11 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
if len(given) == 0 {
|
||||
return nil, errors.New("no settings given; to remove a scope's settings, claude_code_settings_clear")
|
||||
}
|
||||
return SetSettings(p, scopeOf(a), nodesOf(a["nodes"]), func(held map[string]any) map[string]any {
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return SetSettings(p, scopeOf(a), nodes, func(held map[string]any) map[string]any {
|
||||
if boolArg(a, "replace") {
|
||||
return given
|
||||
}
|
||||
@@ -172,7 +189,11 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
Input: map[string]any{"scope": settingsScope, "nodes": nodesArg},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
it := Item{Kind: KindSettings, Name: SettingsName, Scope: scopeOf(a)}
|
||||
return Register(p, it, nodesOf(a["nodes"]), true, state, view, writeManaged)
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Register(p, it, nodes, true, state, view, writeManaged)
|
||||
}},
|
||||
stdio.Tool{Name: "claude_code_permission_add",
|
||||
Description: "Add a permission rule to Claude Code's managed settings, for every node or some: allow (runs without asking), ask (always asks) or deny (never runs). A rule is a tool and an optional specifier, e.g. Bash(git status:*), Read(./secrets/**), mcp__mesh__mesh_call.",
|
||||
@@ -182,7 +203,11 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
if (list != "allow" && list != "ask" && list != "deny") || rule == "" {
|
||||
return nil, errors.New("list is allow, ask or deny, and a rule is needed")
|
||||
}
|
||||
return SetSettings(p, scopeOf(a), nodesOf(a["nodes"]), func(held map[string]any) map[string]any {
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return SetSettings(p, scopeOf(a), nodes, func(held map[string]any) map[string]any {
|
||||
return mergeSettings(held, map[string]any{"permissions": map[string]any{list: []any{rule}}})
|
||||
}, state, view)
|
||||
}},
|
||||
@@ -191,7 +216,11 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
Input: map[string]any{"list": str("allow, ask or deny"), "rule": str("the rule"), "scope": settingsScope, "nodes": nodesArg},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
list, rule := strArg(a, "list"), strArg(a, "rule")
|
||||
return SetSettings(p, scopeOf(a), nodesOf(a["nodes"]), func(held map[string]any) map[string]any {
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return SetSettings(p, scopeOf(a), nodes, func(held map[string]any) map[string]any {
|
||||
perms, _ := held["permissions"].(map[string]any)
|
||||
rules, _ := perms[list].([]any)
|
||||
if len(rules) == 0 {
|
||||
@@ -266,7 +295,11 @@ func configTools(p Paths, state ConfigState, view *ConfigView) []stdio.Tool {
|
||||
if it.Scope == ScopeHome {
|
||||
return nil, errors.New("it is already at home; import it to the mesh or node scope")
|
||||
}
|
||||
return Register(p, it, nodesOf(a["nodes"]), false, state, view, writeManaged)
|
||||
nodes, err := targetNodes(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Register(p, it, nodes, false, state, view, writeManaged)
|
||||
}},
|
||||
)
|
||||
return out
|
||||
@@ -283,6 +316,8 @@ func SetSettings(p Paths, scope string, nodes []string, change func(held map[str
|
||||
nodes = []string{""}
|
||||
} else if len(nodes) == 0 {
|
||||
nodes = []string{p.Node}
|
||||
} else if problem := nodesProblem(nodes); problem != "" {
|
||||
return map[string]any{"set": false, "reason": problem}, nil
|
||||
}
|
||||
answers := map[string]any{}
|
||||
for _, n := range nodes {
|
||||
|
||||
Reference in New Issue
Block a user