9p: add ollie/tmp backed by OLLIE_TMP_PATH flatdirstore
Replaces /tmp/ollie per-session scratch space with a 9P-served tmp/ directory backed by OLLIE_TMP_PATH (~/.local/share/ollie/tmp by default). Markers are now global, hash-addressed by absolute file path, and shared across all sessions. OLLIE_TMP_PATH is always set by ensureEnv(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f17774163c
commit
ece8d22e75
|
|
@ -29,6 +29,7 @@
|
|||
// ctxsz (read) context size vs window
|
||||
// sk/ (dir) skills
|
||||
// t/ (dir) tool scripts
|
||||
// tmp/ (dir) file-edit markers (r/w, backed by OLLIE_TMP_PATH)
|
||||
// tr/ (dir) transcripts (ro; write to s/{id}/chat to save)
|
||||
package p9
|
||||
|
||||
|
|
@ -147,6 +148,7 @@ type Server struct {
|
|||
sessionStore Store
|
||||
batchStore *BatchStore
|
||||
transcriptStore Store
|
||||
tmpStore Store
|
||||
}
|
||||
|
||||
// New creates a new Server.
|
||||
|
|
@ -155,10 +157,12 @@ func New() *Server {
|
|||
os.MkdirAll(memDir, 0755) //nolint:errcheck
|
||||
transcriptDir := defaultTranscriptDir()
|
||||
os.MkdirAll(transcriptDir, 0755) //nolint:errcheck
|
||||
tmpDir := defaultTmpDir()
|
||||
os.MkdirAll(tmpDir, 0755) //nolint:errcheck
|
||||
agentsDir := agent.DefaultAgentsDir()
|
||||
s := &Server{
|
||||
sessions: make(map[string]*session),
|
||||
agentsDir: agentsDir,
|
||||
sessions: make(map[string]*session),
|
||||
agentsDir: agentsDir,
|
||||
sessionsDir: agent.DefaultSessionsDir(),
|
||||
agentStore: NewFlatDirStore(agentsDir, 0644),
|
||||
promptStore: NewFlatDirStore(agent.DefaultPromptsDir(), 0444),
|
||||
|
|
@ -167,6 +171,7 @@ func New() *Server {
|
|||
toolStore: NewToolStore(),
|
||||
skillStore: NewSkillStore(),
|
||||
transcriptStore: NewFlatDirStore(transcriptDir, 0444),
|
||||
tmpStore: NewFlatDirStore(tmpDir, 0600),
|
||||
}
|
||||
s.sessionStore = &SessionStore{srv: s}
|
||||
s.batchStore = NewBatchStore(s)
|
||||
|
|
@ -200,6 +205,15 @@ func defaultMemDir() string {
|
|||
return home + "/.config/ollie/memory"
|
||||
}
|
||||
|
||||
// defaultTmpDir returns the tmp directory from OLLIE_TMP_PATH or the default.
|
||||
func defaultTmpDir() string {
|
||||
if p := os.Getenv("OLLIE_TMP_PATH"); p != "" {
|
||||
return p
|
||||
}
|
||||
home, _ := os.UserHomeDir()
|
||||
return home + "/.local/share/ollie/tmp"
|
||||
}
|
||||
|
||||
// sessionFileStore returns a SessionFileStore for the given session ID.
|
||||
func (s *Server) sessionFileStore(sessID string) (*SessionFileStore, bool) {
|
||||
s.mu.RLock()
|
||||
|
|
@ -356,6 +370,12 @@ func (s *Server) pathType(path string) string {
|
|||
return "file"
|
||||
}
|
||||
}
|
||||
case len(parts) == 1 && parts[0] == "tmp":
|
||||
return "dir"
|
||||
case len(parts) == 2 && parts[0] == "tmp":
|
||||
if _, err := s.tmpStore.Stat(parts[1]); err == nil {
|
||||
return "file"
|
||||
}
|
||||
case len(parts) == 1 && parts[0] == "backends":
|
||||
return "file"
|
||||
case len(parts) == 1 && parts[0] == "help":
|
||||
|
|
@ -529,6 +549,10 @@ func (s *Server) create(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
if err := s.toolStore.Create(fc.Name); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
case "/tmp":
|
||||
if err := s.tmpStore.Create(fc.Name); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
case "/sk":
|
||||
if err := s.skillStore.Create(fc.Name); err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
|
|
@ -674,6 +698,16 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
return s.readSlice(fc, content)
|
||||
}
|
||||
|
||||
// Tmp files are served from the tmp store.
|
||||
if strings.HasPrefix(path, "/tmp/") {
|
||||
plog.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
|
||||
content, err := s.tmpStore.Get(pathBase(path))
|
||||
if err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
return s.readSlice(fc, content)
|
||||
}
|
||||
|
||||
// Transcript files are served from the transcript store.
|
||||
if strings.HasPrefix(path, "/tr/") {
|
||||
plog.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
|
||||
|
|
@ -892,13 +926,6 @@ func (s *Server) renameSession(oldID, newID string) error {
|
|||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Rename the session tmpdir so isread markers remain valid.
|
||||
oldTemp := "/tmp/ollie/" + oldID
|
||||
newTemp := "/tmp/ollie/" + newID
|
||||
if _, err := os.Stat(oldTemp); err == nil {
|
||||
os.Rename(oldTemp, newTemp) //nolint:errcheck
|
||||
}
|
||||
|
||||
sess.appendChat([]byte(fmt.Sprintf("(session renamed: %s -> %s)\n", oldID, newID)))
|
||||
plog.Info("renamed session %s -> %s", oldID, newID)
|
||||
return nil
|
||||
|
|
@ -971,6 +998,8 @@ func (s *Server) remove(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
err = s.skillStore.Delete(pathBase(path))
|
||||
case strings.HasPrefix(path, "/t/"):
|
||||
err = s.toolStore.Delete(pathBase(path))
|
||||
case strings.HasPrefix(path, "/tmp/"):
|
||||
err = s.tmpStore.Delete(pathBase(path))
|
||||
case strings.HasPrefix(path, "/tr/"):
|
||||
err = s.transcriptStore.Delete(pathBase(path))
|
||||
case strings.HasPrefix(path, "/b/") && path != "/b/new":
|
||||
|
|
@ -1043,6 +1072,11 @@ func (s *Server) handleWrite(path, input string) error {
|
|||
return s.toolStore.Put(pathBase(path), []byte(input))
|
||||
}
|
||||
|
||||
// Tmp file writes go to the tmp store.
|
||||
if strings.HasPrefix(path, "/tmp/") {
|
||||
return s.tmpStore.Put(pathBase(path), []byte(input))
|
||||
}
|
||||
|
||||
// Session file writes: /s/{sessid}/{file}
|
||||
if strings.HasPrefix(path, "/s/") {
|
||||
parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
|
||||
|
|
@ -1280,6 +1314,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
|
|||
dirs = append(dirs, makeDir("s", "/s", true, plan9.DMDIR|0555))
|
||||
dirs = append(dirs, makeDir("sk", "/sk", true, plan9.DMDIR|0555))
|
||||
dirs = append(dirs, makeDir("t", "/t", true, plan9.DMDIR|0777))
|
||||
dirs = append(dirs, makeDir("tmp", "/tmp", true, plan9.DMDIR|0755))
|
||||
dirs = append(dirs, makeDir("tr", "/tr", true, plan9.DMDIR|0555))
|
||||
} else if path == "/a" {
|
||||
entries, _ := s.agentStore.List()
|
||||
|
|
@ -1337,6 +1372,18 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
|
|||
}
|
||||
dirs = append(dirs, makeDir(e.Name(), "/t/"+e.Name(), false, mode))
|
||||
}
|
||||
} else if path == "/tmp" {
|
||||
entries, _ := s.tmpStore.List()
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
d := makeDir(e.Name(), "/tmp/"+e.Name(), false, 0600)
|
||||
if info, err := e.Info(); err == nil {
|
||||
d.Atime = uint32(info.ModTime().Unix())
|
||||
d.Mtime = uint32(info.ModTime().Unix())
|
||||
}
|
||||
dirs = append(dirs, d)
|
||||
}
|
||||
}
|
||||
} else if path == "/tr" {
|
||||
entries, _ := s.transcriptStore.List()
|
||||
for _, e := range entries {
|
||||
|
|
@ -1440,7 +1487,7 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
qid.Type = QTDir
|
||||
if path == "/t" {
|
||||
mode = plan9.DMDIR | 0777
|
||||
} else if path == "/a" || path == "/m" || path == "/pl" {
|
||||
} else if path == "/a" || path == "/m" || path == "/pl" || path == "/tmp" {
|
||||
mode = plan9.DMDIR | 0755
|
||||
} else {
|
||||
mode = plan9.DMDIR | 0555
|
||||
|
|
@ -1571,6 +1618,12 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
if content, err := s.toolStore.Get(base); err == nil {
|
||||
dir.Length = uint64(len(content))
|
||||
}
|
||||
case strings.HasPrefix(path, "/tmp/"):
|
||||
if info, err := s.tmpStore.Stat(base); err == nil {
|
||||
dir.Length = uint64(info.Size())
|
||||
dir.Atime = uint32(info.ModTime().Unix())
|
||||
dir.Mtime = uint32(info.ModTime().Unix())
|
||||
}
|
||||
case strings.HasPrefix(path, "/tr/"):
|
||||
if info, err := s.transcriptStore.Stat(base); err == nil {
|
||||
dir.Length = uint64(info.Size())
|
||||
|
|
|
|||
1
main.go
1
main.go
|
|
@ -148,6 +148,7 @@ func ensureEnv() {
|
|||
"OLLIE_SKILLS_PATH": filepath.Join(home, ".config", "ollie", "skills"),
|
||||
"OLLIE_PLAN_PATH": filepath.Join(home, ".config", "ollie", "planning"),
|
||||
"OLLIE_MEMORY_PATH": filepath.Join(home, ".config", "ollie", "memory"),
|
||||
"OLLIE_TMP_PATH": filepath.Join(home, ".local", "share", "ollie", "tmp"),
|
||||
}
|
||||
for k, v := range defaults {
|
||||
if os.Getenv(k) == "" {
|
||||
|
|
|
|||
Reference in New Issue