Add managed_runtime.go (paths, config validation, identity, runtime file changes, status checks) and shell_command.go (quoting, PowerShell encoding, command parsing) and move the four providers onto them. Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
232 lines
5.7 KiB
Go
232 lines
5.7 KiB
Go
package plugins
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
const (
|
|
geminiAgentKey = "gemini"
|
|
geminiConfigFile = "settings.json"
|
|
geminiGuardScript = "guard.js"
|
|
geminiAuditScript = "hook.js"
|
|
geminiGuardHookName = "elydora-guard"
|
|
geminiAuditHookName = "elydora-audit"
|
|
geminiHookTimeout = float64(10_000)
|
|
geminiOwnedFileMarker = "// Managed by Elydora"
|
|
)
|
|
|
|
var geminiManagedEvents = [...]string{"BeforeTool", "AfterTool"}
|
|
|
|
type geminiManagedRemoval struct {
|
|
event string
|
|
groupIndex int
|
|
handlerIndexes []int
|
|
removeGroup bool
|
|
}
|
|
|
|
type geminiRuntimeContract = managedRuntimeContract
|
|
|
|
func buildGeminiGroup(runtimePath, scriptPath, name string) (map[string]any, error) {
|
|
command, err := buildGeminiCommand(runtimePath, scriptPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]any{
|
|
"hooks": []any{map[string]any{
|
|
"type": "command", "name": name, "command": command,
|
|
"timeout": geminiHookTimeout,
|
|
}},
|
|
}, nil
|
|
}
|
|
|
|
func exactManagedGeminiGroup(group map[string]any) bool {
|
|
_, hasHooks := group["hooks"]
|
|
return len(group) == 1 && hasHooks
|
|
}
|
|
|
|
func currentManagedGeminiReference(
|
|
handler map[string]any,
|
|
scriptName string,
|
|
hookName string,
|
|
) (*geminiRuntimeReference, error) {
|
|
if len(handler) != 4 || handler["type"] != "command" ||
|
|
handler["name"] != hookName || handler["timeout"] != geminiHookTimeout {
|
|
return nil, nil
|
|
}
|
|
command, ok := handler["command"].(string)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return geminiRuntimeReferenceForCommand(command, scriptName, false)
|
|
}
|
|
|
|
func legacyManagedGeminiReference(
|
|
handler map[string]any,
|
|
scriptName string,
|
|
) (*geminiRuntimeReference, error) {
|
|
if len(handler) != 2 || handler["type"] != "command" {
|
|
return nil, nil
|
|
}
|
|
command, ok := handler["command"].(string)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
return geminiRuntimeReferenceForCommand(command, scriptName, true)
|
|
}
|
|
|
|
func managedGeminiReference(
|
|
handler map[string]any,
|
|
scriptName string,
|
|
hookName string,
|
|
includeLegacy bool,
|
|
) (*geminiRuntimeReference, error) {
|
|
reference, err := currentManagedGeminiReference(handler, scriptName, hookName)
|
|
if err != nil || reference != nil || !includeLegacy {
|
|
return reference, err
|
|
}
|
|
return legacyManagedGeminiReference(handler, scriptName)
|
|
}
|
|
|
|
func managedGeminiHooksEnabled(controls geminiHookControls) bool {
|
|
if !controls.enabled {
|
|
return false
|
|
}
|
|
for _, entry := range controls.disabled {
|
|
if entry == geminiGuardHookName || entry == geminiAuditHookName {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func disabledManagedGeminiEntries(
|
|
controls geminiHookControls,
|
|
) ([]string, error) {
|
|
disabled := make([]string, 0)
|
|
for _, entry := range controls.disabled {
|
|
managed := entry == geminiGuardHookName || entry == geminiAuditHookName
|
|
for _, script := range []string{geminiGuardScript, geminiAuditScript} {
|
|
reference, err := geminiRuntimeReferenceForCommand(entry, script, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
managed = managed || reference != nil
|
|
}
|
|
if managed {
|
|
disabled = append(disabled, entry)
|
|
}
|
|
}
|
|
return disabled, nil
|
|
}
|
|
|
|
func managedGeminiRemovals(
|
|
hooks geminiHooks,
|
|
agentID string,
|
|
) ([]geminiManagedRemoval, error) {
|
|
removals := make([]geminiManagedRemoval, 0)
|
|
for _, contract := range []struct{ event, script, name string }{
|
|
{"BeforeTool", geminiGuardScript, geminiGuardHookName},
|
|
{"AfterTool", geminiAuditScript, geminiAuditHookName},
|
|
} {
|
|
for groupIndex, groupValue := range hooks[contract.event] {
|
|
group := groupValue.(map[string]any)
|
|
handlers := group["hooks"].([]any)
|
|
indexes := make([]int, 0)
|
|
for handlerIndex, handlerValue := range handlers {
|
|
handler := handlerValue.(map[string]any)
|
|
reference, err := managedGeminiReference(
|
|
handler,
|
|
contract.script,
|
|
contract.name,
|
|
true,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if reference != nil &&
|
|
(agentID == "" || sameManagedAgentID(reference.agentID, agentID)) {
|
|
indexes = append(indexes, handlerIndex)
|
|
}
|
|
}
|
|
if len(indexes) > 0 {
|
|
removals = append(removals, geminiManagedRemoval{
|
|
contract.event,
|
|
groupIndex,
|
|
indexes,
|
|
exactManagedGeminiGroup(group) && len(indexes) == len(handlers),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return removals, nil
|
|
}
|
|
|
|
func geminiReferencesForEvent(
|
|
groups []any,
|
|
scriptName string,
|
|
hookName string,
|
|
) (map[string][]geminiRuntimeReference, error) {
|
|
result := map[string][]geminiRuntimeReference{}
|
|
for _, groupValue := range groups {
|
|
group := groupValue.(map[string]any)
|
|
if !exactManagedGeminiGroup(group) {
|
|
continue
|
|
}
|
|
for _, handlerValue := range group["hooks"].([]any) {
|
|
reference, err := currentManagedGeminiReference(
|
|
handlerValue.(map[string]any),
|
|
scriptName,
|
|
hookName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if reference != nil {
|
|
key := managedReferenceKey(reference.agentID)
|
|
result[key] = append(result[key], *reference)
|
|
}
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func geminiRuntimeContracts(
|
|
hooks geminiHooks,
|
|
) ([]geminiRuntimeContract, error) {
|
|
guards, err := geminiReferencesForEvent(
|
|
hooks["BeforeTool"],
|
|
geminiGuardScript,
|
|
geminiGuardHookName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
audits, err := geminiReferencesForEvent(
|
|
hooks["AfterTool"],
|
|
geminiAuditScript,
|
|
geminiAuditHookName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keys := make([]string, 0, len(guards))
|
|
for key := range guards {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
contracts := make([]geminiRuntimeContract, 0, len(keys))
|
|
for _, key := range keys {
|
|
guard := guards[key]
|
|
audit := audits[key]
|
|
if len(guard) != 1 || len(audit) != 1 {
|
|
continue
|
|
}
|
|
contracts = append(contracts, geminiRuntimeContract{
|
|
agentID: guard[0].agentID,
|
|
guardPath: guard[0].scriptPath,
|
|
auditPath: audit[0].scriptPath,
|
|
})
|
|
}
|
|
return contracts, nil
|
|
}
|