@@ -3,6 +3,7 @@ package claude
33import (
44 "context"
55 "fmt"
6+ "time"
67
78 "github.com/onkernel/cli/internal/claude"
89 "github.com/onkernel/cli/pkg/util"
@@ -110,6 +111,12 @@ func runLaunch(cmd *cobra.Command, args []string) error {
110111
111112 pterm .Info .Printf ("Created browser: %s\n " , browser .SessionID )
112113
114+ // Wait for browser to be ready (eventual consistency)
115+ if err := waitForBrowserReady (ctx , client , browser .SessionID ); err != nil {
116+ _ = client .Browsers .DeleteByID (context .Background (), browser .SessionID )
117+ return fmt .Errorf ("browser not ready: %w" , err )
118+ }
119+
113120 // Load the Claude extension
114121 pterm .Info .Println ("Loading Claude extension..." )
115122 if err := claude .LoadIntoBrowser (ctx , claude.LoadIntoBrowserOptions {
@@ -190,3 +197,23 @@ func parseViewport(viewport string) (int64, int64, int64, error) {
190197
191198 return 0 , 0 , 0 , fmt .Errorf ("invalid format, expected WIDTHxHEIGHT[@RATE]" )
192199}
200+
201+ // waitForBrowserReady polls until the browser is accessible via GET.
202+ // This handles eventual consistency after browser creation.
203+ func waitForBrowserReady (ctx context.Context , client kernel.Client , browserID string ) error {
204+ const maxAttempts = 10
205+ const delay = 500 * time .Millisecond
206+
207+ for attempt := 1 ; attempt <= maxAttempts ; attempt ++ {
208+ _ , err := client .Browsers .Get (ctx , browserID )
209+ if err == nil {
210+ return nil
211+ }
212+
213+ if attempt < maxAttempts {
214+ time .Sleep (delay )
215+ }
216+ }
217+
218+ return fmt .Errorf ("browser %s not accessible after %d attempts" , browserID , maxAttempts )
219+ }
0 commit comments