p9: add x/ plugin namespace; wire elevation via x/elevate
Add PluginStore backed by scripts/x/, exposed read-only at x/ in the 9P filesystem. Remove elevator field and elevation.Detect(); update execute.Decl() call sites to drop the now-removed elevator parameter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8b233c295e
commit
680e2a49bd
|
|
@ -70,6 +70,8 @@ ollie/
|
|||
<script> r/w: tool script content
|
||||
u/ dir: utility scripts (read, backed by ~/.config/ollie/scripts/u/)
|
||||
<script> exec: utility script; compositions of b/ primitives
|
||||
x/ dir: plugins (read, backed by ~/.config/ollie/scripts/x/)
|
||||
<plugin> exec: server-invoked plugin (e.g. elevation backends)
|
||||
```
|
||||
|
||||
Session IDs are Unix nanosecond timestamps with a random suffix (e.g. `1744276689123456789-2b986c`), so `ls s/` sorted lexicographically gives creation order.
|
||||
|
|
@ -88,6 +90,7 @@ Each 9P directory endpoint is backed by a named store implementing one of the st
|
|||
| `/sk` | `SkillStore` | `Store` |
|
||||
| `/t` | `ToolStore` | `Store` |
|
||||
| `/u` | `UtilStore` | `Store` |
|
||||
| `/x` | `PluginStore` | `Store` |
|
||||
| `/s` | `SessionStore` | `Store` |
|
||||
|
||||
To swap a backing store (e.g. replace `/pl` with a vector database or `/m` with an object store), implement the appropriate interface and wire it in `New()`. The store interface requires only `Stat`, `List`, `Get`, `Put`, `Delete`, `Create`, and `Rename` — authentication, connection management, and credential rotation are internal concerns of the implementation.
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func (s *BatchStore) executeJob(ctx context.Context, job *batchJob) (string, err
|
|||
cfg, _ := config.Load(cfgPath)
|
||||
|
||||
newDisp := tools.NewDispatcherFunc(map[string]func() tools.Server{
|
||||
"execute": execute.Decl(cwd, nil),
|
||||
"execute": execute.Decl(cwd),
|
||||
})
|
||||
env := agent.BuildAgentEnv(cfg, newDisp(), cwd)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package p9
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"ollie/pkg/tools/execute"
|
||||
)
|
||||
|
||||
// PluginStore is a read-only FlatDirStore backed by the scripts/x/ directory.
|
||||
// Plugins are executables invoked by the server (e.g. elevation backends), not
|
||||
// by the agent or user directly.
|
||||
type PluginStore struct {
|
||||
*FlatDirStore
|
||||
}
|
||||
|
||||
func NewPluginStore() *PluginStore {
|
||||
return &PluginStore{FlatDirStore: NewFlatDirStore(execute.PluginsPath(), 0555)}
|
||||
}
|
||||
|
||||
func (s *PluginStore) Stat(name string) (os.FileInfo, error) {
|
||||
return s.FlatDirStore.Stat(name)
|
||||
}
|
||||
|
||||
func (s *PluginStore) List() ([]os.DirEntry, error) {
|
||||
return s.FlatDirStore.List()
|
||||
}
|
||||
|
||||
func (s *PluginStore) Get(name string) ([]byte, error) {
|
||||
return s.FlatDirStore.Get(name)
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
// t/ (dir) tool scripts
|
||||
// tmp/ (dir) tool dumping ground (r/w, backed by OLLIE_TMP_PATH)
|
||||
// tr/ (dir) transcripts (ro; write to s/{id}/chat to save)
|
||||
// x/ (dir) plugins (ro, backed by ~/.config/ollie/scripts/x/)
|
||||
package p9
|
||||
|
||||
import (
|
||||
|
|
@ -47,7 +48,6 @@ import (
|
|||
"ollie/pkg/agent"
|
||||
"ollie/pkg/backend"
|
||||
"ollie/pkg/config"
|
||||
"ollie/pkg/elevation"
|
||||
olog "ollie/pkg/log"
|
||||
"ollie/pkg/paths"
|
||||
"ollie/pkg/tools"
|
||||
|
|
@ -102,7 +102,6 @@ type connState struct {
|
|||
|
||||
// Server is the 9P server for ollie sessions.
|
||||
type Server struct {
|
||||
elevator elevation.Elevator
|
||||
mu sync.RWMutex
|
||||
sessions map[string]*session
|
||||
conns []*connState
|
||||
|
|
@ -114,6 +113,7 @@ type Server struct {
|
|||
memStore Store
|
||||
toolStore Store
|
||||
utilStore Store
|
||||
pluginStore Store
|
||||
skillStore Store
|
||||
sessionStore Store
|
||||
batchStore *BatchStore
|
||||
|
|
@ -131,7 +131,6 @@ func New() *Server {
|
|||
os.MkdirAll(tmpDir, 0755) //nolint:errcheck
|
||||
agentsDir := agent.DefaultAgentsDir()
|
||||
s := &Server{
|
||||
elevator: elevation.Detect(),
|
||||
sessions: make(map[string]*session),
|
||||
agentsDir: agentsDir,
|
||||
sessionsDir: agent.DefaultSessionsDir(),
|
||||
|
|
@ -141,6 +140,7 @@ func New() *Server {
|
|||
memStore: NewFlatDirStore(memDir, 0644),
|
||||
toolStore: NewToolStore(),
|
||||
utilStore: NewUtilStore(),
|
||||
pluginStore: NewPluginStore(),
|
||||
skillStore: NewSkillStore(),
|
||||
transcriptStore: NewFlatDirStore(transcriptDir, 0444),
|
||||
tmpStore: NewFlatDirStore(tmpDir, 0600),
|
||||
|
|
@ -332,6 +332,12 @@ func (s *Server) pathType(path string) string {
|
|||
if _, err := s.utilStore.Stat(parts[1]); err == nil {
|
||||
return "file"
|
||||
}
|
||||
case len(parts) == 1 && parts[0] == "x":
|
||||
return "dir"
|
||||
case len(parts) == 2 && parts[0] == "x":
|
||||
if _, err := s.pluginStore.Stat(parts[1]); err == nil {
|
||||
return "file"
|
||||
}
|
||||
case len(parts) == 1 && parts[0] == "tr":
|
||||
return "dir"
|
||||
case len(parts) == 2 && parts[0] == "tr":
|
||||
|
|
@ -701,6 +707,16 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
return s.readSlice(fc, content)
|
||||
}
|
||||
|
||||
// Plugin files are served from the plugin store.
|
||||
if strings.HasPrefix(path, "/x/") {
|
||||
plog.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
|
||||
content, err := s.pluginStore.Get(pathBase(path))
|
||||
if err != nil {
|
||||
return errFcall(fc, err.Error())
|
||||
}
|
||||
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)
|
||||
|
|
@ -1165,7 +1181,7 @@ func (s *Server) createSession(args []string) error {
|
|||
cfg, _ := config.Load(cfgPath) // nil cfg is handled by BuildAgentEnv
|
||||
|
||||
newDisp := tools.NewDispatcherFunc(map[string]func() tools.Server{
|
||||
"execute": execute.Decl(cwd, s.elevator),
|
||||
"execute": execute.Decl(cwd),
|
||||
})
|
||||
|
||||
sessID := name
|
||||
|
|
@ -1297,6 +1313,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
|
|||
dirs = append(dirs, makeDir("t", "/t", true, plan9.DMDIR|0777))
|
||||
dirs = append(dirs, makeDir("tmp", "/tmp", true, plan9.DMDIR|0755))
|
||||
dirs = append(dirs, makeDir("u", "/u", true, plan9.DMDIR|0755))
|
||||
dirs = append(dirs, makeDir("x", "/x", true, plan9.DMDIR|0555))
|
||||
dirs = append(dirs, makeDir("tr", "/tr", true, plan9.DMDIR|0555))
|
||||
} else if path == "/a" {
|
||||
entries, _ := s.agentStore.List()
|
||||
|
|
@ -1383,6 +1400,11 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
|
|||
for _, e := range entries {
|
||||
dirs = append(dirs, makeDir(e.Name(), "/u/"+e.Name(), false, 0555))
|
||||
}
|
||||
} else if path == "/x" {
|
||||
entries, _ := s.pluginStore.List()
|
||||
for _, e := range entries {
|
||||
dirs = append(dirs, makeDir(e.Name(), "/x/"+e.Name(), false, 0555))
|
||||
}
|
||||
} else if path == "/b" {
|
||||
dirs = append(dirs, makeDir("job", "/b/job", false, 0555))
|
||||
dirs = append(dirs, makeDir("q", "/b/q", false, 0555))
|
||||
|
|
@ -1514,6 +1536,8 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
mode = 0777
|
||||
} else if strings.HasPrefix(path, "/u/") {
|
||||
mode = 0555
|
||||
} else if strings.HasPrefix(path, "/x/") {
|
||||
mode = 0555
|
||||
} else if path == "/b/job" || path == "/b/q" || path == "/b/sched" || path == "/b/cleanup" {
|
||||
mode = 0555
|
||||
} else {
|
||||
|
|
@ -1601,6 +1625,10 @@ func (s *Server) makeStat(path string) plan9.Dir {
|
|||
if content, err := s.utilStore.Get(base); err == nil {
|
||||
dir.Length = uint64(len(content))
|
||||
}
|
||||
case strings.HasPrefix(path, "/x/"):
|
||||
if content, err := s.pluginStore.Get(base); err == nil {
|
||||
dir.Length = uint64(len(content))
|
||||
}
|
||||
case path == "/b/job" || path == "/b/q" || path == "/b/sched" || path == "/b/cleanup":
|
||||
if content, err := os.ReadFile(paths.CfgDir() + "/scripts/b/" + base); err == nil {
|
||||
dir.Length = uint64(len(content))
|
||||
|
|
|
|||
Reference in New Issue