xworkmate-bridge/internal/mounts/config.go
2026-04-09 09:49:48 +08:00

151 lines
4.1 KiB
Go

package mounts
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
const (
codexManagedMCPBlockStart = "# BEGIN XWORKMATE MANAGED MCP BLOCK"
codexManagedMCPBlockEnd = "# END XWORKMATE MANAGED MCP BLOCK"
opencodeManagedMCPBlockStart = "# BEGIN XWORKMATE MANAGED MCP BLOCK"
opencodeManagedMCPBlockEnd = "# END XWORKMATE MANAGED MCP BLOCK"
)
var mcpServerSectionPattern = regexp.MustCompile(
`(?m)^\[mcp_servers\.[^\]]+\]`,
)
func countMCPSections(content string) int {
return len(mcpServerSectionPattern.FindAllStringIndex(content, -1))
}
func defaultCodexHome() string {
home, err := os.UserHomeDir()
if err != nil || strings.TrimSpace(home) == "" {
return ""
}
return filepath.Join(home, ".codex")
}
func defaultOpencodeHome() string {
home, err := os.UserHomeDir()
if err != nil || strings.TrimSpace(home) == "" {
return ""
}
return filepath.Join(home, ".opencode")
}
func defaultOpenClawHome() string {
home, err := os.UserHomeDir()
if err != nil || strings.TrimSpace(home) == "" {
return ""
}
return filepath.Join(home, ".openclaw")
}
func stripManagedBlock(content, startMarker, endMarker string) string {
if strings.TrimSpace(content) == "" {
return content
}
remaining := content
for {
start := strings.Index(remaining, startMarker)
if start < 0 {
break
}
end := strings.Index(remaining[start:], endMarker)
if end < 0 {
remaining = remaining[:start]
break
}
end += start
remaining = remaining[:start] + remaining[end+len(endMarker):]
}
return remaining
}
func mergeManagedBlock(content, block, startMarker, endMarker string) string {
preserved := strings.TrimRight(
stripManagedBlock(content, startMarker, endMarker),
"\n",
)
if preserved == "" {
return block + "\n"
}
return preserved + "\n\n" + block + "\n"
}
func buildCodexManagedMCPBlock(servers []ManagedMCPServer) string {
var buffer strings.Builder
buffer.WriteString(codexManagedMCPBlockStart)
buffer.WriteString("\n# Generated by XWorkmate - Managed MCP Server Configuration\n")
buffer.WriteString(
fmt.Sprintf("# Last updated: %s\n\n", time.Now().Format(time.RFC3339Nano)),
)
for _, server := range servers {
buffer.WriteString(fmt.Sprintf("[mcp_servers.%s]\n", server.ID))
buffer.WriteString(fmt.Sprintf("command = %q\n", server.Command))
if len(server.Args) > 0 {
buffer.WriteString(fmt.Sprintf("args = %s\n", formatTOMLArray(server.Args)))
}
buffer.WriteString("\n")
}
buffer.WriteString(codexManagedMCPBlockEnd)
return strings.TrimRight(buffer.String(), "\n")
}
func buildOpencodeManagedMCPBlock(servers []ManagedMCPServer) string {
var buffer strings.Builder
buffer.WriteString(opencodeManagedMCPBlockStart)
buffer.WriteString("\n# Generated by XWorkmate - Managed MCP Server Configuration\n")
buffer.WriteString(
fmt.Sprintf("# Last updated: %s\n\n", time.Now().Format(time.RFC3339Nano)),
)
for _, server := range servers {
buffer.WriteString(fmt.Sprintf("[mcp_servers.%s]\n", server.ID))
if strings.TrimSpace(server.URL) != "" {
buffer.WriteString(fmt.Sprintf("url = %q\n", strings.TrimSpace(server.URL)))
} else {
buffer.WriteString("type = \"stdio\"\n")
buffer.WriteString(fmt.Sprintf("command = %q\n", server.Command))
if len(server.Args) > 0 {
buffer.WriteString(fmt.Sprintf("args = %s\n", formatTOMLArray(server.Args)))
}
}
buffer.WriteString("\n")
}
buffer.WriteString(opencodeManagedMCPBlockEnd)
return strings.TrimRight(buffer.String(), "\n")
}
func formatTOMLArray(items []string) string {
if len(items) == 0 {
return "[]"
}
var quoted []string
for _, item := range items {
quoted = append(quoted, fmt.Sprintf("%q", item))
}
return "[" + strings.Join(quoted, ", ") + "]"
}
func applyManagedBlock(configPath, block, startMarker, endMarker string) error {
configDir := filepath.Dir(configPath)
if err := os.MkdirAll(configDir, 0o755); err != nil {
return err
}
content, err := os.ReadFile(configPath)
if err != nil && !os.IsNotExist(err) {
return err
}
merged := mergeManagedBlock(string(content), block, startMarker, endMarker)
return os.WriteFile(configPath, []byte(merged), 0o644)
}