Skip to content

Commit e0e5afa

Browse files
Merge pull request #218 from ibuildthecloud/ls
feat: add sys.ls
2 parents 102af6a + 4ca1bca commit e0e5afa

File tree

5 files changed

+115
-8
lines changed

5 files changed

+115
-8
lines changed

pkg/builtin/builtin.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ import (
2525
)
2626

2727
var tools = map[string]types.Tool{
28+
"sys.ls": {
29+
Parameters: types.Parameters{
30+
Description: "Lists the contents of a directory",
31+
Arguments: types.ObjectSchema(
32+
"dir", "The directory to list"),
33+
},
34+
BuiltinFunc: SysLs,
35+
},
2836
"sys.read": {
2937
Parameters: types.Parameters{
3038
Description: "Reads the contents of a file",
@@ -268,6 +276,37 @@ func SysExec(ctx context.Context, env []string, input string) (string, error) {
268276
return string(out), err
269277
}
270278

279+
func SysLs(_ context.Context, _ []string, input string) (string, error) {
280+
var params struct {
281+
Dir string `json:"dir,omitempty"`
282+
}
283+
if err := json.Unmarshal([]byte(input), &params); err != nil {
284+
return "", err
285+
}
286+
287+
if params.Dir == "" {
288+
params.Dir = "."
289+
}
290+
291+
entries, err := os.ReadDir(params.Dir)
292+
if errors.Is(err, fs.ErrNotExist) {
293+
return fmt.Sprintf("directory does not exist: %s", params.Dir), nil
294+
} else if err != nil {
295+
return "", err
296+
}
297+
298+
var result []string
299+
for _, entry := range entries {
300+
if entry.IsDir() {
301+
result = append(result, entry.Name()+"/")
302+
} else {
303+
result = append(result, entry.Name())
304+
}
305+
}
306+
307+
return strings.Join(result, "\n"), nil
308+
}
309+
271310
func SysRead(ctx context.Context, env []string, input string) (string, error) {
272311
var params struct {
273312
Filename string `json:"filename,omitempty"`
@@ -282,10 +321,15 @@ func SysRead(ctx context.Context, env []string, input string) (string, error) {
282321

283322
log.Debugf("Reading file %s", params.Filename)
284323
data, err := os.ReadFile(params.Filename)
285-
if err != nil {
324+
if errors.Is(err, fs.ErrNotExist) {
325+
return fmt.Sprintf("The file %s does not exist", params.Filename), nil
326+
} else if err != nil {
286327
return "", err
287328
}
288329

330+
if len(data) == 0 {
331+
return fmt.Sprintf("The file %s has no contents", params.Filename), nil
332+
}
289333
return string(data), nil
290334
}
291335

pkg/cli/eval.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ func (e *Eval) Run(cmd *cobra.Command, args []string) error {
5151
return err
5252
}
5353

54-
opts := e.gptscript.NewGPTScriptOpts()
54+
opts, err := e.gptscript.NewGPTScriptOpts()
55+
if err != nil {
56+
return err
57+
}
58+
5559
runner, err := gptscript.New(&opts)
5660
if err != nil {
5761
return err

pkg/cli/gptscript.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"os"
88
"sort"
9+
"strconv"
910
"strings"
1011

1112
"github.com/acorn-io/cmd"
@@ -50,6 +51,8 @@ type GPTScript struct {
5051
Server bool `usage:"Start server" local:"true"`
5152
ListenAddress string `usage:"Server listen address" default:"127.0.0.1:9090" local:"true"`
5253
Chdir string `usage:"Change current working directory" short:"C"`
54+
Daemon bool `usage:"Run tool as a daemon" local:"true" hidden:"true"`
55+
Ports string `usage:"The port range to use for ephemeral daemon ports (ex: 11000-12000)" hidden:"true"`
5356
}
5457

5558
func New() *cobra.Command {
@@ -67,14 +70,33 @@ func (r *GPTScript) NewRunContext(cmd *cobra.Command) context.Context {
6770
return ctx
6871
}
6972

70-
func (r *GPTScript) NewGPTScriptOpts() gptscript.Options {
71-
return gptscript.Options{
73+
func (r *GPTScript) NewGPTScriptOpts() (gptscript.Options, error) {
74+
opts := gptscript.Options{
7275
Cache: cache.Options(r.CacheOptions),
7376
OpenAI: openai.Options(r.OpenAIOptions),
7477
Monitor: monitor.Options(r.DisplayOptions),
7578
Quiet: r.Quiet,
7679
Env: os.Environ(),
7780
}
81+
82+
if r.Ports != "" {
83+
start, end, _ := strings.Cut(r.Ports, "-")
84+
startNum, err := strconv.ParseInt(strings.TrimSpace(start), 10, 64)
85+
if err != nil {
86+
return gptscript.Options{}, fmt.Errorf("invalid port range: %s", r.Ports)
87+
}
88+
var endNum int64
89+
if end != "" {
90+
endNum, err = strconv.ParseInt(strings.TrimSpace(end), 10, 64)
91+
if err != nil {
92+
return gptscript.Options{}, fmt.Errorf("invalid port range: %s", r.Ports)
93+
}
94+
}
95+
opts.Runner.StartPort = startNum
96+
opts.Runner.EndPort = endNum
97+
}
98+
99+
return opts, nil
78100
}
79101

80102
func (r *GPTScript) Customize(cmd *cobra.Command) {
@@ -205,8 +227,12 @@ func (r *GPTScript) PrintOutput(toolInput, toolOutput string) (err error) {
205227
return
206228
}
207229

208-
func (r *GPTScript) Run(cmd *cobra.Command, args []string) error {
209-
gptOpt := r.NewGPTScriptOpts()
230+
func (r *GPTScript) Run(cmd *cobra.Command, args []string) (retErr error) {
231+
gptOpt, err := r.NewGPTScriptOpts()
232+
if err != nil {
233+
return err
234+
}
235+
210236
ctx := cmd.Context()
211237

212238
if r.Server {
@@ -236,6 +262,15 @@ func (r *GPTScript) Run(cmd *cobra.Command, args []string) error {
236262
return err
237263
}
238264

265+
if r.Daemon {
266+
prg = prg.SetBlocking()
267+
defer func() {
268+
if retErr == nil {
269+
<-ctx.Done()
270+
}
271+
}()
272+
}
273+
239274
if r.ListTools {
240275
return r.listTools(ctx, gptScript, prg)
241276
}

pkg/engine/daemon.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ type Ports struct {
2525
daemonWG sync.WaitGroup
2626
}
2727

28+
func (p *Ports) SetPorts(start, end int64) {
29+
p.startPort = start
30+
p.endPort = end
31+
}
32+
2833
func (p *Ports) CloseDaemons() {
2934
p.daemonLock.Lock()
3035
if p.daemonCtx == nil {

pkg/runner/runner.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,26 @@ type Monitor interface {
2626
type Options struct {
2727
MonitorFactory MonitorFactory `usage:"-"`
2828
RuntimeManager engine.RuntimeManager `usage:"-"`
29+
StartPort int64 `usage:"-"`
30+
EndPort int64 `usage:"-"`
2931
}
3032

3133
func complete(opts ...Options) (result Options) {
3234
for _, opt := range opts {
3335
result.MonitorFactory = types.FirstSet(opt.MonitorFactory, result.MonitorFactory)
3436
result.RuntimeManager = types.FirstSet(opt.RuntimeManager, result.RuntimeManager)
37+
result.StartPort = types.FirstSet(opt.StartPort, result.StartPort)
38+
result.EndPort = types.FirstSet(opt.EndPort, result.EndPort)
3539
}
3640
if result.MonitorFactory == nil {
3741
result.MonitorFactory = noopFactory{}
3842
}
43+
if result.EndPort == 0 {
44+
result.EndPort = result.StartPort
45+
}
46+
if result.StartPort == 0 {
47+
result.StartPort = result.EndPort
48+
}
3949
return
4050
}
4151

@@ -49,11 +59,20 @@ type Runner struct {
4959
func New(client engine.Model, opts ...Options) (*Runner, error) {
5060
opt := complete(opts...)
5161

52-
return &Runner{
62+
runner := &Runner{
5363
c: client,
5464
factory: opt.MonitorFactory,
5565
runtimeManager: opt.RuntimeManager,
56-
}, nil
66+
}
67+
68+
if opt.StartPort != 0 {
69+
if opt.EndPort < opt.StartPort {
70+
return nil, fmt.Errorf("invalid port range: %d-%d", opt.StartPort, opt.EndPort)
71+
}
72+
runner.ports.SetPorts(opt.StartPort, opt.EndPort)
73+
}
74+
75+
return runner, nil
5776
}
5877

5978
func (r *Runner) Close() {

0 commit comments

Comments
 (0)