Skip to content

Commit cea81b9

Browse files
authored
Merge pull request #435 from thedadams/default-to-an-available-port
fix: use random available port by default
2 parents 3192ab8 + b26f1af commit cea81b9

File tree

4 files changed

+33
-10
lines changed

4 files changed

+33
-10
lines changed

pkg/cli/gptscript.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type GPTScript struct {
5959
ListModels bool `usage:"List the models available and exit" local:"true"`
6060
ListTools bool `usage:"List built-in tools and exit" local:"true"`
6161
Server bool `usage:"Start server" local:"true"`
62-
ListenAddress string `usage:"Server listen address" default:"127.0.0.1:9090" local:"true"`
62+
ListenAddress string `usage:"Server listen address" default:"127.0.0.1:0" local:"true"`
6363
Chdir string `usage:"Change current working directory" short:"C"`
6464
Daemon bool `usage:"Run tool as a daemon" local:"true" hidden:"true"`
6565
Ports string `usage:"The port range to use for ephemeral daemon ports (ex: 11000-12000)" hidden:"true"`

pkg/sdkserver/routes.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"net/http"
99
"os"
10-
"slices"
1110
"sort"
1211
"strings"
1312
"sync"
@@ -165,7 +164,14 @@ func (s *server) execHandler(w http.ResponseWriter, r *http.Request) {
165164

166165
reqObject.Env = append(os.Environ(), reqObject.Env...)
167166
// Don't overwrite the PromptURLEnvVar if it is already set in the environment.
168-
if !slices.ContainsFunc(reqObject.Env, func(s string) bool { return strings.HasPrefix(s, types.PromptTokenEnvVar+"=") }) {
167+
var promptTokenAlreadySet bool
168+
for _, env := range reqObject.Env {
169+
if strings.HasPrefix(env, types.PromptTokenEnvVar+"=") {
170+
promptTokenAlreadySet = true
171+
break
172+
}
173+
}
174+
if !promptTokenAlreadySet {
169175
// Append a prompt URL for this run.
170176
reqObject.Env = append(reqObject.Env, fmt.Sprintf("%s=http://%s/prompt/%s", types.PromptURLEnvVar, s.address, runID), fmt.Sprintf("%s=%s", types.PromptTokenEnvVar, s.token))
171177
}

pkg/sdkserver/server.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"log/slog"
9+
"net"
910
"net/http"
1011
"os"
1112
"os/signal"
@@ -17,6 +18,7 @@ import (
1718
"github.com/gptscript-ai/gptscript/pkg/gptscript"
1819
"github.com/gptscript-ai/gptscript/pkg/mvl"
1920
"github.com/gptscript-ai/gptscript/pkg/runner"
21+
"github.com/gptscript-ai/gptscript/pkg/types"
2022
"github.com/rs/cors"
2123
)
2224

@@ -46,14 +48,24 @@ func Start(ctx context.Context, opts Options) error {
4648
opts.Options.Runner.MonitorFactory = NewSessionFactory(events)
4749
go events.Start(ctx)
4850

51+
token := uuid.NewString()
52+
// Add the prompt token env var so that gptscript doesn't start its own server. We never want this client to start the
53+
// prompt server because it is only used for fmt, parse, etc.
54+
opts.Env = append(opts.Env, fmt.Sprintf("%s=%s", types.PromptTokenEnvVar, token))
55+
4956
g, err := gptscript.New(&opts.Options)
5057
if err != nil {
5158
return err
5259
}
5360

61+
listener, err := net.Listen("tcp", opts.ListenAddress)
62+
if err != nil {
63+
return fmt.Errorf("failed to listen on %s: %w", opts.ListenAddress, err)
64+
}
65+
5466
s := &server{
55-
address: opts.ListenAddress,
56-
token: uuid.NewString(),
67+
address: listener.Addr().String(),
68+
token: token,
5769
client: g,
5870
events: events,
5971
waitingToConfirm: make(map[string]chan runner.AuthorizerResponse),
@@ -64,7 +76,6 @@ func Start(ctx context.Context, opts Options) error {
6476
s.addRoutes(http.DefaultServeMux)
6577

6678
server := http.Server{
67-
Addr: opts.ListenAddress,
6879
Handler: apply(http.DefaultServeMux,
6980
contentType("application/json"),
7081
addRequestID,
@@ -74,7 +85,7 @@ func Start(ctx context.Context, opts Options) error {
7485
),
7586
}
7687

77-
slog.Info("Starting server", "addr", server.Addr)
88+
slog.Info("Starting server", "addr", s.address)
7889

7990
context.AfterFunc(sigCtx, func() {
8091
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
@@ -85,7 +96,7 @@ func Start(ctx context.Context, opts Options) error {
8596
slog.Info("Server stopped")
8697
})
8798

88-
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
99+
if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
89100
return fmt.Errorf("server error: %w", err)
90101
}
91102

pkg/server/server.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"io/fs"
10+
"net"
1011
"net/http"
1112
"os"
1213
"path/filepath"
@@ -212,10 +213,15 @@ func (s *Server) getContext(req *http.Request) (string, context.Context) {
212213
}
213214

214215
func (s *Server) Start(ctx context.Context) error {
216+
listener, err := net.Listen("tcp", s.listenAddress)
217+
if err != nil {
218+
return fmt.Errorf("could not listen on %s: %w", s.listenAddress, err)
219+
}
220+
215221
s.ctx = ctx
216222
s.melody.HandleConnect(s.Connect)
217223
go s.events.Start(ctx)
218-
log.Infof("Listening on http://%s", s.listenAddress)
224+
log.Infof("Listening on http://%s", listener.Addr().String())
219225
handler := cors.Default().Handler(s)
220226
server := &http.Server{Addr: s.listenAddress, Handler: handler}
221227
context.AfterFunc(ctx, func() {
@@ -224,7 +230,7 @@ func (s *Server) Start(ctx context.Context) error {
224230
_ = server.Shutdown(ctx)
225231
})
226232

227-
return server.ListenAndServe()
233+
return server.Serve(listener)
228234
}
229235

230236
func (s *Server) Connect(session *melody.Session) {

0 commit comments

Comments
 (0)