support OLLIE_PROMPTS_PATH: multi-dir prompt store via NewFlatDirUnion
This commit is contained in:
parent
11618a7960
commit
77c20cf43d
|
|
@ -251,7 +251,7 @@ func New(sink *olog.Sink, opts ...ServerOption) *Server {
|
|||
sink: sink,
|
||||
agentsDir: agentsDir,
|
||||
agentStore: NewFlatDirStore(agentsDir, 0644),
|
||||
promptStore: NewFlatDirStore(agent.DefaultPromptsDir(), 0444),
|
||||
promptStore: store.NewFlatDirUnion(agent.PromptsDirs(), 0444),
|
||||
memStore: NewFlatDirStore(memDir, 0644),
|
||||
toolStore: NewToolStore(),
|
||||
utilStore: NewUtilStore(),
|
||||
|
|
|
|||
|
|
@ -111,6 +111,66 @@ func NewFlatDir(dir string, perm os.FileMode) Store {
|
|||
}
|
||||
}
|
||||
|
||||
// NewFlatDirUnion returns a read-only Store that merges multiple directories.
|
||||
// Files are deduplicated by name; the first directory containing a name wins.
|
||||
func NewFlatDirUnion(dirs []string, perm os.FileMode) Store {
|
||||
notBlocking := func(context.Context, string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("blocking read not supported")
|
||||
}
|
||||
|
||||
resolve := func(name string) (string, error) {
|
||||
for _, dir := range dirs {
|
||||
p := filepath.Join(dir, name)
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("%s: not found", name)
|
||||
}
|
||||
|
||||
return &storeConfig{
|
||||
StatFn: func(name string) (os.FileInfo, error) {
|
||||
p, err := resolve(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Stat(p)
|
||||
},
|
||||
ListFn: func() ([]os.DirEntry, error) {
|
||||
seen := make(map[string]bool)
|
||||
var result []os.DirEntry
|
||||
for _, dir := range dirs {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, e := range entries {
|
||||
if !seen[e.Name()] {
|
||||
seen[e.Name()] = true
|
||||
result = append(result, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
},
|
||||
OpenFn: func(name string) (StoreEntry, error) {
|
||||
p, err := resolve(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EntryConfig{
|
||||
StatFn: func() (os.FileInfo, error) { return os.Stat(p) },
|
||||
ReadFn: func() ([]byte, error) { return os.ReadFile(p) },
|
||||
WriteFn: func([]byte) error { return fmt.Errorf("%s: read-only", name) },
|
||||
BlockingReadFn: notBlocking,
|
||||
}, nil
|
||||
},
|
||||
CreateFn: func(string) error { return fmt.Errorf("read-only store") },
|
||||
DeleteFn: func(string) error { return fmt.Errorf("read-only store") },
|
||||
RenameFn: func(string, string) error { return fmt.Errorf("read-only store") },
|
||||
}
|
||||
}
|
||||
|
||||
// SyntheticFileInfo implements os.FileInfo for entries with no backing file.
|
||||
type SyntheticFileInfo struct {
|
||||
Name_ string
|
||||
|
|
|
|||
Reference in New Issue