// spec.go - virtfs-based namespace specification for toolsrv. package fs import ( "context" "fmt" "os" "strconv" "strings" "ollie/virtfs" ) // Spec returns the virtfs specification for the toolsrv namespace. // The server is captured by closures — no context threading needed. func Spec(srv *Server) virtfs.FsNodeDecl { return virtfs.DirNode("/", virtfs.FileNode("ctl", 0222, virtfs.Doc("Control: write 'load ' or 'unload '"), virtfs.Write(func(data []byte) error { return srv.Fs.HandleCtl(strings.TrimSpace(string(data))) }), ), virtfs.FileNode("tools", 0666, virtfs.Doc("Tools: write agent ID, read tool list (rdwr)"), virtfs.Request(func(data []byte) ([]byte, error) { agentID := strings.TrimSpace(string(data)) if agentID == "" { return nil, fmt.Errorf("agent ID required") } return []byte(srv.Fs.HandleToolsRequest(agentID)), nil }), ), virtfs.FileNode("info", 0444, virtfs.Doc("Host info: platform, arch"), virtfs.Read(func() ([]byte, error) { return []byte(srv.HostInfo()), nil }), ), virtfs.DirNode("proc", virtfs.FileNode("new", 0666, virtfs.Doc("Execute tool: write token + tool + args, read result (blocking)"), virtfs.Request(func(data []byte) ([]byte, error) { result, err := srv.Fs.HandleProcNew(context.Background(), string(data)) if err != nil { return nil, err } return []byte(result), nil }), ), virtfs.FileNode("new.bg", 0666, virtfs.Doc("Execute tool in background: write token + tool + args, read pid"), virtfs.Request(func(data []byte) ([]byte, error) { result, err := srv.Fs.HandleProcNewBg(context.Background(), string(data)) if err != nil { return nil, err } return []byte(result), nil }), ), virtfs.Each("{pid}", func() ([]virtfs.Binding, error) { pids := srv.Fs.ListProcs() var bindings []virtfs.Binding for _, pid := range pids { proc := srv.Fs.GetProc(pid) if proc == nil { continue } p := proc bindings = append(bindings, virtfs.Binding{ Name: strconv.Itoa(pid), Children: []virtfs.FsNodeDecl{ virtfs.FileNode("out", 0444, virtfs.Doc("Process output"), virtfs.Read(func() ([]byte, error) { return []byte(p.Output()), nil }), ), virtfs.FileNode("wait", 0444, virtfs.Doc("Block until process exits, returns exit code"), virtfs.BlockOnce(func(_ context.Context, _ string) ([]byte, string, error) { exitCode := p.Wait() return []byte(fmt.Sprintf("%d\n", exitCode)), "", nil }), ), virtfs.FileNode("stat", 0444, virtfs.Doc("Process status: running/exited, runtime, tool"), virtfs.Read(func() ([]byte, error) { return []byte(p.Stat()), nil }), ), virtfs.FileNode("ctl", 0222, virtfs.Doc("Process control: write 'signal ' or 'dismiss'"), virtfs.Write(func(data []byte) error { return srv.Fs.HandleProcCtl(p.ID, strings.TrimSpace(string(data))) }), ), }, }) } return bindings, nil }), ), ) } // Mode constants for convenience. const ( ModeReadOnly os.FileMode = 0444 ModeWriteOnly os.FileMode = 0222 ModeReadWrite os.FileMode = 0666 )