fix: ensure unifying on hardcoded /data mount path

This commit is contained in:
Ran
2026-02-24 03:25:35 +08:00
committed by 晨苒
parent 6cb80d30be
commit 5e12b5a53f
23 changed files with 96 additions and 96 deletions
+1 -4
View File
@@ -334,10 +334,7 @@ func (m *Manager) dataRoot() string {
}
func (m *Manager) dataMount() string {
if m.cfg.DataMount == "" {
return config.DefaultDataMount
}
return m.cfg.DataMount
return config.DefaultDataMount
}
func (m *Manager) imageRef() string {
+20 -12
View File
@@ -2,6 +2,7 @@ package container
import (
"context"
"fmt"
"log/slog"
"strings"
@@ -54,6 +55,10 @@ func NewExecutor(log *slog.Logger, execRunner ExecRunner, execWorkDir string) *E
// ListTools returns read, write, list, edit, and exec tool descriptors.
func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionContext) ([]mcpgw.ToolDescriptor, error) {
wd := p.execWorkDir
if wd == "" {
wd = defaultExecWorkDir
}
return []mcpgw.ToolDescriptor{
{
Name: toolRead,
@@ -61,7 +66,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]any{"type": "string", "description": "file path (relative to /data or absolute inside container)"},
"path": map[string]any{"type": "string", "description": fmt.Sprintf("file path (relative to %s or absolute inside container)", wd)},
},
"required": []string{"path"},
},
@@ -72,7 +77,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]any{"type": "string", "description": "file path (relative to /data or absolute inside container)"},
"path": map[string]any{"type": "string", "description": fmt.Sprintf("file path (relative to %s or absolute inside container)", wd)},
"content": map[string]any{"type": "string", "description": "file content"},
},
"required": []string{"path", "content"},
@@ -84,7 +89,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]any{"type": "string", "description": "directory path (relative to /data or absolute inside container)"},
"path": map[string]any{"type": "string", "description": fmt.Sprintf("directory path (relative to %s or absolute inside container)", wd)},
"recursive": map[string]any{"type": "boolean", "description": "list recursively"},
},
"required": []string{"path"},
@@ -96,7 +101,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"path": map[string]any{"type": "string", "description": "file path (relative to /data or absolute inside container)"},
"path": map[string]any{"type": "string", "description": fmt.Sprintf("file path (relative to %s or absolute inside container)", wd)},
"old_text": map[string]any{"type": "string", "description": "exact text to find"},
"new_text": map[string]any{"type": "string", "description": "replacement text"},
},
@@ -105,7 +110,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
},
{
Name: toolExec,
Description: "Execute a command in the bot container. Runs in the bot's data directory (/data) by default.",
Description: fmt.Sprintf("Execute a command in the bot container. Runs in the bot's data directory (%s) by default.", wd),
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
@@ -115,7 +120,7 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
},
"work_dir": map[string]any{
"type": "string",
"description": "Working directory inside the container (default: /data)",
"description": fmt.Sprintf("Working directory inside the container (default: %s)", wd),
},
},
"required": []string{"command"},
@@ -126,12 +131,15 @@ func (p *Executor) ListTools(ctx context.Context, session mcpgw.ToolSessionConte
// normalizePath converts paths that the LLM may send as /data/... into relative
// paths under the working directory. e.g. /data/test.txt -> test.txt, /data -> .
func normalizePath(path string) string {
func (p *Executor) normalizePath(path string) string {
path = strings.TrimSpace(path)
if path == "" {
return path
}
const prefix = "/data"
prefix := p.execWorkDir
if prefix == "" {
prefix = defaultExecWorkDir
}
if path == prefix {
return "."
}
@@ -150,7 +158,7 @@ func (p *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContex
switch toolName {
case toolRead:
filePath := normalizePath(mcpgw.StringArg(arguments, "path"))
filePath := p.normalizePath(mcpgw.StringArg(arguments, "path"))
if filePath == "" {
return mcpgw.BuildToolErrorResult("path is required"), nil
}
@@ -163,7 +171,7 @@ func (p *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContex
}), nil
case toolWrite:
filePath := normalizePath(mcpgw.StringArg(arguments, "path"))
filePath := p.normalizePath(mcpgw.StringArg(arguments, "path"))
content := mcpgw.StringArg(arguments, "content")
if filePath == "" {
return mcpgw.BuildToolErrorResult("path is required"), nil
@@ -174,7 +182,7 @@ func (p *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContex
return mcpgw.BuildToolSuccessResult(map[string]any{"ok": true}), nil
case toolList:
dirPath := normalizePath(mcpgw.StringArg(arguments, "path"))
dirPath := p.normalizePath(mcpgw.StringArg(arguments, "path"))
if dirPath == "" {
dirPath = "."
}
@@ -196,7 +204,7 @@ func (p *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContex
return mcpgw.BuildToolSuccessResult(map[string]any{"path": dirPath, "entries": entriesMaps}), nil
case toolEdit:
filePath := normalizePath(mcpgw.StringArg(arguments, "path"))
filePath := p.normalizePath(mcpgw.StringArg(arguments, "path"))
oldText := mcpgw.StringArg(arguments, "old_text")
newText := mcpgw.StringArg(arguments, "new_text")
if filePath == "" || oldText == "" {
@@ -227,8 +227,9 @@ func TestNormalizePath(t *testing.T) {
{"", ""},
{".", "."},
}
exec := &Executor{execWorkDir: "/data"}
for _, tt := range tests {
got := normalizePath(tt.in)
got := exec.normalizePath(tt.in)
if got != tt.want {
t.Errorf("normalizePath(%q) = %q, want %q", tt.in, got, tt.want)
}
+9 -3
View File
@@ -392,7 +392,10 @@ func (p *Executor) resolveAttachmentRef(ctx context.Context, botID, ref, attType
}
// Container media path — resolve via asset storage.
const mediaMarker = "/data/media/"
mediaMarker := filepath.Join("/data", "media")
if !strings.HasSuffix(mediaMarker, "/") {
mediaMarker += "/"
}
if idx := strings.Index(ref, mediaMarker); idx >= 0 && p.assetResolver != nil {
storageKey := ref[idx+len(mediaMarker):]
asset, err := p.assetResolver.GetByStorageKey(ctx, botID, storageKey)
@@ -404,8 +407,11 @@ func (p *Executor) resolveAttachmentRef(ctx context.Context, botID, ref, attType
}
}
// Other container /data/ path — ingest into media store first.
const dataPrefix = "/data/"
// Other container data mount path — ingest into media store first.
dataPrefix := "/data"
if !strings.HasSuffix(dataPrefix, "/") {
dataPrefix += "/"
}
if strings.HasPrefix(ref, dataPrefix) && p.assetResolver != nil {
asset, err := p.assetResolver.IngestContainerFile(ctx, botID, ref)
if err == nil {
@@ -85,7 +85,7 @@ func TestSourceListToolsIncludesSSETools(t *testing.T) {
if len(tools) != 1 {
t.Fatalf("expected 1 tool, got %d", len(tools))
}
if tools[0].Name != "remote_sse.search" {
if tools[0].Name != "remote_sse_search" {
t.Fatalf("unexpected tool alias: %s", tools[0].Name)
}
}
@@ -113,7 +113,7 @@ func TestSourceCallToolRoutesToSSEConnection(t *testing.T) {
}
source := NewSource(slog.Default(), gateway, lister)
result, err := source.CallTool(context.Background(), mcpgw.ToolSessionContext{BotID: "bot-1"}, "remote_sse.search", map[string]any{"query": "hello"})
result, err := source.CallTool(context.Background(), mcpgw.ToolSessionContext{BotID: "bot-1"}, "remote_sse_search", map[string]any{"query": "hello"})
if err != nil {
t.Fatalf("call tool failed: %v", err)
}
+2 -9
View File
@@ -294,10 +294,7 @@ func (m *Manager) buildVersionSpec(botID string) (ctr.ContainerSpec, error) {
if err != nil {
return ctr.ContainerSpec{}, err
}
dataMount := m.cfg.DataMount
if dataMount == "" {
dataMount = config.DefaultDataMount
}
dataMount := config.DefaultDataMount
resolvPath, err := ctr.ResolveConfSource(dataDir)
if err != nil {
return ctr.ContainerSpec{}, err
@@ -351,11 +348,7 @@ func (m *Manager) ensureDBRecords(ctx context.Context, botID, containerID, runti
return pgtype.UUID{}, err
}
containerPath := m.cfg.DataMount
if containerPath == "" {
containerPath = config.DefaultDataMount
}
containerPath := config.DefaultDataMount
if err := m.queries.UpsertContainer(ctx, dbsqlc.UpsertContainerParams{
BotID: botUUID,
ContainerID: containerID,