tools: one file per builtin tool
Each tool is a single file, single function. builtins.go is the registry.
This commit is contained in:
parent
18beb15698
commit
a21c256efa
|
|
@ -0,0 +1,18 @@
|
|||
// Package tools provides the built-in tool handlers for the toolsrv server.
|
||||
// Each file in this package is one tool. Builtins() is the single registry.
|
||||
package tools
|
||||
|
||||
import "ollie/toolsrv"
|
||||
|
||||
// Builtins returns the default set of built-in tool handlers.
|
||||
func Builtins() map[string]toolsrv.Handler {
|
||||
return map[string]toolsrv.Handler{
|
||||
"shell": Shell,
|
||||
"tool_list": ToolList,
|
||||
"tool_load": ToolLoad,
|
||||
"tool_active": ToolActive,
|
||||
"skill_list": SkillList,
|
||||
"skill_load": SkillLoad,
|
||||
"skill_active": SkillActive,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// Shell executes a single bash command in a sandboxed environment.
|
||||
func Shell(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Timeout int `json:"timeout"`
|
||||
Sandbox string `json:"sandbox"`
|
||||
Elevated bool `json:"elevated"`
|
||||
Detach bool `json:"detach"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("shell: bad args: %w", err)
|
||||
}
|
||||
if a.Cmd == "" {
|
||||
return "", fmt.Errorf("shell: cmd is required")
|
||||
}
|
||||
timeout := a.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = 30
|
||||
}
|
||||
if a.Elevated {
|
||||
return srv.ExecuteElevated(ctx, a.Cmd, srv.CWD(), timeout, a.Detach)
|
||||
}
|
||||
sandboxName := a.Sandbox
|
||||
if sandboxName == "" {
|
||||
sandboxName = "default"
|
||||
}
|
||||
return srv.ExecuteInSandbox(ctx, a.Cmd, "bash", timeout, sandboxName, false, "", a.Detach)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// SkillActive lists skills currently loaded in this session.
|
||||
func SkillActive(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.SkillsRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "(no skills loaded)", nil
|
||||
}
|
||||
loaded := reg.Loaded(sid)
|
||||
if len(loaded) == 0 {
|
||||
return "(no skills loaded)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range loaded {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// SkillList lists all available skills from the global registry.
|
||||
func SkillList(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.SkillsRegistry()
|
||||
if reg == nil {
|
||||
return "", fmt.Errorf("skill_list: no skills registry available")
|
||||
}
|
||||
summaries := reg.Summaries()
|
||||
if len(summaries) == 0 {
|
||||
return "(no skills found)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range summaries {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// SkillLoad loads a skill into the current session.
|
||||
func SkillLoad(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("skill_load: bad args: %w", err)
|
||||
}
|
||||
if a.Name == "" {
|
||||
return "", fmt.Errorf("skill_load: name is required")
|
||||
}
|
||||
reg := srv.SkillsRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "", fmt.Errorf("skill_load: no session registry")
|
||||
}
|
||||
if err := reg.Load(sid, a.Name); err != nil {
|
||||
return "", fmt.Errorf("skill_load: %w", err)
|
||||
}
|
||||
content, err := reg.SkillContent(a.Name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("skill_load: %w", err)
|
||||
}
|
||||
injectMsg := fmt.Sprintf("[system: loaded skill %s]\n\n%s", a.Name, content)
|
||||
srv.InjectContent(injectMsg)
|
||||
return fmt.Sprintf("loaded: %s (%d bytes)", a.Name, len(content)), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// ToolActive lists tools currently loaded (promoted) in this session.
|
||||
func ToolActive(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.ToolRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "(no tools loaded)", nil
|
||||
}
|
||||
loaded := reg.Loaded(sid)
|
||||
if len(loaded) == 0 {
|
||||
return "(no tools loaded)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, t := range loaded {
|
||||
out.WriteString(t.Name)
|
||||
if t.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(t.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// ToolList lists all available tools from the global registry.
|
||||
func ToolList(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.ToolRegistry()
|
||||
if reg == nil {
|
||||
return "", fmt.Errorf("tool_list: no registry available")
|
||||
}
|
||||
summaries := reg.Summaries()
|
||||
if len(summaries) == 0 {
|
||||
return "(no tools found)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range summaries {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// ToolLoad loads a tool into the current session.
|
||||
func ToolLoad(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("tool_load: bad args: %w", err)
|
||||
}
|
||||
if a.Name == "" {
|
||||
return "", fmt.Errorf("tool_load: name is required")
|
||||
}
|
||||
reg := srv.ToolRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "", fmt.Errorf("tool_load: no session registry")
|
||||
}
|
||||
if err := reg.Load(sid, a.Name); err != nil {
|
||||
return "", fmt.Errorf("tool_load: %w", err)
|
||||
}
|
||||
return fmt.Sprintf("loaded: %s", a.Name), nil
|
||||
}
|
||||
193
tools/tools.go
193
tools/tools.go
|
|
@ -1,193 +0,0 @@
|
|||
// Package tools provides the built-in tool handlers for the toolsrv server.
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// Builtins returns the default set of built-in tool handlers.
|
||||
func Builtins() map[string]toolsrv.Handler {
|
||||
return map[string]toolsrv.Handler{
|
||||
"shell": Shell,
|
||||
"tool_list": ToolList,
|
||||
"tool_load": ToolLoad,
|
||||
"tool_active": ToolActive,
|
||||
"skill_list": SkillList,
|
||||
"skill_load": SkillLoad,
|
||||
"skill_active": SkillActive,
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executes a single bash command in a sandboxed environment.
|
||||
func Shell(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Timeout int `json:"timeout"`
|
||||
Sandbox string `json:"sandbox"`
|
||||
Elevated bool `json:"elevated"`
|
||||
Detach bool `json:"detach"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("shell: bad args: %w", err)
|
||||
}
|
||||
if a.Cmd == "" {
|
||||
return "", fmt.Errorf("shell: cmd is required")
|
||||
}
|
||||
timeout := a.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = 30
|
||||
}
|
||||
if a.Elevated {
|
||||
return srv.ExecuteElevated(ctx, a.Cmd, srv.CWD(), timeout, a.Detach)
|
||||
}
|
||||
sandboxName := a.Sandbox
|
||||
if sandboxName == "" {
|
||||
sandboxName = "default"
|
||||
}
|
||||
return srv.ExecuteInSandbox(ctx, a.Cmd, "bash", timeout, sandboxName, false, "", a.Detach)
|
||||
}
|
||||
|
||||
// ToolList lists all available tools from the global registry.
|
||||
func ToolList(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.ToolRegistry()
|
||||
if reg == nil {
|
||||
return "", fmt.Errorf("tool_list: no registry available")
|
||||
}
|
||||
summaries := reg.Summaries()
|
||||
if len(summaries) == 0 {
|
||||
return "(no tools found)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range summaries {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
||||
// ToolLoad loads a tool into the current session.
|
||||
func ToolLoad(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("tool_load: bad args: %w", err)
|
||||
}
|
||||
if a.Name == "" {
|
||||
return "", fmt.Errorf("tool_load: name is required")
|
||||
}
|
||||
reg := srv.ToolRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "", fmt.Errorf("tool_load: no session registry")
|
||||
}
|
||||
if err := reg.Load(sid, a.Name); err != nil {
|
||||
return "", fmt.Errorf("tool_load: %w", err)
|
||||
}
|
||||
return fmt.Sprintf("loaded: %s", a.Name), nil
|
||||
}
|
||||
|
||||
// ToolActive lists tools currently loaded (promoted) in this session.
|
||||
func ToolActive(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.ToolRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "(no tools loaded)", nil
|
||||
}
|
||||
loaded := reg.Loaded(sid)
|
||||
if len(loaded) == 0 {
|
||||
return "(no tools loaded)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, t := range loaded {
|
||||
out.WriteString(t.Name)
|
||||
if t.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(t.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
||||
// SkillList lists all available skills from the global registry.
|
||||
func SkillList(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.SkillsRegistry()
|
||||
if reg == nil {
|
||||
return "", fmt.Errorf("skill_list: no skills registry available")
|
||||
}
|
||||
summaries := reg.Summaries()
|
||||
if len(summaries) == 0 {
|
||||
return "(no skills found)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range summaries {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
|
||||
// SkillLoad loads a skill into the current session.
|
||||
func SkillLoad(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
var a struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return "", fmt.Errorf("skill_load: bad args: %w", err)
|
||||
}
|
||||
if a.Name == "" {
|
||||
return "", fmt.Errorf("skill_load: name is required")
|
||||
}
|
||||
reg := srv.SkillsRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "", fmt.Errorf("skill_load: no session registry")
|
||||
}
|
||||
if err := reg.Load(sid, a.Name); err != nil {
|
||||
return "", fmt.Errorf("skill_load: %w", err)
|
||||
}
|
||||
content, err := reg.SkillContent(a.Name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("skill_load: %w", err)
|
||||
}
|
||||
injectMsg := fmt.Sprintf("[system: loaded skill %s]\n\n%s", a.Name, content)
|
||||
srv.InjectContent(injectMsg)
|
||||
return fmt.Sprintf("loaded: %s (%d bytes)", a.Name, len(content)), nil
|
||||
}
|
||||
|
||||
// SkillActive lists skills currently loaded in this session.
|
||||
func SkillActive(ctx context.Context, srv *toolsrv.Server, args json.RawMessage) (string, error) {
|
||||
reg := srv.SkillsRegistry()
|
||||
sid := srv.SessionID()
|
||||
if reg == nil || sid == "" {
|
||||
return "(no skills loaded)", nil
|
||||
}
|
||||
loaded := reg.Loaded(sid)
|
||||
if len(loaded) == 0 {
|
||||
return "(no skills loaded)", nil
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, s := range loaded {
|
||||
out.WriteString(s.Name)
|
||||
if s.Description != "" {
|
||||
out.WriteString(" — ")
|
||||
out.WriteString(s.Description)
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
return strings.TrimRight(out.String(), "\n"), nil
|
||||
}
|
||||
Reference in New Issue