Files
narratio/internal/artifacts/resolve.go

59 lines
1.4 KiB
Go

package artifacts
import (
"os"
"path/filepath"
"strings"
)
// ResolveSessionLocalPathForRead resolves manifest/local artifact paths for read use-cases.
// Invariants for stage consumers:
// - local paths may be absolute, workspace-root qualified, or session-workdir relative.
// - callers should resolve through this helper instead of manually joining session/workdir roots.
func ResolveSessionLocalPathForRead(paths SessionPaths, localPath string) string {
p := filepath.Clean(strings.TrimSpace(localPath))
if p == "" {
return ""
}
if filepath.IsAbs(p) {
return p
}
// Already qualified against known roots.
if underRoot(p, paths.WorkspaceRoot) || underRoot(p, paths.Root) {
return p
}
candidates := []string{
p,
filepath.Clean(filepath.Join(paths.WorkspaceRoot, p)),
filepath.Clean(filepath.Join(paths.Root, p)),
}
for _, c := range candidates {
if c == "" {
continue
}
if _, err := os.Stat(c); err == nil {
return c
}
}
// Deterministic fallback for ambiguous relative values.
if strings.TrimSpace(paths.WorkspaceRoot) != "" {
return candidates[1]
}
return candidates[2]
}
func underRoot(path, root string) bool {
p := filepath.Clean(strings.TrimSpace(path))
r := filepath.Clean(strings.TrimSpace(root))
if p == "" || r == "" {
return false
}
if p == r {
return true
}
return strings.HasPrefix(p, r+string(filepath.Separator))
}