Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ Examples:
websocketId = cstr.NewHash(orgId, userId)
}

port, _ := cmd.Flags().GetInt("port")
if port == 0 {
port, err = dev.FindAvailablePort(theproject)
if err != nil {
log.Fatal("failed to find available port: %s", err)
}
}

websocketConn, err := dev.NewWebsocket(dev.WebsocketArgs{
Ctx: ctx,
Logger: log,
Expand All @@ -88,11 +96,6 @@ Examples:
}
defer websocketConn.Close()

port, err := dev.FindAvailablePort(theproject)
if err != nil {
log.Fatal("failed to find available port: %s", err)
}

projectServerCmd, err := dev.CreateRunProjectCmd(ctx, log, theproject, websocketConn, dir, orgId, port)
if err != nil {
errsystem.New(errsystem.ErrInvalidConfiguration, err, errsystem.WithContextMessage("Failed to run project")).ShowErrorAndExit()
Expand Down Expand Up @@ -222,6 +225,7 @@ func init() {
devCmd.Flags().StringP("dir", "d", ".", "The directory to run the development server in")
devCmd.Flags().String("websocket-id", "", "The websocket room id to use for the development agent")
devCmd.Flags().String("org-id", "", "The organization to run the project")
devCmd.Flags().Int("port", 0, "The port to run the development server on (uses project default if not provided)")
devCmd.Flags().MarkHidden("websocket-id")
devCmd.Flags().MarkHidden("org-id")
}
2 changes: 1 addition & 1 deletion internal/bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func bundleJavascript(ctx BundleContext, dir string, outdir string, theproject *
defines["process.env.AGENTUITY_ENVIRONMENT"] = fmt.Sprintf("'%s'", "development")
}
}
defines["process.env.AGENTUITY_CLOUD_AGENTS_JSON"] = fmt.Sprintf("'%s'", cstr.JSONStringify(agents))
defines["process.env.AGENTUITY_CLOUD_AGENTS_JSON"] = cstr.JSONStringify(cstr.JSONStringify(agents))

ctx.Logger.Debug("starting build")
started := time.Now()
Expand Down
30 changes: 25 additions & 5 deletions internal/dev/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,47 @@ func (c *Websocket) Done() <-chan struct{} {
return c.done
}

const maxHealthCheckDuration = time.Second * 30

func isConnectionErrorRetryable(err error) bool {
if strings.Contains(err.Error(), "connection refused") {
return true
}
if strings.Contains(err.Error(), "connection reset by peer") {
return true
}
if strings.Contains(err.Error(), "No connection could be made because the target machine actively refused it") { // windows
return true
}
return false
}

func (c *Websocket) getAgentProtocol(ctx context.Context, port int) (bool, error) {
url := fmt.Sprintf("http://127.0.0.1:%d/_health", port)
for i := 0; i < 5; i++ {
started := time.Now()
var i int
for time.Since(started) < maxHealthCheckDuration {
i++
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return false, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
c.logger.Debug("healthcheck attempt #%d failed: %s", i, err)
if isConnectionErrorRetryable(err) {
time.Sleep(time.Millisecond * time.Duration(100*i+1))
continue
}
return false, err
}
c.logger.Debug("healthcheck attempt #%d succeeded with status code %d", i, resp.StatusCode)
defer resp.Body.Close()
if resp.StatusCode == 200 {
return resp.Header.Get("x-agentuity-binary") == "true", nil
}
}
return false, fmt.Errorf("failed to inspect agents after 5 attempts")
return false, fmt.Errorf("failed to inspect agents after %s", maxHealthCheckDuration)
}

func (c *Websocket) getAgentWelcome(ctx context.Context, port int) (map[string]Welcome, error) {
Expand All @@ -112,7 +132,7 @@ func (c *Websocket) getAgentWelcome(ctx context.Context, port int) (map[string]W
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
if isConnectionErrorRetryable(err) {
time.Sleep(time.Millisecond * time.Duration(100*i+1))
continue
}
Expand All @@ -135,7 +155,7 @@ func (c *Websocket) StartReadingMessages(ctx context.Context, logger logger.Logg
var err error
c.binaryProtocol, err = c.getAgentProtocol(ctx, port)
if err != nil {
logger.Error("failed to healthcheck agents: %s", err)
logger.Fatal("Your project failed to start. This typically happens when the project cannot compile or this is an underlying issue with starting the project.")
return
}

Expand Down
12 changes: 10 additions & 2 deletions internal/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/url"
"os"
"path/filepath"
"strings"

"github.com/Masterminds/semver"
"github.com/agentuity/cli/internal/errsystem"
Expand Down Expand Up @@ -486,6 +487,13 @@ func LoadProject(logger logger.Logger, dir string, apiUrl string, appUrl string,
}
}

func isVersionCheckRequired(ver string) bool {
if ver != "" && ver != "dev" && !strings.Contains(ver, "-next") {
return true
}
return false
}

func EnsureProject(ctx context.Context, cmd *cobra.Command) ProjectContext {
logger := env.NewLogger(cmd)
dir := ResolveProjectDir(logger, cmd, true)
Expand All @@ -498,7 +506,7 @@ func EnsureProject(ctx context.Context, cmd *cobra.Command) ProjectContext {
token, _ = util.EnsureLoggedIn(ctx, logger, cmd)
}
p := LoadProject(logger, dir, apiUrl, appUrl, transportUrl, token)
if !p.NewProject && Version != "" && Version != "dev" && p.Project.Version != "" {
if !p.NewProject && isVersionCheckRequired(Version) && p.Project.Version != "" {
v := semver.MustParse(Version)
c, err := semver.NewConstraint(p.Project.Version)
if err != nil {
Expand Down Expand Up @@ -538,7 +546,7 @@ func TryProject(ctx context.Context, cmd *cobra.Command) ProjectContext {
}
}
p := LoadProject(logger, dir, apiUrl, appUrl, transportUrl, token)
if !p.NewProject && Version != "" && Version != "dev" && p.Project.Version != "" {
if !p.NewProject && isVersionCheckRequired(Version) && p.Project.Version != "" {
v := semver.MustParse(Version)
c, err := semver.NewConstraint(p.Project.Version)
if err != nil {
Expand Down
Loading