@@ -32,17 +32,14 @@ import (
3232
3333 livekitcli "github.com/livekit/livekit-cli/v2"
3434 "github.com/livekit/livekit-cli/v2/pkg/agentfs"
35+ "github.com/livekit/livekit-cli/v2/pkg/bootstrap"
3536 "github.com/livekit/livekit-cli/v2/pkg/config"
3637 "github.com/livekit/livekit-cli/v2/pkg/util"
3738 lkproto "github.com/livekit/protocol/livekit"
3839 "github.com/livekit/protocol/logger"
3940 lksdk "github.com/livekit/server-sdk-go/v2"
4041)
4142
42- const (
43- cloudAgentsBetaSignupURL = "https://forms.gle/GkGNNTiMt2qyfnu78"
44- )
45-
4643var (
4744 idFlag = func (required bool ) * cli.StringFlag {
4845 return & cli.StringFlag {
9794 Aliases : []string {"a" },
9895 Usage : "Manage LiveKit Cloud Agents" ,
9996 Commands : []* cli.Command {
97+ {
98+ Name : "init" ,
99+ Usage : "Initialize a new LiveKit Cloud agent project" ,
100+ Before : createAgentClient ,
101+ Action : initAgent ,
102+ MutuallyExclusiveFlags : []cli.MutuallyExclusiveFlags {{
103+ Flags : [][]cli.Flag {{
104+ & cli.StringFlag {
105+ Name : "lang" ,
106+ Usage : "`LANGUAGE` of the project, one of \" node\" , \" python\" " ,
107+ Action : func (ctx context.Context , cmd * cli.Command , l string ) error {
108+ if l == "" {
109+ return nil
110+ }
111+ if ! slices .Contains ([]string {"node" , "python" }, l ) {
112+ return fmt .Errorf ("unsupported language: %s" , l )
113+ }
114+ return nil
115+ },
116+ Hidden : true ,
117+ },
118+ & cli.BoolFlag {
119+ Name : "deploy" ,
120+ Usage : "If set, automatically deploys the agent to LiveKit Cloud after initialization." ,
121+ Value : false ,
122+ },
123+ templateFlag ,
124+ templateURLFlag ,
125+ sandboxFlag ,
126+ }},
127+ }},
128+ Flags : []cli.Flag {},
129+ ArgsUsage : "[AGENT-NAME]" ,
130+ DisableSliceFlagSeparator : true ,
131+ },
100132 {
101133 Name : "create" ,
102134 Usage : "Create a new LiveKit Cloud Agent" ,
@@ -330,6 +362,81 @@ func createAgentClient(ctx context.Context, cmd *cli.Command) (context.Context,
330362 return ctx , nil
331363}
332364
365+ func initAgent (ctx context.Context , cmd * cli.Command ) error {
366+ if ! (cmd .IsSet ("lang" ) || cmd .IsSet ("template" ) || cmd .IsSet ("template-url" )) {
367+ var lang string
368+ // Prompt for language
369+ if err := huh .NewSelect [string ]().
370+ Title ("Select the language for your agent project" ).
371+ Options (
372+ huh .NewOption ("Python" , "python" ),
373+ huh .NewOption ("Node.js" , "node" ),
374+ ).
375+ Value (& lang ).
376+ WithTheme (util .Theme ).
377+ Run (); err != nil {
378+ return err
379+ }
380+
381+ switch lang {
382+ case "node" :
383+ return fmt .Errorf ("this language is not yet supported" )
384+ case "python" :
385+ templateURL = "https://github.com/livekit-examples/agent-starter-python-gateway"
386+ default :
387+ return fmt .Errorf ("unsupported language: %s" , lang )
388+ }
389+ }
390+
391+ logger .Debugw ("Initializing agent project" , "working-dir" , workingDir )
392+
393+ // Create sandbox if one is not provided
394+ if sandboxID == "" {
395+ fmt .Println ("Creating Sandbox app..." )
396+ token , err := requireToken (ctx , cmd )
397+ if err != nil {
398+ return err
399+ }
400+
401+ appName = cmd .Args ().First ()
402+ if appName == "" {
403+ appName = project .Name
404+ }
405+ // We set agent name in env for use in template tasks
406+ os .Setenv ("LIVEKIT_AGENT_NAME" , appName )
407+
408+ // NOTE: for now, we assume that agent-starter-react is the desired template.
409+ sandboxID , err = bootstrap .CreateSandbox (
410+ ctx ,
411+ appName ,
412+ "https://github.com/livekit-examples/agent-starter-react" ,
413+ token ,
414+ serverURL ,
415+ )
416+ if err != nil {
417+ return fmt .Errorf ("failed to create sandbox: %w" , err )
418+ }
419+ }
420+
421+ // Run template bootstrap
422+ shouldDeploy := cmd .Bool ("deploy" )
423+ if shouldDeploy {
424+ cmd .Set ("install" , "true" )
425+ }
426+ if err := setupTemplate (ctx , cmd ); err != nil {
427+ return err
428+ }
429+ // Deploy if requested
430+ if shouldDeploy {
431+ fmt .Println ("Deploying agent..." )
432+ if err := createAgent (ctx , cmd ); err != nil {
433+ return fmt .Errorf ("failed to deploy agent: %w" , err )
434+ }
435+ }
436+
437+ return nil
438+ }
439+
333440func createAgent (ctx context.Context , cmd * cli.Command ) error {
334441 subdomainMatches := subdomainPattern .FindStringSubmatch (project .URL )
335442 if len (subdomainMatches ) < 2 {
@@ -424,11 +531,9 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
424531 resp , err := agentsClient .CreateAgent (ctx , req )
425532 if err != nil {
426533 if twerr , ok := err .(twirp.Error ); ok {
427- if twerr .Code () == twirp .PermissionDenied {
428- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
429- }
534+ return fmt .Errorf ("unable to create agent: %s" , twerr .Msg ())
430535 }
431- return err
536+ return fmt . Errorf ( "unable to create agent: %w" , err )
432537 }
433538
434539 lkConfig .Agent .ID = resp .AgentId
@@ -512,11 +617,9 @@ func createAgentConfig(ctx context.Context, cmd *cli.Command) error {
512617 })
513618 if err != nil {
514619 if twerr , ok := err .(twirp.Error ); ok {
515- if twerr .Code () == twirp .PermissionDenied {
516- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
517- }
620+ return fmt .Errorf ("unable to list agents: %s" , twerr .Msg ())
518621 }
519- return err
622+ return fmt . Errorf ( "unable to list agents: %w" , err )
520623 }
521624 if len (response .Agents ) == 0 {
522625 return fmt .Errorf ("agent not found" )
@@ -581,11 +684,9 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error {
581684 resp , err := agentsClient .DeployAgent (ctx , req )
582685 if err != nil {
583686 if twerr , ok := err .(twirp.Error ); ok {
584- if twerr .Code () == twirp .PermissionDenied {
585- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
586- }
687+ return fmt .Errorf ("unable to deploy agent: %s" , twerr .Msg ())
587688 }
588- return err
689+ return fmt . Errorf ( "unable to deploy agent: %w" , err )
589690 }
590691
591692 if ! resp .Success {
@@ -619,11 +720,9 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error {
619720 })
620721 if err != nil {
621722 if twerr , ok := err .(twirp.Error ); ok {
622- if twerr .Code () == twirp .PermissionDenied {
623- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
624- }
723+ return fmt .Errorf ("unable to list agents: %s" , twerr .Msg ())
625724 }
626- return err
725+ return fmt . Errorf ( "unable to list agents: %w" , err )
627726 }
628727
629728 if len (res .Agents ) == 0 {
@@ -721,11 +820,9 @@ func updateAgent(ctx context.Context, cmd *cli.Command) error {
721820 })
722821 if err != nil {
723822 if twerr , ok := err .(twirp.Error ); ok {
724- if twerr .Code () == twirp .PermissionDenied {
725- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
726- }
823+ return fmt .Errorf ("unable to update agent: %s" , twerr .Msg ())
727824 }
728- return err
825+ return fmt . Errorf ( "unable to update agent: %w" , err )
729826 }
730827
731828 if resp .Success {
@@ -755,11 +852,9 @@ func rollbackAgent(ctx context.Context, cmd *cli.Command) error {
755852
756853 if err != nil {
757854 if twerr , ok := err .(twirp.Error ); ok {
758- if twerr .Code () == twirp .PermissionDenied {
759- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
760- }
855+ return fmt .Errorf ("unable to rollback agent: %s" , twerr .Msg ())
761856 }
762- return err
857+ return fmt . Errorf ( "unable to rollback agent: %w" , err )
763858 }
764859
765860 if ! resp .Success {
@@ -818,11 +913,9 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error {
818913
819914 if err != nil {
820915 if twerr , ok := err .(twirp.Error ); ok {
821- if twerr .Code () == twirp .PermissionDenied {
822- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
823- }
916+ return fmt .Errorf ("unable to delete agent: %s" , twerr .Msg ())
824917 }
825- return err
918+ return fmt . Errorf ( "unable to delete agent: %w" , err )
826919 }
827920
828921 if ! res .Success {
@@ -846,11 +939,9 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error {
846939 versions , err := agentsClient .ListAgentVersions (ctx , req )
847940 if err != nil {
848941 if twerr , ok := err .(twirp.Error ); ok {
849- if twerr .Code () == twirp .PermissionDenied {
850- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
851- }
942+ return fmt .Errorf ("unable to list agent versions: %s" , twerr .Msg ())
852943 }
853- return err
944+ return fmt . Errorf ( "unable to list agent versions: %w" , err )
854945 }
855946
856947 table := util .CreateTable ().
@@ -885,23 +976,19 @@ func listAgents(ctx context.Context, cmd *cli.Command) error {
885976 })
886977 if err != nil {
887978 if twerr , ok := err .(twirp.Error ); ok {
888- if twerr .Code () == twirp .PermissionDenied {
889- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
890- }
979+ return fmt .Errorf ("unable to list agents: %s" , twerr .Msg ())
891980 }
892- return err
981+ return fmt . Errorf ( "unable to list agents: %w" , err )
893982 }
894983 items = append (items , res .Agents ... )
895984 }
896985 } else {
897986 agents , err := agentsClient .ListAgents (ctx , & lkproto.ListAgentsRequest {})
898987 if err != nil {
899988 if twerr , ok := err .(twirp.Error ); ok {
900- if twerr .Code () == twirp .PermissionDenied {
901- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
902- }
989+ return fmt .Errorf ("unable to list agents: %s" , twerr .Msg ())
903990 }
904- return err
991+ return fmt . Errorf ( "unable to list agents: %w" , err )
905992 }
906993 items = agents .Agents
907994 }
@@ -950,11 +1037,9 @@ func listAgentSecrets(ctx context.Context, cmd *cli.Command) error {
9501037 secrets , err := agentsClient .ListAgentSecrets (ctx , req )
9511038 if err != nil {
9521039 if twerr , ok := err .(twirp.Error ); ok {
953- if twerr .Code () == twirp .PermissionDenied {
954- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
955- }
1040+ return fmt .Errorf ("unable to list agent secrets: %s" , twerr .Msg ())
9561041 }
957- return err
1042+ return fmt . Errorf ( "unable to list agent secrets: %w" , err )
9581043 }
9591044
9601045 table := util .CreateTable ().
@@ -1011,11 +1096,9 @@ func updateAgentSecrets(ctx context.Context, cmd *cli.Command) error {
10111096 resp , err := agentsClient .UpdateAgentSecrets (ctx , req )
10121097 if err != nil {
10131098 if twerr , ok := err .(twirp.Error ); ok {
1014- if twerr .Code () == twirp .PermissionDenied {
1015- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
1016- }
1099+ return fmt .Errorf ("unable to update agent secrets: %s" , twerr .Msg ())
10171100 }
1018- return err
1101+ return fmt . Errorf ( "unable to update agent secrets: %w" , err )
10191102 }
10201103
10211104 if resp .Success {
@@ -1067,11 +1150,9 @@ func selectAgent(ctx context.Context, _ *cli.Command, excludeEmptyVersion bool)
10671150 })
10681151 if err != nil {
10691152 if twerr , ok := err .(twirp.Error ); ok {
1070- if twerr .Code () == twirp .PermissionDenied {
1071- return "" , fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
1072- }
1153+ return "" , fmt .Errorf ("unable to list agents: %s" , twerr .Msg ())
10731154 }
1074- return "" , err
1155+ return "" , fmt . Errorf ( "unable to list agents: %w" , err )
10751156 }
10761157
10771158 if len (agents .Agents ) == 0 {
@@ -1213,11 +1294,9 @@ func getClientSettings(ctx context.Context, silent bool) (map[string]string, err
12131294
12141295 if err != nil {
12151296 if twerr , ok := err .(twirp.Error ); ok {
1216- if twerr .Code () == twirp .PermissionDenied {
1217- return nil , fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
1218- }
1297+ return nil , fmt .Errorf ("unable to get client settings: %s" , twerr .Msg ())
12191298 }
1220- return nil , err
1299+ return nil , fmt . Errorf ( "unable to get client settings: %w" , err )
12211300 }
12221301
12231302 if clientSettingsResponse == nil {
0 commit comments