9999 silentFlag ,
100100 regionFlag ,
101101 },
102- ArgsUsage : "[working-dir]" ,
102+ // NOTE: since secrets may contain commas, or indeed any special character we might want to treat as a flag separator,
103+ // we disable it entirely here and require multiple --secrets flags to be used.
104+ DisableSliceFlagSeparator : true ,
105+ ArgsUsage : "[working-dir]" ,
103106 },
104107 {
105108 Name : "config" ,
@@ -120,7 +123,10 @@ var (
120123 secretsFlag ,
121124 secretsFileFlag ,
122125 },
123- ArgsUsage : "[working-dir]" ,
126+ // NOTE: since secrets may contain commas, or indeed any special character we might want to treat as a flag separator,
127+ // we disable it entirely here and require multiple --secrets flags to be used.
128+ DisableSliceFlagSeparator : true ,
129+ ArgsUsage : "[working-dir]" ,
124130 },
125131 {
126132 Name : "status" ,
@@ -141,7 +147,10 @@ var (
141147 secretsFlag ,
142148 secretsFileFlag ,
143149 },
144- ArgsUsage : "[working-dir]" ,
150+ // NOTE: since secrets may contain commas, or indeed any special character we might want to treat as a flag separator,
151+ // we disable it entirely here and require multiple --secrets flags to be used.
152+ DisableSliceFlagSeparator : true ,
153+ ArgsUsage : "[working-dir]" ,
145154 },
146155 {
147156 Name : "restart" ,
@@ -237,7 +246,10 @@ var (
237246 Value : false ,
238247 },
239248 },
240- ArgsUsage : "[working-dir]" ,
249+ // NOTE: since secrets may contain commas, or indeed any special character we might want to treat as a flag separator,
250+ // we disable it entirely here and require multiple --secrets flags to be used.
251+ DisableSliceFlagSeparator : true ,
252+ ArgsUsage : "[working-dir]" ,
241253 },
242254 },
243255 },
@@ -293,7 +305,7 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
293305 }
294306
295307 // We have a configured project, but don't need to double-confirm if it was
296- // set via a command line flag, because intent it clear.
308+ // set via a command line flag, because intent is clear.
297309 if ! cmd .IsSet ("project" ) {
298310 useProject := true
299311 if err := huh .NewForm (huh .NewGroup (huh .NewConfirm ().
@@ -352,10 +364,21 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
352364 return err
353365 }
354366
355- if err := requireDockerfile (ctx , cmd , workingDir ); err != nil {
367+ settingsMap , err := getClientSettings (ctx , cmd .Bool ("silent" ))
368+ if err != nil {
369+ return err
370+ }
371+
372+ if err := requireDockerfile (ctx , cmd , workingDir , settingsMap ); err != nil {
356373 return err
357374 }
358375
376+ // TODO (steveyoon): disable check SDK version until we add support for uv and lockfile generation
377+ // https://github.com/livekit/livekit-cli/pull/618/files
378+ // if err := agentfs.CheckSDKVersion(workingDir, settingsMap); err != nil {
379+ // return err
380+ // }
381+
359382 req := & lkproto.CreateAgentRequest {
360383 Secrets : secrets ,
361384 Regions : lkConfig .Agent .Regions ,
@@ -1003,13 +1026,18 @@ func selectAgent(ctx context.Context, _ *cli.Command) (string, error) {
10031026func requireSecrets (_ context.Context , cmd * cli.Command , required , lazy bool ) ([]* lkproto.AgentSecret , error ) {
10041027 silent := cmd .Bool ("silent" )
10051028 secrets := make (map [string ]* lkproto.AgentSecret )
1006- for _ , secret := range cmd .StringSlice ("secrets" ) {
1007- secret := strings .Split (secret , "=" )
1008- agentSecret := & lkproto.AgentSecret {
1009- Name : secret [0 ],
1010- Value : []byte (secret [1 ]),
1029+
1030+ if values , err := parseKeyValuePairs (cmd , "secrets" ); err != nil {
1031+ return nil , fmt .Errorf ("failed to parse secrets: %w" , err )
1032+ } else {
1033+ for key , val := range values {
1034+ agentSecret := & lkproto.AgentSecret {
1035+ Name : key ,
1036+ Value : []byte (val ),
1037+ }
1038+ secrets [key ] = agentSecret
10111039 }
1012- secrets [ secret [ 0 ]] = agentSecret
1040+
10131041 }
10141042
10151043 shouldReadFromDisk := cmd .IsSet ("secrets-file" ) || ! lazy || (required && len (secrets ) == 0 )
@@ -1056,42 +1084,13 @@ func requireSecrets(_ context.Context, cmd *cli.Command, required, lazy bool) ([
10561084 return secretsSlice , nil
10571085}
10581086
1059- func requireDockerfile (ctx context.Context , cmd * cli.Command , workingDir string ) error {
1087+ func requireDockerfile (ctx context.Context , cmd * cli.Command , workingDir string , settingsMap map [ string ] string ) error {
10601088 dockerfileExists , err := agentfs .HasDockerfile (workingDir )
10611089 if err != nil {
10621090 return err
10631091 }
10641092
10651093 if ! dockerfileExists {
1066- var clientSettingsResponse * lkproto.ClientSettingsResponse
1067-
1068- if ! cmd .Bool ("silent" ) {
1069- if err := util .Await (
1070- "Loading client settings..." ,
1071- func () {
1072- clientSettingsResponse , err = agentsClient .GetClientSettings (ctx , & lkproto.ClientSettingsRequest {})
1073- },
1074- ); err != nil {
1075- return err
1076- }
1077- } else {
1078- clientSettingsResponse , err = agentsClient .GetClientSettings (ctx , & lkproto.ClientSettingsRequest {})
1079- }
1080-
1081- if err != nil {
1082- if twerr , ok := err .(twirp.Error ); ok {
1083- if twerr .Code () == twirp .PermissionDenied {
1084- return fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
1085- }
1086- }
1087- return err
1088- }
1089-
1090- settingsMap := make (map [string ]string )
1091- for _ , setting := range clientSettingsResponse .Params {
1092- settingsMap [setting .Name ] = setting .Value
1093- }
1094-
10951094 if ! cmd .Bool ("silent" ) {
10961095 var innerErr error
10971096 if err := util .Await (
@@ -1120,6 +1119,38 @@ func requireDockerfile(ctx context.Context, cmd *cli.Command, workingDir string)
11201119 return nil
11211120}
11221121
1122+ func getClientSettings (ctx context.Context , silent bool ) (map [string ]string , error ) {
1123+ var clientSettingsResponse * lkproto.ClientSettingsResponse
1124+ var err error
1125+
1126+ if ! silent {
1127+ err = util .Await (
1128+ "Loading client settings..." ,
1129+ func () {
1130+ clientSettingsResponse , err = agentsClient .GetClientSettings (ctx , & lkproto.ClientSettingsRequest {})
1131+ },
1132+ )
1133+ } else {
1134+ clientSettingsResponse , err = agentsClient .GetClientSettings (ctx , & lkproto.ClientSettingsRequest {})
1135+ }
1136+
1137+ if err != nil {
1138+ if twerr , ok := err .(twirp.Error ); ok {
1139+ if twerr .Code () == twirp .PermissionDenied {
1140+ return nil , fmt .Errorf ("agent hosting is disabled for this project -- join the beta program here [%s]" , cloudAgentsBetaSignupURL )
1141+ }
1142+ }
1143+ return nil , err
1144+ }
1145+
1146+ settingsMap := make (map [string ]string )
1147+ for _ , setting := range clientSettingsResponse .Params {
1148+ settingsMap [setting .Name ] = setting .Value
1149+ }
1150+
1151+ return settingsMap , nil
1152+ }
1153+
11231154func requireConfig (workingDir , tomlFilename string ) (bool , error ) {
11241155 if lkConfig != nil {
11251156 return true , nil
0 commit comments