From ee7426d628de4191e9e2c661275d16aced64b5ce Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Tue, 11 Aug 2026 17:21:44 +0200 Subject: [PATCH] =?UTF-8?q?rename=20fsedsl=20=E2=86=92=20virtfs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package has outgrown its original 'eDSL' framing. It's a virtual filesystem library. Rename to match. --- cmd/olliesrv/internal/fs/handlers_root.go | 6 +- cmd/olliesrv/internal/fs/newroot.go | 4 +- cmd/olliesrv/internal/fs/spec.go | 290 +++++++++++----------- cmd/toolsrv/internal/fs/spec.go | 74 +++--- cmd/toolsrv/internal/fs/state.go | 8 +- cmd/toolsrv/server.go | 24 +- fsedsl/go.mod | 3 - go.mod | 4 +- {fsedsl => virtfs}/LICENSE.md | 0 {fsedsl => virtfs}/README.md | 64 ++--- {fsedsl => virtfs}/builder.go | 2 +- {fsedsl => virtfs}/decl.go | 4 +- {fsedsl => virtfs}/decl_test.go | 2 +- {fsedsl => virtfs}/file.go | 2 +- virtfs/go.mod | 3 + {fsedsl => virtfs}/help.go | 2 +- {fsedsl => virtfs}/tree.go | 2 +- {fsedsl => virtfs}/tree_test.go | 2 +- 18 files changed, 248 insertions(+), 248 deletions(-) delete mode 100644 fsedsl/go.mod rename {fsedsl => virtfs}/LICENSE.md (100%) rename {fsedsl => virtfs}/README.md (79%) rename {fsedsl => virtfs}/builder.go (99%) rename {fsedsl => virtfs}/decl.go (98%) rename {fsedsl => virtfs}/decl_test.go (99%) rename {fsedsl => virtfs}/file.go (99%) create mode 100644 virtfs/go.mod rename {fsedsl => virtfs}/help.go (98%) rename {fsedsl => virtfs}/tree.go (99%) rename {fsedsl => virtfs}/tree_test.go (99%) diff --git a/cmd/olliesrv/internal/fs/handlers_root.go b/cmd/olliesrv/internal/fs/handlers_root.go index e753b62..a4ccc73 100644 --- a/cmd/olliesrv/internal/fs/handlers_root.go +++ b/cmd/olliesrv/internal/fs/handlers_root.go @@ -11,7 +11,7 @@ import ( "ollie/cmd/olliesrv/internal/agent" "ollie/cmd/olliesrv/internal/backend" "ollie/cmd/olliesrv/internal/bypass" - "ollie/fsedsl" + "ollie/virtfs" "ollie/cmd/olliesrv/internal/session" "ollie/toolsrv" ) @@ -44,7 +44,7 @@ func readBackends() ([]byte, error) { var helpFn func() string // helpSpec stores the spec for help generation. Set during tree build. -var helpSpec *fsedsl.FsNodeDecl +var helpSpec *virtfs.FsNodeDecl func readHelp() ([]byte, error) { if helpFn == nil { @@ -57,7 +57,7 @@ func init() { helpFn = func() string { helpOnce.Do(func() { if helpSpec != nil { - helpText = fsedsl.GenerateHelp(*helpSpec) + helpText = virtfs.GenerateHelp(*helpSpec) } }) return helpText diff --git a/cmd/olliesrv/internal/fs/newroot.go b/cmd/olliesrv/internal/fs/newroot.go index 6c45d97..0e0169b 100644 --- a/cmd/olliesrv/internal/fs/newroot.go +++ b/cmd/olliesrv/internal/fs/newroot.go @@ -4,7 +4,7 @@ import ( "context" "ollie/cmd/olliesrv/internal/bypass" - "ollie/fsedsl" + "ollie/virtfs" olog "ollie/log" "ollie/cmd/olliesrv/internal/session" ) @@ -45,7 +45,7 @@ func NewRoot(cfg Config) *Tree { // Build the tree from the spec. spec := buildTreeSpec(rs) helpSpec = &spec - tree := fsedsl.BuildTree(spec) + tree := virtfs.BuildTree(spec) tree.Data = rs // Restore persisted sessions from disk. diff --git a/cmd/olliesrv/internal/fs/spec.go b/cmd/olliesrv/internal/fs/spec.go index 565e5fa..81b1474 100644 --- a/cmd/olliesrv/internal/fs/spec.go +++ b/cmd/olliesrv/internal/fs/spec.go @@ -11,29 +11,29 @@ import ( "ollie/cmd/olliesrv/internal/agent" "ollie/cmd/olliesrv/internal/bypass" "ollie/cmd/olliesrv/internal/session" - "ollie/fsedsl" + "ollie/virtfs" ) -// Type aliases for fsedsl types used throughout the package. +// Type aliases for virtfs types used throughout the package. type ( - Tree = fsedsl.Tree - File = fsedsl.File - SyntheticFileInfo = fsedsl.SyntheticFileInfo + Tree = virtfs.Tree + File = virtfs.File + SyntheticFileInfo = virtfs.SyntheticFileInfo ) // Structural permissions. const ( - PermChildDir = fsedsl.PermChildDir - PermIdx = fsedsl.PermIdx - PermMkdir = fsedsl.PermMkdir - PermMkdirPrivate = fsedsl.PermMkdirPrivate + PermChildDir = virtfs.PermChildDir + PermIdx = virtfs.PermIdx + PermMkdir = virtfs.PermMkdir + PermMkdirPrivate = virtfs.PermMkdirPrivate ) var treeSpecUID, treeSpecGID string -// buildTreeSpec constructs the full 9P namespace as an fsedsl.FsNodeDecl. +// buildTreeSpec constructs the full 9P namespace as an virtfs.FsNodeDecl. // All handlers are closures that capture their dependencies at binding time. -func buildTreeSpec(rs *RootState) fsedsl.FsNodeDecl { +func buildTreeSpec(rs *RootState) virtfs.FsNodeDecl { cfg := &rs.Cfg mc := cfg.ModelCache broker := cfg.BypassBroker @@ -45,71 +45,71 @@ func buildTreeSpec(rs *RootState) fsedsl.FsNodeDecl { treeSpecUID = u.Username } - return fsedsl.DirNode("/", - fsedsl.UID(treeSpecUID), - fsedsl.GID(treeSpecGID), + return virtfs.DirNode("/", + virtfs.UID(treeSpecUID), + virtfs.GID(treeSpecGID), - fsedsl.FileNode("backends", 0444, - fsedsl.Doc("Available backend names, one per line"), - fsedsl.Read(readBackends), + virtfs.FileNode("backends", 0444, + virtfs.Doc("Available backend names, one per line"), + virtfs.Read(readBackends), ), - fsedsl.FileNode("help", 0444, - fsedsl.Doc("Filesystem reference"), - fsedsl.Read(readHelp), + virtfs.FileNode("help", 0444, + virtfs.Doc("Filesystem reference"), + virtfs.Read(readHelp), ), - fsedsl.FileNode("models", 0444, - fsedsl.Doc("Available models. Format: backendmodel per line."), - fsedsl.Read(func() ([]byte, error) { return readModels(mc) }), + virtfs.FileNode("models", 0444, + virtfs.Doc("Available models. Format: backendmodel per line."), + virtfs.Read(func() ([]byte, error) { return readModels(mc) }), ), - fsedsl.FileNode("agents", 0444, - fsedsl.Doc("Available agent profiles, one per line"), - fsedsl.Read(readAgents), + virtfs.FileNode("agents", 0444, + virtfs.Doc("Available agent profiles, one per line"), + virtfs.Read(readAgents), ), - fsedsl.FileNode("tools", 0444, - fsedsl.Doc("Available tools. Format: namedescription per line."), - fsedsl.Read(readTools), + virtfs.FileNode("tools", 0444, + virtfs.Doc("Available tools. Format: namedescription per line."), + virtfs.Read(readTools), ), - fsedsl.FileNode("ctl", 0666, - fsedsl.Doc("Server control. Write: 'invalidate', 'kill'"), - fsedsl.Request(rootCtlHandler(mc, cfg.Invalidate, cfg.Shutdown)), + virtfs.FileNode("ctl", 0666, + virtfs.Doc("Server control. Write: 'invalidate', 'kill'"), + virtfs.Request(rootCtlHandler(mc, cfg.Invalidate, cfg.Shutdown)), ), - fsedsl.FileNode("eventwait", 0444, - fsedsl.Doc("Blocking read for server events"), - fsedsl.Read(readEventwait), - fsedsl.BlockOnce(blockEventwait), + virtfs.FileNode("eventwait", 0444, + virtfs.Doc("Blocking read for server events"), + virtfs.Read(readEventwait), + virtfs.BlockOnce(blockEventwait), ), - fsedsl.FileNode("generate", 0666, - fsedsl.Doc("One-shot LLM generation"), - fsedsl.Request(func(data []byte) ([]byte, error) { return requestGenerate(cfg.Ctx, data) }), + virtfs.FileNode("generate", 0666, + virtfs.Doc("One-shot LLM generation"), + virtfs.Request(func(data []byte) ([]byte, error) { return requestGenerate(cfg.Ctx, data) }), ), - fsedsl.FileNode("aliases", 0444, - fsedsl.Doc("Alias table. Format: idpath per line."), - fsedsl.Read(readAliases), + virtfs.FileNode("aliases", 0444, + virtfs.Doc("Alias table. Format: idpath per line."), + virtfs.Read(readAliases), ), - fsedsl.DirNode("bypass", - fsedsl.Doc("Bypass broker for sandboxed command approval"), - fsedsl.FileNode("policy", 0666, - fsedsl.Doc("Global bypass policy"), - fsedsl.Read(func() ([]byte, error) { return readBypassPolicy(broker) }), - fsedsl.Write(func(data []byte) error { return writeBypassPolicy(broker, data) }), + virtfs.DirNode("bypass", + virtfs.Doc("Bypass broker for sandboxed command approval"), + virtfs.FileNode("policy", 0666, + virtfs.Doc("Global bypass policy"), + virtfs.Read(func() ([]byte, error) { return readBypassPolicy(broker) }), + virtfs.Write(func(data []byte) error { return writeBypassPolicy(broker, data) }), ), - fsedsl.Each("{reqid}", func() ([]fsedsl.Binding, error) { + virtfs.Each("{reqid}", func() ([]virtfs.Binding, error) { return adaptBypassBindings(broker) }), ), - fsedsl.DirNode("session", - fsedsl.Doc("Session management"), - fsedsl.FileNode("new", 0666, - fsedsl.Doc("Create session"), - fsedsl.Request(requestSessionNew), + virtfs.DirNode("session", + virtfs.Doc("Session management"), + virtfs.FileNode("new", 0666, + virtfs.Doc("Create session"), + virtfs.Request(requestSessionNew), ), - fsedsl.FileNode("idx", 0444, - fsedsl.Doc("Session index"), - fsedsl.Read(readSessionIdx), + virtfs.FileNode("idx", 0444, + virtfs.Doc("Session index"), + virtfs.Read(readSessionIdx), ), - fsedsl.Each("{sname}", func() ([]fsedsl.Binding, error) { + virtfs.Each("{sname}", func() ([]virtfs.Binding, error) { return buildSessionBindings(rs, broker) }), ), @@ -117,9 +117,9 @@ func buildTreeSpec(rs *RootState) fsedsl.FsNodeDecl { } // buildSessionBindings produces bindings for each session. -func buildSessionBindings(rs *RootState, broker *bypass.Broker) ([]fsedsl.Binding, error) { +func buildSessionBindings(rs *RootState, broker *bypass.Broker) ([]virtfs.Binding, error) { sessions := session.Sessions() - var out []fsedsl.Binding + var out []virtfs.Binding for name, sess := range sessions { node := rs.GetOrCreateNode(sess) @@ -138,7 +138,7 @@ func buildSessionBindings(rs *RootState, broker *bypass.Broker) ([]fsedsl.Bindin return nil } - out = append(out, fsedsl.Binding{ + out = append(out, virtfs.Binding{ Name: sess.Name(), Aliases: []string{sess.ID}, Remove: removeFn, @@ -156,39 +156,39 @@ func buildSessionChildren( broker *bypass.Broker, removeFn func(), renameFn func(string) error, -) []fsedsl.FsNodeDecl { - return []fsedsl.FsNodeDecl{ - fsedsl.FileNode("env", 0444, - fsedsl.Read(func() ([]byte, error) { return readSessionEnv(s) }), +) []virtfs.FsNodeDecl { + return []virtfs.FsNodeDecl{ + virtfs.FileNode("env", 0444, + virtfs.Read(func() ([]byte, error) { return readSessionEnv(s) }), ), - fsedsl.FileNode("paused", 0444, - fsedsl.Read(func() ([]byte, error) { return readSessionPaused(s) }), + virtfs.FileNode("paused", 0444, + virtfs.Read(func() ([]byte, error) { return readSessionPaused(s) }), ), - fsedsl.FileNode("connected", 0444, - fsedsl.Read(func() ([]byte, error) { return readSessionConnected(s) }), + virtfs.FileNode("connected", 0444, + virtfs.Read(func() ([]byte, error) { return readSessionConnected(s) }), ), - fsedsl.FileNode("ctl", 0666, - fsedsl.Request(sessionCtlHandler(s, removeFn, renameFn)), + virtfs.FileNode("ctl", 0666, + virtfs.Request(sessionCtlHandler(s, removeFn, renameFn)), ), - fsedsl.FileNode("name", 0666, - fsedsl.Read(func() ([]byte, error) { return readSessionName(s) }), - fsedsl.Write(func(data []byte) error { return writeSessionName(s, renameFn, data) }), + virtfs.FileNode("name", 0666, + virtfs.Read(func() ([]byte, error) { return readSessionName(s) }), + virtfs.Write(func(data []byte) error { return writeSessionName(s, renameFn, data) }), ), - fsedsl.FileNode("id", 0444, - fsedsl.Read(func() ([]byte, error) { return readSessionID(s) }), + virtfs.FileNode("id", 0444, + virtfs.Read(func() ([]byte, error) { return readSessionID(s) }), ), - fsedsl.FileNode("bypass", 0666, - fsedsl.Read(func() ([]byte, error) { return readSessionBypass(broker, s) }), - fsedsl.Write(func(data []byte) error { return writeSessionBypass(broker, s, data) }), + virtfs.FileNode("bypass", 0666, + virtfs.Read(func() ([]byte, error) { return readSessionBypass(broker, s) }), + virtfs.Write(func(data []byte) error { return writeSessionBypass(broker, s, data) }), ), - fsedsl.DirNode("agent", - fsedsl.FileNode("new", 0666, - fsedsl.Request(func(data []byte) ([]byte, error) { return requestAgentNew(s, data) }), + virtfs.DirNode("agent", + virtfs.FileNode("new", 0666, + virtfs.Request(func(data []byte) ([]byte, error) { return requestAgentNew(s, data) }), ), - fsedsl.FileNode("idx", 0444, - fsedsl.Read(func() ([]byte, error) { return readAgentIdx(s.Session) }), + virtfs.FileNode("idx", 0444, + virtfs.Read(func() ([]byte, error) { return readAgentIdx(s.Session) }), ), - fsedsl.Each("{aname}", func() ([]fsedsl.Binding, error) { + virtfs.Each("{aname}", func() ([]virtfs.Binding, error) { return buildAgentBindings(s, rs) }), ), @@ -196,9 +196,9 @@ func buildSessionChildren( } // buildAgentBindings produces bindings for each agent in a session. -func buildAgentBindings(s *SessionNode, rs *RootState) ([]fsedsl.Binding, error) { +func buildAgentBindings(s *SessionNode, rs *RootState) ([]virtfs.Binding, error) { agents := s.Agents() - var out []fsedsl.Binding + var out []virtfs.Binding for _, ag := range agents { a := ag @@ -209,7 +209,7 @@ func buildAgentBindings(s *SessionNode, rs *RootState) ([]fsedsl.Binding, error) wireAgentEvents(s.ID, a, al) } - out = append(out, fsedsl.Binding{ + out = append(out, virtfs.Binding{ Name: a.Name(), Aliases: []string{a.ID()}, Remove: func() { @@ -223,67 +223,67 @@ func buildAgentBindings(s *SessionNode, rs *RootState) ([]fsedsl.Binding, error) } // buildAgentChildren returns the file nodes for a single agent. -func buildAgentChildren(a *agent.Agent, al *AgentLog, s *SessionNode) []fsedsl.FsNodeDecl { - return []fsedsl.FsNodeDecl{ - fsedsl.FileNode("prompt", 0666, - fsedsl.Write(func(data []byte) error { return writeAgentPrompt(s, a, al, data) }), +func buildAgentChildren(a *agent.Agent, al *AgentLog, s *SessionNode) []virtfs.FsNodeDecl { + return []virtfs.FsNodeDecl{ + virtfs.FileNode("prompt", 0666, + virtfs.Write(func(data []byte) error { return writeAgentPrompt(s, a, al, data) }), ), - fsedsl.FileNode("fifo", 0666, - fsedsl.Write(func(data []byte) error { return writeAgentFifoIn(a, data) }), - fsedsl.Read(func() ([]byte, error) { return readAgentFifoOut(a) }), + virtfs.FileNode("fifo", 0666, + virtfs.Write(func(data []byte) error { return writeAgentFifoIn(a, data) }), + virtfs.Read(func() ([]byte, error) { return readAgentFifoOut(a) }), ), - fsedsl.FileNode("chat", 0444, - fsedsl.StatOverride(statAgentChat), - fsedsl.Read(func() ([]byte, error) { return readAgentChatText(al) }), - fsedsl.Stream(func(cctx context.Context, base string) ([]byte, string, error) { + virtfs.FileNode("chat", 0444, + virtfs.StatOverride(statAgentChat), + virtfs.Read(func() ([]byte, error) { return readAgentChatText(al) }), + virtfs.Stream(func(cctx context.Context, base string) ([]byte, string, error) { return streamAgentChatText(s, al, cctx, base) }), ), - fsedsl.FileNode("chat.raw", 0444, - fsedsl.StatOverride(statAgentChat), - fsedsl.Read(func() ([]byte, error) { return readAgentChat(al) }), - fsedsl.Stream(func(cctx context.Context, base string) ([]byte, string, error) { + virtfs.FileNode("chat.raw", 0444, + virtfs.StatOverride(statAgentChat), + virtfs.Read(func() ([]byte, error) { return readAgentChat(al) }), + virtfs.Stream(func(cctx context.Context, base string) ([]byte, string, error) { return streamAgentChat(s, al, cctx, base) }), ), - fsedsl.FileNode("statewait", 0444, - fsedsl.Read(func() ([]byte, error) { + virtfs.FileNode("statewait", 0444, + virtfs.Read(func() ([]byte, error) { return []byte(a.State() + "\n"), nil }), - fsedsl.BlockOnce(func(cctx context.Context, base string) ([]byte, string, error) { + virtfs.BlockOnce(func(cctx context.Context, base string) ([]byte, string, error) { return blockAgentStateWait(s, a, cctx, base) }), ), - fsedsl.FileNode("log", 0444, - fsedsl.Read(func() ([]byte, error) { return readAgentLog(al) }), + virtfs.FileNode("log", 0444, + virtfs.Read(func() ([]byte, error) { return readAgentLog(al) }), ), - fsedsl.FileNode("plan", 0666, - fsedsl.Read(func() ([]byte, error) { return readAgentPlan(al) }), - fsedsl.Write(func(data []byte) error { return writeAgentPlan(al, data) }), + virtfs.FileNode("plan", 0666, + virtfs.Read(func() ([]byte, error) { return readAgentPlan(al) }), + virtfs.Write(func(data []byte) error { return writeAgentPlan(al, data) }), ), - fsedsl.FileNode("cfg", 0666, - fsedsl.Read(func() ([]byte, error) { return readAgentCfg(s, a, al) }), - fsedsl.Write(func(data []byte) error { return writeAgentCfg(a, data) }), + virtfs.FileNode("cfg", 0666, + virtfs.Read(func() ([]byte, error) { return readAgentCfg(s, a, al) }), + virtfs.Write(func(data []byte) error { return writeAgentCfg(a, data) }), ), - fsedsl.FileNode("ctl", 0666, - fsedsl.Request(agentCtlHandler(s, a)), + virtfs.FileNode("ctl", 0666, + virtfs.Request(agentCtlHandler(s, a)), ), - fsedsl.FileNode("stats", 0444, - fsedsl.Read(func() ([]byte, error) { return readAgentStats(a) }), + virtfs.FileNode("stats", 0444, + virtfs.Read(func() ([]byte, error) { return readAgentStats(a) }), ), - fsedsl.FileNode("tools", 0666, - fsedsl.Read(func() ([]byte, error) { return readAgentTools(a) }), - fsedsl.Write(func(data []byte) error { return writeAgentTools(a, data) }), + virtfs.FileNode("tools", 0666, + virtfs.Read(func() ([]byte, error) { return readAgentTools(a) }), + virtfs.Write(func(data []byte) error { return writeAgentTools(a, data) }), ), - fsedsl.FileNode("name", 0666, - fsedsl.Read(func() ([]byte, error) { return readAgentName(a) }), - fsedsl.Write(func(data []byte) error { return writeAgentName(s, a, data) }), + virtfs.FileNode("name", 0666, + virtfs.Read(func() ([]byte, error) { return readAgentName(a) }), + virtfs.Write(func(data []byte) error { return writeAgentName(s, a, data) }), ), - fsedsl.FileNode("id", 0444, - fsedsl.Read(func() ([]byte, error) { return readAgentID(a) }), + virtfs.FileNode("id", 0444, + virtfs.Read(func() ([]byte, error) { return readAgentID(a) }), ), - fsedsl.DirNode("proc", - fsedsl.Each("{pid}", func() ([]fsedsl.Binding, error) { + virtfs.DirNode("proc", + virtfs.Each("{pid}", func() ([]virtfs.Binding, error) { return adaptProcessBindings(s) }), ), @@ -291,22 +291,22 @@ func buildAgentChildren(a *agent.Agent, al *AgentLog, s *SessionNode) []fsedsl.F } // adaptBypassBindings returns bindings for pending bypass requests. -func adaptBypassBindings(broker *bypass.Broker) ([]fsedsl.Binding, error) { +func adaptBypassBindings(broker *bypass.Broker) ([]virtfs.Binding, error) { if broker == nil { return nil, errNoBypass } pending := broker.Pending() - var out []fsedsl.Binding + var out []virtfs.Binding for _, r := range pending { req := r - out = append(out, fsedsl.Binding{ + out = append(out, virtfs.Binding{ Name: req.ID, - Children: []fsedsl.FsNodeDecl{ - fsedsl.FileNode("request", 0666, - fsedsl.Read(func() ([]byte, error) { + Children: []virtfs.FsNodeDecl{ + virtfs.FileNode("request", 0666, + virtfs.Read(func() ([]byte, error) { return readBypassRequest(req) }), - fsedsl.Write(func(data []byte) error { + virtfs.Write(func(data []byte) error { return writeBypassRequest(broker, req, data) }), ), @@ -317,7 +317,7 @@ func adaptBypassBindings(broker *bypass.Broker) ([]fsedsl.Binding, error) { } // adaptProcessBindings returns bindings for detached processes. -func adaptProcessBindings(s *SessionNode) ([]fsedsl.Binding, error) { +func adaptProcessBindings(s *SessionNode) ([]virtfs.Binding, error) { if s == nil { return nil, nil } @@ -327,26 +327,26 @@ func adaptProcessBindings(s *SessionNode) ([]fsedsl.Binding, error) { } defer conn.Close() raw := conn.ListDetachedRaw() - var out []fsedsl.Binding + var out []virtfs.Binding for _, r := range raw { if m, ok := r.(map[string]any); ok { if id, ok := m["pid"].(int); ok { pid := id - out = append(out, fsedsl.Binding{ + out = append(out, virtfs.Binding{ Name: strconv.Itoa(pid), - Children: []fsedsl.FsNodeDecl{ - fsedsl.FileNode("out", 0444, - fsedsl.Read(func() ([]byte, error) { + Children: []virtfs.FsNodeDecl{ + virtfs.FileNode("out", 0444, + virtfs.Read(func() ([]byte, error) { return readProcess(s, pid) }), ), - fsedsl.FileNode("ctl", 0222, - fsedsl.Write(func(data []byte) error { + virtfs.FileNode("ctl", 0222, + virtfs.Write(func(data []byte) error { return writeProcessCtl(s, pid, data) }), ), - fsedsl.FileNode("status", 0444, - fsedsl.Read(func() ([]byte, error) { + virtfs.FileNode("status", 0444, + virtfs.Read(func() ([]byte, error) { return readProcessStatus(s, pid) }), ), diff --git a/cmd/toolsrv/internal/fs/spec.go b/cmd/toolsrv/internal/fs/spec.go index d7e3b0b..42f0e36 100644 --- a/cmd/toolsrv/internal/fs/spec.go +++ b/cmd/toolsrv/internal/fs/spec.go @@ -1,4 +1,4 @@ -// spec.go - fsedsl-based namespace specification for toolsrv. +// spec.go - virtfs-based namespace specification for toolsrv. package fs import ( @@ -8,22 +8,22 @@ import ( "strconv" "strings" - "ollie/fsedsl" + "ollie/virtfs" ) -// Spec returns the fsedsl specification for the toolsrv namespace. +// Spec returns the virtfs specification for the toolsrv namespace. // The server is captured by closures — no context threading needed. -func Spec(srv *Server) fsedsl.FsNodeDecl { - return fsedsl.DirNode("/", - fsedsl.FileNode("ctl", 0222, - fsedsl.Doc("Control: write 'load ' or 'unload '"), - fsedsl.Write(func(data []byte) error { +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))) }), ), - fsedsl.FileNode("tools", 0666, - fsedsl.Doc("Tools: write agent ID, read tool list (rdwr)"), - fsedsl.Request(func(data []byte) ([]byte, error) { + 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") @@ -31,16 +31,16 @@ func Spec(srv *Server) fsedsl.FsNodeDecl { return []byte(srv.Fs.HandleToolsRequest(agentID)), nil }), ), - fsedsl.FileNode("info", 0444, - fsedsl.Doc("Host info: platform, arch"), - fsedsl.Read(func() ([]byte, error) { + virtfs.FileNode("info", 0444, + virtfs.Doc("Host info: platform, arch"), + virtfs.Read(func() ([]byte, error) { return []byte(srv.HostInfo()), nil }), ), - fsedsl.DirNode("proc", - fsedsl.FileNode("new", 0666, - fsedsl.Doc("Execute tool: write token + tool + args, read result (blocking)"), - fsedsl.Request(func(data []byte) ([]byte, error) { + 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 @@ -48,9 +48,9 @@ func Spec(srv *Server) fsedsl.FsNodeDecl { return []byte(result), nil }), ), - fsedsl.FileNode("new.bg", 0666, - fsedsl.Doc("Execute tool in background: write token + tool + args, read pid"), - fsedsl.Request(func(data []byte) ([]byte, error) { + 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 @@ -58,40 +58,40 @@ func Spec(srv *Server) fsedsl.FsNodeDecl { return []byte(result), nil }), ), - fsedsl.Each("{pid}", func() ([]fsedsl.Binding, error) { + virtfs.Each("{pid}", func() ([]virtfs.Binding, error) { pids := srv.Fs.ListProcs() - var bindings []fsedsl.Binding + var bindings []virtfs.Binding for _, pid := range pids { proc := srv.Fs.GetProc(pid) if proc == nil { continue } p := proc - bindings = append(bindings, fsedsl.Binding{ + bindings = append(bindings, virtfs.Binding{ Name: strconv.Itoa(pid), - Children: []fsedsl.FsNodeDecl{ - fsedsl.FileNode("out", 0444, - fsedsl.Doc("Process output"), - fsedsl.Read(func() ([]byte, error) { + Children: []virtfs.FsNodeDecl{ + virtfs.FileNode("out", 0444, + virtfs.Doc("Process output"), + virtfs.Read(func() ([]byte, error) { return []byte(p.Output()), nil }), ), - fsedsl.FileNode("wait", 0444, - fsedsl.Doc("Block until process exits, returns exit code"), - fsedsl.BlockOnce(func(_ context.Context, _ string) ([]byte, string, error) { + 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 }), ), - fsedsl.FileNode("stat", 0444, - fsedsl.Doc("Process status: running/exited, runtime, tool"), - fsedsl.Read(func() ([]byte, error) { + virtfs.FileNode("stat", 0444, + virtfs.Doc("Process status: running/exited, runtime, tool"), + virtfs.Read(func() ([]byte, error) { return []byte(p.Stat()), nil }), ), - fsedsl.FileNode("ctl", 0222, - fsedsl.Doc("Process control: write 'signal ' or 'dismiss'"), - fsedsl.Write(func(data []byte) error { + 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))) }), ), diff --git a/cmd/toolsrv/internal/fs/state.go b/cmd/toolsrv/internal/fs/state.go index 31e6736..828539f 100644 --- a/cmd/toolsrv/internal/fs/state.go +++ b/cmd/toolsrv/internal/fs/state.go @@ -9,7 +9,7 @@ import ( "sync" "ollie/cmd/toolsrv/internal/registry" - "ollie/fsedsl" + "ollie/virtfs" ) // Server holds the state for the toolsrv 9P server. @@ -82,9 +82,9 @@ func (s *Server) HostInfo() string { return fmt.Sprintf("platform=%s\narch=%s\n", runtime.GOOS, runtime.GOARCH) } -// BuildTree creates the fsedsl tree for this server. -func (s *Server) BuildTree() *fsedsl.Tree { - return fsedsl.BuildTree(Spec(s)) +// BuildTree creates the virtfs tree for this server. +func (s *Server) BuildTree() *virtfs.Tree { + return virtfs.BuildTree(Spec(s)) } // GenerateSecret generates a random shared secret for toolsrv auth. diff --git a/cmd/toolsrv/server.go b/cmd/toolsrv/server.go index 9c8145f..5f0d766 100644 --- a/cmd/toolsrv/server.go +++ b/cmd/toolsrv/server.go @@ -10,7 +10,7 @@ import ( "sync" "ollie/cmd/toolsrv/internal/fs" - "ollie/fsedsl" + "ollie/virtfs" "9fans.net/go/plan9" ) @@ -21,7 +21,7 @@ type fid struct { qid plan9.Qid mode uint8 opened bool - entry fsedsl.File + entry virtfs.File readBuf []byte // buffer for read data readDone bool // true if file has been read dirCache []byte @@ -46,8 +46,8 @@ type connState struct { noAuth bool // skip authentication (for debugging) } -// serve9P handles a single 9P connection using the fsedsl tree. -func serve9P(ctx context.Context, conn net.Conn, tree *fsedsl.Tree, srv *fs.Server, noAuth bool) { +// serve9P handles a single 9P connection using the virtfs tree. +func serve9P(ctx context.Context, conn net.Conn, tree *virtfs.Tree, srv *fs.Server, noAuth bool) { defer conn.Close() cs := &connState{ @@ -81,7 +81,7 @@ func serve9P(ctx context.Context, conn net.Conn, tree *fsedsl.Tree, srv *fs.Serv } } -func handleFcall(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleFcall(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { switch fc.Type { case plan9.Tversion: return handleVersion(fc) @@ -133,7 +133,7 @@ func handleAuth(cs *connState, fc *plan9.Fcall) *plan9.Fcall { } } -func handleAttach(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleAttach(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { // Skip auth check if noAuth is set (for debugging) if cs.noAuth { cs.mu.Lock() @@ -210,7 +210,7 @@ func handleAuthRead(cs *connState, fc *plan9.Fcall, af *authFid) *plan9.Fcall { return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Data: data[offset:end]} } -func handleWalk(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleWalk(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { cs.mu.RLock() f, ok := cs.fids[fc.Fid] cs.mu.RUnlock() @@ -288,7 +288,7 @@ func handleWalk(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall return &plan9.Fcall{Type: plan9.Rwalk, Tag: fc.Tag, Wqid: qids} } -func handleOpen(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleOpen(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { cs.mu.Lock() defer cs.mu.Unlock() @@ -325,7 +325,7 @@ func handleOpen(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall return &plan9.Fcall{Type: plan9.Ropen, Tag: fc.Tag, Qid: f.qid, Iounit: 8192} } -func handleRead(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleRead(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { // Check if reading from auth fid cs.mu.RLock() af, isAuthFid := cs.authFids[fc.Fid] @@ -380,7 +380,7 @@ func handleRead(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Data: f.readBuf[offset:end]} } -func handleDirRead(cs *connState, f *fid, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleDirRead(cs *connState, f *fid, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { if fc.Offset == 0 || f.dirCache == nil { path := f.path if path == "/" { @@ -428,7 +428,7 @@ func handleDirRead(cs *connState, f *fid, fc *plan9.Fcall, tree *fsedsl.Tree) *p return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Data: f.dirCache[offset:end]} } -func handleWrite(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleWrite(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { // Check if writing to auth fid cs.mu.Lock() af, isAuthFid := cs.authFids[fc.Fid] @@ -465,7 +465,7 @@ func handleClunk(cs *connState, fc *plan9.Fcall) *plan9.Fcall { return &plan9.Fcall{Type: plan9.Rclunk, Tag: fc.Tag} } -func handleStat(cs *connState, fc *plan9.Fcall, tree *fsedsl.Tree) *plan9.Fcall { +func handleStat(cs *connState, fc *plan9.Fcall, tree *virtfs.Tree) *plan9.Fcall { cs.mu.RLock() f, ok := cs.fids[fc.Fid] cs.mu.RUnlock() diff --git a/fsedsl/go.mod b/fsedsl/go.mod deleted file mode 100644 index 9889f7e..0000000 --- a/fsedsl/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module ollie/fsedsl - -go 1.25.6 diff --git a/go.mod b/go.mod index 293ab9f..5f5ab5a 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,9 @@ require ( github.com/simonfxr/pubsub v0.0.5 golang.org/x/time v0.15.0 gopkg.in/yaml.v3 v3.0.1 - ollie/fsedsl v0.0.0 + ollie/virtfs v0.0.0 ) require golang.org/x/sys v0.47.0 // indirect -replace ollie/fsedsl => ./fsedsl +replace ollie/virtfs => ./virtfs diff --git a/fsedsl/LICENSE.md b/virtfs/LICENSE.md similarity index 100% rename from fsedsl/LICENSE.md rename to virtfs/LICENSE.md diff --git a/fsedsl/README.md b/virtfs/README.md similarity index 79% rename from fsedsl/README.md rename to virtfs/README.md index a2ee913..8a8cba8 100644 --- a/fsedsl/README.md +++ b/virtfs/README.md @@ -1,4 +1,4 @@ -# fsedsl +# virtfs A Go library providing an embedded domain-specific language (EDSL) for declaring synthetic filesystems. Define your namespace structure statically while populating it dynamically at runtime. @@ -14,19 +14,19 @@ The library produces a generic `Tree` that can be used with any filesystem proto ## Installation -The library is currently embedded in the [ollie](https://git.lneely.de/lkn/ollie) repository at `fsedsl/`. +The library is currently embedded in the [ollie](https://git.lneely.de/lkn/ollie) repository at `virtfs/`. To use it in your project, add a replace directive to your `go.mod`: ```bash -# Clone or copy the fsedsl directory to your project, then: -go mod edit -replace=git.lneely.de/lkn/fsedsl=./fsedsl +# Clone or copy the virtfs directory to your project, then: +go mod edit -replace=git.lneely.de/lkn/virtfs=./virtfs ``` Or reference it directly from the ollie repo: ```bash -go mod edit -replace=git.lneely.de/lkn/fsedsl=git.lneely.de/lkn/ollie/fsedsl@main +go mod edit -replace=git.lneely.de/lkn/virtfs=git.lneely.de/lkn/ollie/virtfs@main ``` ## Quick Start @@ -36,7 +36,7 @@ package main import ( "fmt" - "git.lneely.de/lkn/fsedsl" + "git.lneely.de/lkn/virtfs" ) type Ctx struct { @@ -55,18 +55,18 @@ var users = []*User{ func main() { // Declare the filesystem spec - spec := fsedsl.DirNode[Ctx]("/", - fsedsl.FileNode[Ctx]("version", 0444, - fsedsl.Doc[Ctx]("API version"), - fsedsl.Read(func(ctx Ctx) ([]byte, error) { + spec := virtfs.DirNode[Ctx]("/", + virtfs.FileNode[Ctx]("version", 0444, + virtfs.Doc[Ctx]("API version"), + virtfs.Read(func(ctx Ctx) ([]byte, error) { return []byte("1.0\n"), nil }), ), - fsedsl.Each[Ctx]("{user}", func(ctx Ctx) ([]fsedsl.Binding[Ctx], error) { - var out []fsedsl.Binding[Ctx] + virtfs.Each[Ctx]("{user}", func(ctx Ctx) ([]virtfs.Binding[Ctx], error) { + var out []virtfs.Binding[Ctx] for _, u := range users { user := u // capture - out = append(out, fsedsl.Binding[Ctx]{ + out = append(out, virtfs.Binding[Ctx]{ Name: user.Name, Aliases: []string{user.ID}, Applier: func(c Ctx) Ctx { @@ -77,13 +77,13 @@ func main() { } return out, nil }, - fsedsl.FileNode[Ctx]("id", 0444, - fsedsl.Read(func(ctx Ctx) ([]byte, error) { + virtfs.FileNode[Ctx]("id", 0444, + virtfs.Read(func(ctx Ctx) ([]byte, error) { return []byte(ctx.User.ID + "\n"), nil }), ), - fsedsl.FileNode[Ctx]("name", 0444, - fsedsl.Read(func(ctx Ctx) ([]byte, error) { + virtfs.FileNode[Ctx]("name", 0444, + virtfs.Read(func(ctx Ctx) ([]byte, error) { return []byte(ctx.User.Name + "\n"), nil }), ), @@ -91,7 +91,7 @@ func main() { ) // Build the tree - tree := fsedsl.BuildTree(spec, Ctx{}) + tree := virtfs.BuildTree(spec, Ctx{}) // Use the tree entries, _ := tree.List() @@ -112,7 +112,7 @@ Define your context type, then create type and function aliases that bake it in: ```go package fs -import "git.lneely.de/lkn/fsedsl" +import "git.lneely.de/lkn/virtfs" // Ctx carries runtime dependencies for handler functions. type Ctx struct { @@ -122,19 +122,19 @@ type Ctx struct { // Type aliases for external consumers type ( - FsNodeDecl = fsedsl.FsNodeDecl[Ctx] - Binding = fsedsl.Binding[Ctx] - Tree = fsedsl.Tree + FsNodeDecl = virtfs.FsNodeDecl[Ctx] + Binding = virtfs.Binding[Ctx] + Tree = virtfs.Tree ) // Function aliases for clean spec definitions var ( - DirNode = fsedsl.DirNode[Ctx] - FileNode = fsedsl.FileNode[Ctx] - Each = fsedsl.Each[Ctx] - Doc = fsedsl.Doc[Ctx] - Read = fsedsl.Read[Ctx] - Write = fsedsl.Write[Ctx] + DirNode = virtfs.DirNode[Ctx] + FileNode = virtfs.FileNode[Ctx] + Each = virtfs.Each[Ctx] + Doc = virtfs.Doc[Ctx] + Read = virtfs.Read[Ctx] + Write = virtfs.Write[Ctx] // ... add others as needed ) @@ -172,7 +172,7 @@ func userBindings(ctx Ctx) ([]Binding, error) { Build and use: ```go -tree := fsedsl.BuildTree(fs.Spec, fs.Ctx{DB: db}) +tree := virtfs.BuildTree(fs.Spec, fs.Ctx{DB: db}) entries, _ := tree.List() f, _ := tree.Open("alice/name") data, _ := f.Read() @@ -208,7 +208,7 @@ data, _ := f.Read() ### Binding ```go -fsedsl.Binding[Ctx]{ +virtfs.Binding[Ctx]{ Name: "filename", // Primary name Aliases: []string{"alt-name"}, // Alternate lookup names UID: "owner", // Owner (optional) @@ -225,7 +225,7 @@ fsedsl.Binding[Ctx]{ ### Tree Operations ```go -tree := fsedsl.BuildTree(spec, rootCtx) +tree := virtfs.BuildTree(spec, rootCtx) tree.List() // List entries tree.Stat(name) // Get file info @@ -239,7 +239,7 @@ tree.Readdir(path) // List subdirectory ### Help Generation ```go -help := fsedsl.GenerateHelp(spec) +help := virtfs.GenerateHelp(spec) // Returns formatted documentation from Doc() annotations ``` diff --git a/fsedsl/builder.go b/virtfs/builder.go similarity index 99% rename from fsedsl/builder.go rename to virtfs/builder.go index fe7953e..640fe97 100644 --- a/fsedsl/builder.go +++ b/virtfs/builder.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "context" diff --git a/fsedsl/decl.go b/virtfs/decl.go similarity index 98% rename from fsedsl/decl.go rename to virtfs/decl.go index a57424f..afaaaf1 100644 --- a/fsedsl/decl.go +++ b/virtfs/decl.go @@ -1,4 +1,4 @@ -// Package fsedsl provides an embedded domain-specific language for declaring +// Package virtfs provides an embedded domain-specific language for declaring // synthetic filesystems. It is designed for building 9P namespaces where the // entire filesystem structure is declared statically but populated dynamically. // @@ -30,7 +30,7 @@ // ) // // tree := BuildTree(spec) -package fsedsl +package virtfs import ( "context" diff --git a/fsedsl/decl_test.go b/virtfs/decl_test.go similarity index 99% rename from fsedsl/decl_test.go rename to virtfs/decl_test.go index 52b5e01..ad9f233 100644 --- a/fsedsl/decl_test.go +++ b/virtfs/decl_test.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "context" diff --git a/fsedsl/file.go b/virtfs/file.go similarity index 99% rename from fsedsl/file.go rename to virtfs/file.go index 8e1a776..f9ea676 100644 --- a/fsedsl/file.go +++ b/virtfs/file.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "context" diff --git a/virtfs/go.mod b/virtfs/go.mod new file mode 100644 index 0000000..0160ba9 --- /dev/null +++ b/virtfs/go.mod @@ -0,0 +1,3 @@ +module ollie/virtfs + +go 1.25.6 diff --git a/fsedsl/help.go b/virtfs/help.go similarity index 98% rename from fsedsl/help.go rename to virtfs/help.go index 36956de..aebc7fd 100644 --- a/fsedsl/help.go +++ b/virtfs/help.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "fmt" diff --git a/fsedsl/tree.go b/virtfs/tree.go similarity index 99% rename from fsedsl/tree.go rename to virtfs/tree.go index d04898e..d74f0e6 100644 --- a/fsedsl/tree.go +++ b/virtfs/tree.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "fmt" diff --git a/fsedsl/tree_test.go b/virtfs/tree_test.go similarity index 99% rename from fsedsl/tree_test.go rename to virtfs/tree_test.go index 9d8c140..eb3d224 100644 --- a/fsedsl/tree_test.go +++ b/virtfs/tree_test.go @@ -1,4 +1,4 @@ -package fsedsl +package virtfs import ( "context"