refactor: move olliesrv and toolsrv packages to cmd/*/internal/

Move server-only packages under their respective cmd directories:

olliesrv:
- agent/ -> cmd/olliesrv/internal/agent/
- backend/ -> cmd/olliesrv/internal/backend/
- bypass/ -> cmd/olliesrv/internal/bypass/
- fs/ -> cmd/olliesrv/internal/fs/
- prompts/ -> cmd/olliesrv/internal/prompts/
- session/ -> cmd/olliesrv/internal/session/

toolsrv:
- Server-only code (exec9p, fs9p, server9p, spec9p, auth9p) -> cmd/toolsrv/
- sandbox/ -> cmd/toolsrv/internal/sandbox/
- Keep shared client code (client9p, spawn, registry, meta) in toolsrv/
- Add toolsrv/types.go for shared types (ToolResult, ToolResultContent)

This enforces package boundaries - code in cmd/*/internal/ cannot be
imported by external packages, while shared code remains importable.
This commit is contained in:
Levi Neely 2026-08-10 20:16:41 +02:00
parent 85df48b0c3
commit 1fc051e3bf
82 changed files with 127 additions and 118 deletions

View File

@ -5,7 +5,7 @@ import (
"sync"
"github.com/godbus/dbus/v5"
"ollie/bypass"
"ollie/cmd/olliesrv/internal/bypass"
)
// bypassNotifier handles desktop notifications for bypass requests

View File

@ -9,7 +9,7 @@ import (
"sync/atomic"
"syscall"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
olog "ollie/log"
"ollie/paths"
"ollie/toolsrv"

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"io"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
)
// Prompt holds the agent prompt — a list of file paths to read and concatenate.

View File

@ -3,7 +3,7 @@ package agent
import (
"strings"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
)
// auditTruncate trims s to 200 runes for log output.

View File

@ -7,7 +7,7 @@ import (
"os"
"strings"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
"ollie/paths"
"gopkg.in/yaml.v3"

View File

@ -10,7 +10,7 @@ import (
"sync"
"time"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
"ollie/paths"
"ollie/toolsrv"
)

View File

@ -8,8 +8,8 @@ import (
"strings"
"time"
"ollie/backend"
"ollie/prompts"
"ollie/cmd/olliesrv/internal/backend"
"ollie/cmd/olliesrv/internal/prompts"
"ollie/toolsrv"
)

View File

@ -5,7 +5,7 @@ import (
"fmt"
"strings"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
)
// parseTextToolCalls extracts tool calls from assistant text when the model

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
"ollie/toolsrv"
)

View File

@ -12,7 +12,7 @@ import (
"testing"
"time"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
)
func mustNewOpenAI(t *testing.T, name, baseURL, apiKey string) *backend.OpenAIBackend {

View File

@ -3,8 +3,8 @@ package fs
import (
"sync"
"ollie/agent"
"ollie/backend"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/backend"
"ollie/format"
)

View File

@ -5,7 +5,7 @@ import (
"strings"
"sync"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
)
// ModelCache caches the model list from all backends.

View File

@ -8,9 +8,9 @@ import (
"strconv"
"strings"
"ollie/agent"
"ollie/cmd/olliesrv/internal/agent"
"ollie/paths"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
)
func requestAgentNew(ctx HandlerCtx, data []byte) ([]byte, error) {

View File

@ -8,11 +8,11 @@ import (
"strings"
"sync"
"ollie/agent"
"ollie/backend"
"ollie/bypass"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/backend"
"ollie/cmd/olliesrv/internal/bypass"
"ollie/fsedsl"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
"ollie/toolsrv"
)

View File

@ -5,8 +5,8 @@ import (
"os"
"strings"
"ollie/bypass"
"ollie/session"
"ollie/cmd/olliesrv/internal/bypass"
"ollie/cmd/olliesrv/internal/session"
)
func readSessionIdx(_ HandlerCtx) ([]byte, error) {

View File

@ -3,7 +3,7 @@ package fs
import (
"sync"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
)
// RootState holds the root-level state for the 9P filesystem.

View File

@ -4,10 +4,10 @@ import (
"context"
"os/user"
"ollie/bypass"
"ollie/cmd/olliesrv/internal/bypass"
"ollie/fsedsl"
olog "ollie/log"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
)
// Config carries all runtime dependencies for the filesystem tree.

View File

@ -5,7 +5,7 @@ import (
"strings"
"testing"
"ollie/backend"
"ollie/cmd/olliesrv/internal/backend"
"ollie/format"
)

View File

@ -6,8 +6,8 @@ import (
"sync"
"time"
"ollie/agent"
"ollie/session"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/session"
"ollie/toolsrv"
)

View File

@ -7,8 +7,8 @@ package fs
import (
"context"
"ollie/agent"
"ollie/bypass"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/bypass"
"ollie/fsedsl"
olog "ollie/log"
)

View File

@ -9,8 +9,8 @@ import (
"strings"
"sync"
"ollie/agent"
"ollie/backend"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/backend"
)
// --- Persistence ---

View File

@ -17,8 +17,8 @@ import (
"sync"
"time"
"ollie/agent"
"ollie/backend"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/backend"
olog "ollie/log"
"ollie/paths"
)

View File

@ -9,7 +9,7 @@ import (
"sync"
"time"
"ollie/agent"
"ollie/cmd/olliesrv/internal/agent"
olog "ollie/log"
"ollie/paths"
"ollie/toolsrv"

View File

@ -4,9 +4,9 @@ import (
"context"
"fmt"
"ollie/agent"
"ollie/cmd/olliesrv/internal/agent"
"ollie/paths"
"ollie/prompts"
"ollie/cmd/olliesrv/internal/prompts"
"ollie/toolsrv"
)

View File

@ -14,13 +14,13 @@ import (
"syscall"
"9fans.net/go/plan9/client"
"ollie/agent"
"ollie/bypass"
"ollie/cmd/olliesrv/internal/agent"
"ollie/cmd/olliesrv/internal/bypass"
"ollie/env"
"ollie/fs"
"ollie/cmd/olliesrv/internal/fs"
olog "ollie/log"
"ollie/paths"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
"github.com/godbus/dbus/v5"
)

View File

@ -14,9 +14,9 @@ import (
"sync"
"time"
"ollie/fs"
"ollie/cmd/olliesrv/internal/fs"
olog "ollie/log"
"ollie/session"
"ollie/cmd/olliesrv/internal/session"
"9fans.net/go/plan9"
)

View File

@ -1,5 +1,5 @@
// auth9p.go - Authentication helpers for 9P toolsrv
package toolsrv
package main
import (
"crypto/rand"

View File

@ -1,5 +1,5 @@
// exec9p.go - Decoupled tool execution for 9P toolsrv
package toolsrv
package main
import (
"bytes"
@ -16,8 +16,9 @@ import (
"syscall"
"time"
"ollie/cmd/toolsrv/internal/sandbox"
"ollie/paths"
"ollie/sandbox"
"ollie/toolsrv"
)
// ExecConfig contains all context needed for tool execution.
@ -29,20 +30,6 @@ type ExecConfig struct {
Timeout int
}
// ToolResult is the JSON structure returned by tool execution.
type ToolResult struct {
Content []ToolResultContent `json:"content"`
IsError bool `json:"isError,omitempty"`
}
// ToolResultContent is a single content item in a tool result.
type ToolResultContent struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
MediaType string `json:"mediaType,omitempty"`
Data string `json:"data,omitempty"`
}
// StreamFunc returns the streaming output function from context, if any.
func StreamFunc(ctx context.Context) func(string) {
if fn, ok := ctx.Value(streamKey{}).(func(string)); ok {
@ -60,9 +47,9 @@ func WithStreamFunc(ctx context.Context, fn func(string)) context.Context {
// ExecuteTool runs a tool script with the given args and returns the result.
// This is the core execution function, decoupled from Server.
func ExecuteTool(ctx context.Context, info ToolInfo, args json.RawMessage, cfg ExecConfig) (json.RawMessage, error) {
func ExecuteTool(ctx context.Context, info toolsrv.ToolInfo, args json.RawMessage, cfg ExecConfig) (json.RawMessage, error) {
// Resolve script path
toolPath, err := ResolveTool(info.Name)
toolPath, err := toolsrv.ResolveTool(info.Name)
if err != nil {
return nil, fmt.Errorf("resolve tool %s: %w", info.Name, err)
}
@ -109,7 +96,7 @@ func ExecuteTool(ctx context.Context, info ToolInfo, args json.RawMessage, cfg E
// Check if tool requires sudo
needsSudo := false
if m, err := LoadMetaFile(info.Name); err == nil && m != nil {
if m, err := toolsrv.LoadMetaFile(info.Name); err == nil && m != nil {
resolved := m.Resolve()
if resolved != nil && resolved.Sudo {
needsSudo = true
@ -135,13 +122,13 @@ func ExecuteTool(ctx context.Context, info ToolInfo, args json.RawMessage, cfg E
}
if err != nil {
return json.Marshal(ToolResult{
return json.Marshal(toolsrv.ToolResult{
IsError: true,
Content: []ToolResultContent{{Type: "text", Text: result + ": " + err.Error()}},
Content: []toolsrv.ToolResultContent{{Type: "text", Text: result + ": " + err.Error()}},
})
}
return json.Marshal(ToolResult{
Content: []ToolResultContent{{Type: "text", Text: result}},
return json.Marshal(toolsrv.ToolResult{
Content: []toolsrv.ToolResultContent{{Type: "text", Text: result}},
})
}

View File

@ -21,7 +21,7 @@
// tool=shell
// cmd=ls -la
// timeout=30
package toolsrv
package main
import (
"bytes"
@ -36,6 +36,8 @@ import (
"sync/atomic"
"syscall"
"time"
"ollie/toolsrv"
)
// FS9P implements a 9P-style tool server.
@ -45,7 +47,7 @@ type FS9P struct {
cwd string
env map[string]string
registry *Registry
registry *toolsrv.Registry
sessID string
yolo bool
@ -88,7 +90,7 @@ func NewFS9P(cwd string) *FS9P {
}
// SetRegistry configures the tool registry and session ID.
func (fs *FS9P) SetRegistry(r *Registry, sessID string) {
func (fs *FS9P) SetRegistry(r *toolsrv.Registry, sessID string) {
fs.mu.Lock()
fs.registry = r
fs.sessID = sessID
@ -136,7 +138,7 @@ func (fs *FS9P) GetEnv(key string) string {
// --- Tool Registry ---
// ListTools returns loaded tools for the current session.
func (fs *FS9P) ListTools() []ToolInfo {
func (fs *FS9P) ListTools() []toolsrv.ToolInfo {
fs.mu.RLock()
reg := fs.registry
sid := fs.sessID
@ -299,7 +301,7 @@ func (fs *FS9P) NewProc(ctx context.Context, payload string, background bool) (r
}
// executeTool runs a tool and returns output + exit code.
func (fs *FS9P) executeTool(ctx context.Context, info ToolInfo, args map[string]string, cwd string, envExtra map[string]string, yolo bool) (string, int) {
func (fs *FS9P) executeTool(ctx context.Context, info toolsrv.ToolInfo, args map[string]string, cwd string, envExtra map[string]string, yolo bool) (string, int) {
// Convert args map to JSON for the execution path
jsonArgs := argsToJSON(args)

View File

@ -1,10 +1,12 @@
package toolsrv
package main
import (
"context"
"strings"
"testing"
"time"
"ollie/toolsrv"
)
func TestFS9P_Basic(t *testing.T) {
@ -76,7 +78,7 @@ func TestFS9P_ProcLimit(t *testing.T) {
fs.procLimit = 2
// Create a fake registry and session ID
reg, err := NewRegistry()
reg, err := toolsrv.NewRegistry()
if err != nil {
t.Skip("no tools directory available")
}

View File

@ -2,7 +2,7 @@
//
// These tests build and spawn a real toolsrv process, then connect via 9P
// to verify the authentication flow works correctly.
package toolsrv
package main
import (
"context"
@ -14,6 +14,8 @@ import (
"strings"
"testing"
"time"
"ollie/toolsrv"
)
var testToolsrvBinary string
@ -95,7 +97,7 @@ func TestIntegration_FirstConnectionSetsSecret(t *testing.T) {
// First connection with a secret should succeed
secret := "test-secret-first-connection"
conn, err := Dial(socketPath, secret)
conn, err := toolsrv.Dial(socketPath, secret)
if err != nil {
t.Fatalf("first Dial failed: %v", err)
}
@ -119,7 +121,7 @@ func TestIntegration_SameSecretReconnects(t *testing.T) {
secret := "test-secret-reconnect"
// First connection establishes the secret
conn1, err := Dial(socketPath, secret)
conn1, err := toolsrv.Dial(socketPath, secret)
if err != nil {
t.Fatalf("first Dial failed: %v", err)
}
@ -127,7 +129,7 @@ func TestIntegration_SameSecretReconnects(t *testing.T) {
conn1.Close()
// Second connection with same secret should succeed
conn2, err := Dial(socketPath, secret)
conn2, err := toolsrv.Dial(socketPath, secret)
if err != nil {
t.Fatalf("second Dial failed: %v", err)
}
@ -144,14 +146,14 @@ func TestIntegration_WrongSecretFails(t *testing.T) {
defer cleanup()
// First connection establishes the secret
conn1, err := Dial(socketPath, "correct-secret")
conn1, err := toolsrv.Dial(socketPath, "correct-secret")
if err != nil {
t.Fatalf("first Dial failed: %v", err)
}
conn1.Close()
// Second connection with wrong secret should fail
conn2, err := Dial(socketPath, "wrong-secret")
conn2, err := toolsrv.Dial(socketPath, "wrong-secret")
if err == nil {
conn2.Close()
t.Fatal("Dial with wrong secret should fail")
@ -163,7 +165,7 @@ func TestIntegration_EmptySecretGeneratesOne(t *testing.T) {
defer cleanup()
// Empty secret should generate a random one
conn, err := Dial(socketPath, "")
conn, err := toolsrv.Dial(socketPath, "")
if err != nil {
t.Fatalf("Dial with empty secret failed: %v", err)
}
@ -184,7 +186,7 @@ func TestIntegration_BasicOperations(t *testing.T) {
socketPath, cleanup := startTestServer(t)
defer cleanup()
conn, err := Dial(socketPath, "test-secret-ops")
conn, err := toolsrv.Dial(socketPath, "test-secret-ops")
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
@ -214,14 +216,14 @@ func TestIntegration_BasicOperations(t *testing.T) {
func TestIntegration_ToolExecution(t *testing.T) {
// Skip if tools directory doesn't exist (CI environment)
if _, err := os.ReadDir(ToolsPath()); err != nil {
if _, err := os.ReadDir(toolsrv.ToolsPath()); err != nil {
t.Skipf("skipping: tools directory not available: %v", err)
}
socketPath, cleanup := startTestServer(t)
defer cleanup()
conn, err := Dial(socketPath, "test-secret-exec")
conn, err := toolsrv.Dial(socketPath, "test-secret-exec")
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
@ -257,14 +259,14 @@ func TestIntegration_ToolExecution(t *testing.T) {
}
// Parse the result the same way agent/runtime.go does
var toolResult ToolResult
var toolResult toolsrv.ToolResult
if err := json.Unmarshal(result, &toolResult); err != nil {
t.Fatalf("failed to parse ToolResult: %v (raw: %s)", err, string(result))
t.Fatalf("failed to parse toolsrv.ToolResult: %v (raw: %s)", err, string(result))
}
// Verify structure
if len(toolResult.Content) == 0 {
t.Fatal("ToolResult.Content is empty")
t.Fatal("toolsrv.ToolResult.Content is empty")
}
if toolResult.Content[0].Type != "text" {
t.Errorf("expected type 'text', got %q", toolResult.Content[0].Type)
@ -273,7 +275,7 @@ func TestIntegration_ToolExecution(t *testing.T) {
t.Errorf("output doesn't contain expected text: %q", toolResult.Content[0].Text)
}
if toolResult.IsError {
t.Error("ToolResult.IsError should be false")
t.Error("toolsrv.ToolResult.IsError should be false")
}
t.Logf("Tool result: %+v", toolResult)
@ -281,14 +283,14 @@ func TestIntegration_ToolExecution(t *testing.T) {
func TestIntegration_MultilineContent(t *testing.T) {
// Skip if tools directory doesn't exist (CI environment)
if _, err := os.ReadDir(ToolsPath()); err != nil {
if _, err := os.ReadDir(toolsrv.ToolsPath()); err != nil {
t.Skipf("skipping: tools directory not available: %v", err)
}
socketPath, cleanup := startTestServer(t)
defer cleanup()
conn, err := Dial(socketPath, "test-secret-multiline")
conn, err := toolsrv.Dial(socketPath, "test-secret-multiline")
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
@ -312,9 +314,9 @@ func TestIntegration_MultilineContent(t *testing.T) {
t.Fatalf("CallTool failed: %v", err)
}
var toolResult ToolResult
var toolResult toolsrv.ToolResult
if err := json.Unmarshal(result, &toolResult); err != nil {
t.Fatalf("failed to parse ToolResult: %v (raw: %s)", err, string(result))
t.Fatalf("failed to parse toolsrv.ToolResult: %v (raw: %s)", err, string(result))
}
if toolResult.IsError {
@ -334,14 +336,14 @@ func TestIntegration_MultilineContent(t *testing.T) {
func TestIntegration_ToolExecutionError(t *testing.T) {
// Skip if tools directory doesn't exist (CI environment)
if _, err := os.ReadDir(ToolsPath()); err != nil {
if _, err := os.ReadDir(toolsrv.ToolsPath()); err != nil {
t.Skipf("skipping: tools directory not available: %v", err)
}
socketPath, cleanup := startTestServer(t)
defer cleanup()
conn, err := Dial(socketPath, "test-secret-exec-err")
conn, err := toolsrv.Dial(socketPath, "test-secret-exec-err")
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
@ -361,14 +363,14 @@ func TestIntegration_ToolExecutionError(t *testing.T) {
}
// Parse the result
var toolResult ToolResult
var toolResult toolsrv.ToolResult
if err := json.Unmarshal(result, &toolResult); err != nil {
t.Fatalf("failed to parse ToolResult: %v (raw: %s)", err, string(result))
t.Fatalf("failed to parse toolsrv.ToolResult: %v (raw: %s)", err, string(result))
}
// Should have exit code in output
if len(toolResult.Content) == 0 {
t.Fatal("ToolResult.Content is empty")
t.Fatal("toolsrv.ToolResult.Content is empty")
}
if !strings.Contains(toolResult.Content[0].Text, "exit: 42") {
t.Errorf("output doesn't contain exit code: %q", toolResult.Content[0].Text)
@ -384,13 +386,13 @@ func TestIntegration_ProcessKeeperReconnect(t *testing.T) {
secret := "test-secret-keeper"
// Create a process manually (simulating what Spawn returns)
proc := &Process{
proc := &toolsrv.Process{
SocketPath: socketPath,
Secret: secret,
}
// Create keeper without respawn (we're testing reconnect, not respawn)
keeper := NewProcessKeeper(context.Background(), proc, nil)
keeper := toolsrv.NewProcessKeeper(context.Background(), proc, nil)
// First dial
conn1, err := keeper.Dial()
@ -419,7 +421,7 @@ func TestIntegration_ConcurrentConnections(t *testing.T) {
secret := "test-secret-concurrent"
// Establish the secret
conn0, err := Dial(socketPath, secret)
conn0, err := toolsrv.Dial(socketPath, secret)
if err != nil {
t.Fatalf("initial Dial failed: %v", err)
}
@ -428,11 +430,11 @@ func TestIntegration_ConcurrentConnections(t *testing.T) {
// Open multiple concurrent connections
const numConns = 5
conns := make([]*Conn, numConns)
conns := make([]*toolsrv.Conn, numConns)
errors := make([]error, numConns)
for i := 0; i < numConns; i++ {
conns[i], errors[i] = Dial(socketPath, secret)
conns[i], errors[i] = toolsrv.Dial(socketPath, secret)
}
// Check all succeeded with same token
@ -461,7 +463,7 @@ func TestIntegration_ProcessKeeperRespawn(t *testing.T) {
socketPath1, cleanup1 := startTestServer(t)
secret1 := "secret-for-first-server"
conn1, err := Dial(socketPath1, secret1)
conn1, err := toolsrv.Dial(socketPath1, secret1)
if err != nil {
cleanup1()
t.Fatalf("first Dial failed: %v", err)
@ -478,7 +480,7 @@ func TestIntegration_ProcessKeeperRespawn(t *testing.T) {
// New server should accept a new secret (any secret, since it's fresh)
secret2 := "secret-for-second-server"
conn2, err := Dial(socketPath2, secret2)
conn2, err := toolsrv.Dial(socketPath2, secret2)
if err != nil {
t.Fatalf("second Dial failed: %v", err)
}
@ -491,18 +493,18 @@ func TestIntegration_ProcessKeeperRespawn(t *testing.T) {
}
// Old secret should NOT work on new server
conn3, err := Dial(socketPath2, secret1)
conn3, err := toolsrv.Dial(socketPath2, secret1)
if err == nil {
conn3.Close()
t.Error("old secret should not work on new server")
}
// Verify ProcessKeeper handles this correctly
proc := &Process{
proc := &toolsrv.Process{
SocketPath: socketPath2,
Secret: secret2,
}
keeper := NewProcessKeeper(context.Background(), proc, nil)
keeper := toolsrv.NewProcessKeeper(context.Background(), proc, nil)
conn4, err := keeper.Dial()
if err != nil {

View File

@ -62,15 +62,15 @@ func main() {
}
// Create 9P server (secret is established via Tauth, not env var)
srv := toolsrv.NewServer9P()
srv := NewServer9P()
srv.SetYolo(*yolo)
if toolReg != nil && sid != "" {
srv.SetRegistry(toolReg, sid)
}
// Build the filesystem tree
ctx := toolsrv.ToolsrvCtx{Server: srv}
tree := fsedsl.BuildTree(toolsrv.ToolsrvSpec(), ctx)
ctx := ToolsrvCtx{Server: srv}
tree := fsedsl.BuildTree(ToolsrvSpec(), ctx)
if tree == nil {
fmt.Fprintln(os.Stderr, "error: failed to build filesystem tree")
os.Exit(1)

View File

@ -10,7 +10,6 @@ import (
"sync"
"ollie/fsedsl"
"ollie/toolsrv"
"9fans.net/go/plan9"
)
@ -42,12 +41,12 @@ type connState struct {
authFids map[uint32]*authFid
ctx context.Context
uname string
srv *toolsrv.Server9P
srv *Server9P
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 *toolsrv.Server9P, noAuth bool) {
func serve9P(ctx context.Context, conn net.Conn, tree *fsedsl.Tree, srv *Server9P, noAuth bool) {
defer conn.Close()
cs := &connState{

View File

@ -1,5 +1,5 @@
// server9p.go - State management for 9P toolsrv
package toolsrv
package main
import (
"crypto/rand"
@ -9,6 +9,7 @@ import (
"sync"
"ollie/fsedsl"
"ollie/toolsrv"
)
// Server9P holds the state for the 9P tool server.
@ -17,7 +18,7 @@ type Server9P struct {
secret string // set on first auth, verified on subsequent
token string // session token returned after auth
registry *Registry // tool registry
registry *toolsrv.Registry // tool registry
sessID string // session ID for registry scoping
yolo bool // skip sandbox
@ -33,7 +34,7 @@ func NewServer9P() *Server9P {
}
// SetRegistry configures the tool registry and session ID.
func (s *Server9P) SetRegistry(r *Registry, sessID string) {
func (s *Server9P) SetRegistry(r *toolsrv.Registry, sessID string) {
s.mu.Lock()
s.registry = r
s.sessID = sessID

View File

@ -1,4 +1,4 @@
package toolsrv
package main
import (
"testing"

View File

@ -1,5 +1,5 @@
// spec9p.go - fsedsl-based namespace specification for toolsrv
package toolsrv
package main
import (
"context"

16
toolsrv/types.go Normal file
View File

@ -0,0 +1,16 @@
// types.go - Shared types for toolsrv
package toolsrv
// ToolResult is the structured result from a tool execution.
type ToolResult struct {
Content []ToolResultContent `json:"content"`
IsError bool `json:"isError,omitempty"`
}
// ToolResultContent is a single content item in a tool result.
type ToolResultContent struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
MediaType string `json:"mediaType,omitempty"`
Data string `json:"data,omitempty"`
}