refactor: remove 9P mount logic from core execute server
Moved all 9P FUSE mount code from core to 9p module using lifecycle hooks. Changes: - Removed 9P-specific fields (mountProc, mountPath, mountEnabled) from execute.Server - Removed WithMount(), MountPath(), startMount(), remountIfStale() functions - Added lifecycle hooks: OnPreDispatch, OnClose, OnEnvSet with corresponding options - Updated SetEnv(), Close(), and Dispatch() to call hooks instead of hardcoded mount logic - Fixed tool descriptions to use $OLLIE_TOOLS_PATH instead of 9P-specific $OLLIE/s/$SID/t paths - Removed unused execute import from agent/commands.go The core execute server is now harness-agnostic.
This commit is contained in:
parent
f05b736af9
commit
fd20070294
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"ollie/pkg/config"
|
||||
"ollie/pkg/tools/execute"
|
||||
)
|
||||
|
||||
func (s *agent) handleCommand(ctx context.Context, input string) bool {
|
||||
|
|
@ -176,7 +175,7 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
|
|||
return
|
||||
}
|
||||
d := s.newDispatcher()
|
||||
rt := BuildRuntime(cfg, d, s.cwd, []string{"OLLIE_SESSION_ID=" + s.sessionID, "OLLIE=" + execute.MountPath(s.uname)})
|
||||
rt := BuildRuntime(cfg, d, s.cwd, []string{"OLLIE_SESSION_ID=" + s.sessionID})
|
||||
if rt.CfgBackend != "" {
|
||||
newBe, err := s.newBackend(rt.CfgBackend)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ var languagePatterns = map[string][]*regexp.Regexp{
|
|||
|
||||
// Dispatch routes a named execute tool call.
|
||||
func (e *Server) Dispatch(ctx context.Context, name string, args json.RawMessage) (string, error) {
|
||||
e.remountIfStale()
|
||||
if e.OnPreDispatch != nil {
|
||||
e.OnPreDispatch()
|
||||
}
|
||||
if len(e.allowExecutors) > 0 && !e.allowExecutors[name] {
|
||||
return "", fmt.Errorf("executor %q not permitted in this agent context", name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ type Server struct {
|
|||
// step dispatch. Set once at session init via SetLockDir; empty disables locking.
|
||||
lockDir string
|
||||
|
||||
// mountProc is the ollie-9p-mount subprocess for this session's FUSE proxy.
|
||||
mountProc *os.Process
|
||||
mountPath string
|
||||
mountEnabled bool
|
||||
// Hooks for lifecycle events (harnesses like 9P can inject mount logic here)
|
||||
OnPreDispatch func()
|
||||
OnClose func()
|
||||
OnEnvSet func(key, value string)
|
||||
|
||||
// Strict rejects inline {code} steps; only {tool} steps are allowed.
|
||||
Strict bool
|
||||
|
|
@ -76,8 +76,14 @@ func WithStrict() Option { return func(s *Server) { s.Strict = true } }
|
|||
// WithYolo skips the landrun sandbox.
|
||||
func WithYolo() Option { return func(s *Server) { s.Yolo = true } }
|
||||
|
||||
// WithMount enables per-session FUSE mount for 9P identity.
|
||||
func WithMount() Option { return func(s *Server) { s.mountEnabled = true } }
|
||||
// WithOnPreDispatch registers a hook called before each Dispatch.
|
||||
func WithOnPreDispatch(fn func()) Option { return func(s *Server) { s.OnPreDispatch = fn } }
|
||||
|
||||
// WithOnClose registers a hook called during Close.
|
||||
func WithOnClose(fn func()) Option { return func(s *Server) { s.OnClose = fn } }
|
||||
|
||||
// WithOnEnvSet registers a hook called each time SetEnv is called.
|
||||
func WithOnEnvSet(fn func(key, value string)) Option { return func(s *Server) { s.OnEnvSet = fn } }
|
||||
|
||||
// WithAllowExecutors restricts which executors are available.
|
||||
func WithAllowExecutors(names []string) Option {
|
||||
|
|
@ -185,10 +191,10 @@ Examples:
|
|||
},
|
||||
{
|
||||
Name: "call_tool",
|
||||
Description: `Run one or more named scripts from $OLLIE/s/$SID/t (the tools directory).
|
||||
Description: `Run one or more named scripts from the tools directory.
|
||||
|
||||
Tool names and their arguments are discoverable via:
|
||||
grep -iA2 'keyword' $OLLIE/s/$SID/t/idx
|
||||
grep -iA2 'keyword' $OLLIE_TOOLS_PATH/idx
|
||||
|
||||
call_tool fans out in parallel when consecutive calls carry an ollie:parallel read
|
||||
annotation in their script header; write-annotated or unannotated tools run serially.
|
||||
|
|
@ -245,7 +251,7 @@ Use this when you need to chain heterogeneous code and tool steps together.
|
|||
|
||||
Each stage is one of:
|
||||
- {code, language} — inline code (default: bash)
|
||||
- {tool, args} — named script from $OLLIE/s/$SID/t
|
||||
- {tool, args} — named script from the tools directory
|
||||
- {elevated: true, code/tool} — run outside sandbox
|
||||
- {parallel: [{code/tool}...]} — fan-out within a stage; outputs concatenated, fed to next stage
|
||||
|
||||
|
|
@ -373,87 +379,15 @@ func (e *Server) SetEnv(key, value string) {
|
|||
e.envExtra = make(map[string]string)
|
||||
}
|
||||
e.envExtra[key] = value
|
||||
// Start mount once we have the principal.
|
||||
if key == "OLLIE_UNAME" && value != "" {
|
||||
e.startMount(value)
|
||||
}
|
||||
e.envMu.Unlock()
|
||||
}
|
||||
|
||||
// MountPath returns the per-session FUSE mount path for a given uname.
|
||||
func MountPath(uname string) string {
|
||||
return filepath.Join(os.TempDir(), "ollie-mount-"+uname)
|
||||
}
|
||||
|
||||
// startMount launches ollie-9p-mount for this session. Must be called with envMu held.
|
||||
func (e *Server) startMount(sessionID string) {
|
||||
if !e.mountEnabled {
|
||||
return
|
||||
}
|
||||
bin, err := exec.LookPath("ollie-9p-mount")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mnt := MountPath(sessionID)
|
||||
os.MkdirAll(mnt, 0755) //nolint:errcheck
|
||||
cmd := exec.Command(bin, "-u", sessionID, mnt)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
// Reap in background so Wait works later.
|
||||
go cmd.Wait() //nolint:errcheck
|
||||
// Wait for mount to be ready.
|
||||
for i := 0; i < 20; i++ {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if _, err := os.Stat(filepath.Join(mnt, "s")); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
e.mountProc = cmd.Process
|
||||
e.mountPath = mnt
|
||||
e.envExtra["OLLIE"] = mnt
|
||||
}
|
||||
|
||||
// remountIfStale checks the per-session FUSE mount health and re-establishes it if broken.
|
||||
// Must be called without envMu held.
|
||||
func (e *Server) remountIfStale() {
|
||||
e.envMu.RLock()
|
||||
mnt := e.mountPath
|
||||
proc := e.mountProc
|
||||
e.envMu.RUnlock()
|
||||
if mnt == "" || proc == nil {
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(mnt, "s")); err == nil {
|
||||
return
|
||||
}
|
||||
// Mount is stale — tear down and restart.
|
||||
e.envMu.Lock()
|
||||
defer e.envMu.Unlock()
|
||||
exec.Command("fusermount", "-u", e.mountPath).Run() //nolint:errcheck
|
||||
syscall.Kill(e.mountProc.Pid, syscall.SIGKILL) //nolint:errcheck
|
||||
e.mountProc = nil
|
||||
e.mountPath = ""
|
||||
if uname := e.envExtra["OLLIE_UNAME"]; uname != "" {
|
||||
e.startMount(uname)
|
||||
if e.OnEnvSet != nil {
|
||||
e.OnEnvSet(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Close tears down the per-session FUSE mount.
|
||||
// Close is called when the session ends. Calls OnClose hook if registered.
|
||||
func (e *Server) Close() {
|
||||
e.envMu.Lock()
|
||||
proc := e.mountProc
|
||||
mnt := e.mountPath
|
||||
e.mountProc = nil
|
||||
e.mountPath = ""
|
||||
e.envMu.Unlock()
|
||||
if proc != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
exec.CommandContext(ctx, "fusermount", "-u", mnt).Run() //nolint:errcheck
|
||||
cancel()
|
||||
syscall.Kill(proc.Pid, syscall.SIGKILL) //nolint:errcheck
|
||||
os.Remove(mnt) //nolint:errcheck
|
||||
if e.OnClose != nil {
|
||||
e.OnClose()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in New Issue