toolsrv: move Registry to cmd/toolsrv, keep only client SDK in shared package
Split toolsrv/registry.go: - cmd/toolsrv/registry.go: Registry type with session-scoped tool state (Load, Unload, Loaded, Lookup, Revision methods) - toolsrv/registry.go: shared types only (ToolInfo, ToolsPath, DiscoverTools) The toolsrv/ package is now a pure client SDK: - Dial, Conn for 9P connection - ToolInfo, ToolResult, HostInfo types - DiscoverTools for listing available tools Server-only code lives in cmd/toolsrv/: - Registry for session-scoped tool state - 9P handlers and execution logic - Sandbox integration
This commit is contained in:
parent
a0315a558a
commit
e8239927de
|
|
@ -47,7 +47,7 @@ type FS9P struct {
|
|||
|
||||
cwd string
|
||||
env map[string]string
|
||||
registry *toolsrv.Registry
|
||||
registry *Registry
|
||||
sessID string
|
||||
yolo bool
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ func NewFS9P(cwd string) *FS9P {
|
|||
}
|
||||
|
||||
// SetRegistry configures the tool registry and session ID.
|
||||
func (fs *FS9P) SetRegistry(r *toolsrv.Registry, sessID string) {
|
||||
func (fs *FS9P) SetRegistry(r *Registry, sessID string) {
|
||||
fs.mu.Lock()
|
||||
fs.registry = r
|
||||
fs.sessID = sessID
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
func TestFS9P_Basic(t *testing.T) {
|
||||
|
|
@ -78,7 +76,7 @@ func TestFS9P_ProcLimit(t *testing.T) {
|
|||
fs.procLimit = 2
|
||||
|
||||
// Create a fake registry and session ID
|
||||
reg, err := toolsrv.NewRegistry()
|
||||
reg, err := NewRegistry()
|
||||
if err != nil {
|
||||
t.Skip("no tools directory available")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
|
||||
"ollie/env"
|
||||
"ollie/fsedsl"
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -55,7 +54,7 @@ func main() {
|
|||
os.Setenv("PATH", binDir+":"+os.Getenv("PATH"))
|
||||
|
||||
// Create tool registry
|
||||
toolReg, _ := toolsrv.NewRegistry()
|
||||
toolReg, _ := NewRegistry()
|
||||
sid := *sessionID
|
||||
if sid == "" {
|
||||
sid = os.Getenv("OLLIE_SESSION_ID") // fallback to env var
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
// registry.go - Session-scoped tool registry for toolsrv.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// Registry tracks loaded tools per session.
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
sessions map[string]map[string]toolsrv.ToolInfo
|
||||
revisions map[string]uint64
|
||||
}
|
||||
|
||||
// NewRegistry creates a new tool registry.
|
||||
func NewRegistry() (*Registry, error) {
|
||||
r := &Registry{
|
||||
sessions: make(map[string]map[string]toolsrv.ToolInfo),
|
||||
revisions: make(map[string]uint64),
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// discover scans the tools directory and returns all valid tool infos.
|
||||
func (r *Registry) discover() map[string]toolsrv.ToolInfo {
|
||||
infos := toolsrv.DiscoverTools()
|
||||
tools := make(map[string]toolsrv.ToolInfo, len(infos))
|
||||
for _, info := range infos {
|
||||
tools[info.Name] = info
|
||||
}
|
||||
return tools
|
||||
}
|
||||
|
||||
// Load loads a tool into a session's registry.
|
||||
func (r *Registry) Load(sessionID, name string) error {
|
||||
tools := r.discover()
|
||||
tool, exists := tools[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("tool not found: %s", name)
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.sessions[sessionID] == nil {
|
||||
r.sessions[sessionID] = make(map[string]toolsrv.ToolInfo)
|
||||
}
|
||||
|
||||
r.sessions[sessionID][name] = tool
|
||||
r.revisions[sessionID]++
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unload removes a tool from a session's registry.
|
||||
func (r *Registry) Unload(sessionID, name string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, exists := sessionTools[name]; !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(sessionTools, name)
|
||||
r.revisions[sessionID]++
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loaded returns all tools loaded for a session.
|
||||
func (r *Registry) Loaded(sessionID string) []toolsrv.ToolInfo {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var loaded []toolsrv.ToolInfo
|
||||
for _, info := range sessionTools {
|
||||
loaded = append(loaded, info)
|
||||
}
|
||||
sort.Slice(loaded, func(i, j int) bool { return loaded[i].Name < loaded[j].Name })
|
||||
return loaded
|
||||
}
|
||||
|
||||
// Lookup returns a tool by name from a session's registry.
|
||||
func (r *Registry) Lookup(sessionID, name string) (toolsrv.ToolInfo, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return toolsrv.ToolInfo{}, false
|
||||
}
|
||||
|
||||
tool, exists := sessionTools[name]
|
||||
return tool, exists
|
||||
}
|
||||
|
||||
// Revision returns the revision counter for a session's registry.
|
||||
func (r *Registry) Revision(sessionID string) uint64 {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.revisions[sessionID]
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"sync"
|
||||
|
||||
"ollie/fsedsl"
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// Server9P holds the state for the 9P tool server.
|
||||
|
|
@ -18,7 +17,7 @@ type Server9P struct {
|
|||
|
||||
secret string // set on first auth, verified on subsequent
|
||||
token string // session token returned after auth
|
||||
registry *toolsrv.Registry // tool registry
|
||||
registry *Registry // tool registry
|
||||
sessID string // session ID for registry scoping
|
||||
yolo bool // skip sandbox
|
||||
|
||||
|
|
@ -34,7 +33,7 @@ func NewServer9P() *Server9P {
|
|||
}
|
||||
|
||||
// SetRegistry configures the tool registry and session ID.
|
||||
func (s *Server9P) SetRegistry(r *toolsrv.Registry, sessID string) {
|
||||
func (s *Server9P) SetRegistry(r *Registry, sessID string) {
|
||||
s.mu.Lock()
|
||||
s.registry = r
|
||||
s.sessID = sessID
|
||||
|
|
|
|||
|
|
@ -1,121 +1,15 @@
|
|||
// registry.go - Tool discovery and shared types.
|
||||
package toolsrv
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"ollie/paths"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
sessions map[string]map[string]ToolInfo
|
||||
revisions map[string]uint64
|
||||
}
|
||||
|
||||
func NewRegistry() (*Registry, error) {
|
||||
r := &Registry{
|
||||
sessions: make(map[string]map[string]ToolInfo),
|
||||
revisions: make(map[string]uint64),
|
||||
}
|
||||
// Validate that the tools directory is readable.
|
||||
dir := ToolsPath()
|
||||
if _, err := os.ReadDir(dir); err != nil {
|
||||
return nil, fmt.Errorf("read tools dir %s: %w", dir, err)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// discover scans the tools directory and returns all valid tool infos.
|
||||
func (r *Registry) discover() map[string]ToolInfo {
|
||||
infos := DiscoverTools()
|
||||
tools := make(map[string]ToolInfo, len(infos))
|
||||
for _, info := range infos {
|
||||
tools[info.Name] = info
|
||||
}
|
||||
return tools
|
||||
}
|
||||
|
||||
func (r *Registry) Load(sessionID, name string) error {
|
||||
tools := r.discover()
|
||||
tool, exists := tools[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("tool not found: %s", name)
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.sessions[sessionID] == nil {
|
||||
r.sessions[sessionID] = make(map[string]ToolInfo)
|
||||
}
|
||||
|
||||
r.sessions[sessionID][name] = tool
|
||||
r.revisions[sessionID]++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) Unload(sessionID, name string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, exists := sessionTools[name]; !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(sessionTools, name)
|
||||
r.revisions[sessionID]++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) Loaded(sessionID string) []ToolInfo {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var loaded []ToolInfo
|
||||
for _, info := range sessionTools {
|
||||
loaded = append(loaded, info)
|
||||
}
|
||||
sort.Slice(loaded, func(i, j int) bool { return loaded[i].Name < loaded[j].Name })
|
||||
return loaded
|
||||
}
|
||||
|
||||
func (r *Registry) Lookup(sessionID, name string) (ToolInfo, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
sessionTools, ok := r.sessions[sessionID]
|
||||
if !ok {
|
||||
return ToolInfo{}, false
|
||||
}
|
||||
|
||||
tool, exists := sessionTools[name]
|
||||
return tool, exists
|
||||
}
|
||||
|
||||
func (r *Registry) Revision(sessionID string) uint64 {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.revisions[sessionID]
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ToolInfo describes a tool provided by a server.
|
||||
type ToolInfo struct {
|
||||
Server string
|
||||
|
|
|
|||
Loading…
Reference in New Issue