77 "encoding/xml"
88 "errors"
99 "fmt"
10+ "io"
1011 "net/http"
1112 "strconv"
1213 "strings"
@@ -17,28 +18,31 @@ import (
1718)
1819
1920const (
20- antigravityFallbackVersion = "2.1.0"
21+ antigravityFallbackVersion = "1.0.8"
22+ antigravityCLIPlatform = "darwin/arm64"
2123 antigravityVersionCacheTTL = 6 * time .Hour
2224 antigravityFetchTimeout = 10 * time .Second
2325 AntigravityNodeAPIClientUA = "google-api-nodejs-client/10.3.0"
2426 AntigravityGoogAPIClientUA = "gl-node/22.21.1"
2527)
2628
2729var (
28- antigravityHubGCSListURL = "https://storage.googleapis.com/antigravity-public/?prefix=antigravity-hub/&delimiter=/"
29- antigravityReleasesURL = "https://antigravity-auto-updater-974169037036.us-central1.run.app/releases"
30+ antigravityCLIUpdaterBaseURL = "https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests"
31+ antigravityCLILatestURL = "https://storage.googleapis.com/antigravity-public/antigravity-cli/latest"
32+ antigravityCLIGCSListURL = "https://storage.googleapis.com/antigravity-public/?prefix=antigravity-cli/&delimiter=/"
3033)
3134
32- type antigravityRelease struct {
33- Version string `json:"version"`
34- ExecutionID string `json:"execution_id"`
35+ type antigravityCLIUpdaterManifest struct {
36+ Version string `json:"version"`
37+ URL string `json:"url"`
38+ SHA512 string `json:"sha512"`
3539}
3640
37- type antigravityHubGCSList struct {
38- CommonPrefixes []antigravityHubGCSPrefix `xml:"CommonPrefixes"`
41+ type antigravityGCSList struct {
42+ CommonPrefixes []antigravityGCSPrefix `xml:"CommonPrefixes"`
3943}
4044
41- type antigravityHubGCSPrefix struct {
45+ type antigravityGCSPrefix struct {
4246 Prefix string `xml:"Prefix"`
4347}
4448
@@ -123,10 +127,13 @@ func AntigravityLatestVersion() string {
123127 return antigravityFallbackVersion
124128}
125129
126- // AntigravityUserAgent returns the User-Agent string for antigravity requests
127- // using the latest version fetched from the releases API.
130+ // AntigravityUserAgent returns the User-Agent string used by the agy CLI family.
128131func AntigravityUserAgent () string {
129- return fmt .Sprintf ("antigravity/%s darwin/arm64" , AntigravityLatestVersion ())
132+ return fmt .Sprintf ("antigravity/cli/%s %s" , AntigravityLatestVersion (), antigravityCLIPlatform )
133+ }
134+
135+ func isAntigravityFamilyUserAgent (lower string ) bool {
136+ return strings .HasPrefix (lower , "antigravity/cli/" ) || strings .HasPrefix (lower , "antigravity/" )
130137}
131138
132139func antigravityBaseUserAgent (userAgent string ) string {
@@ -135,7 +142,7 @@ func antigravityBaseUserAgent(userAgent string) string {
135142 return AntigravityUserAgent ()
136143 }
137144 lower := strings .ToLower (userAgent )
138- if strings . HasPrefix (lower , "antigravity/" ) {
145+ if isAntigravityFamilyUserAgent (lower ) {
139146 if idx := strings .Index (lower , " google-api-nodejs-client/" ); idx >= 0 {
140147 trimmed := strings .TrimSpace (userAgent [:idx ])
141148 if trimmed != "" {
@@ -160,7 +167,7 @@ func AntigravityLoadCodeAssistUserAgent(userAgent string) string {
160167 return AntigravityUserAgent () + " " + AntigravityNodeAPIClientUA
161168 }
162169 lower := strings .ToLower (userAgent )
163- if ! strings . HasPrefix (lower , "antigravity/" ) {
170+ if ! isAntigravityFamilyUserAgent (lower ) {
164171 return userAgent
165172 }
166173 if strings .Contains (lower , "google-api-nodejs-client/" ) {
@@ -174,10 +181,24 @@ func AntigravityLoadCodeAssistUserAgent(userAgent string) string {
174181func AntigravityVersionFromUserAgent (userAgent string ) string {
175182 base := antigravityBaseUserAgent (userAgent )
176183 lower := strings .ToLower (base )
177- if ! strings .HasPrefix (lower , "antigravity/" ) {
184+ for _ , familyPrefix := range []string {"antigravity/cli/" , "antigravity/hub/" } {
185+ if strings .HasPrefix (lower , familyPrefix ) {
186+ rest := base [len (familyPrefix ):]
187+ if idx := strings .IndexAny (rest , " \t " ); idx >= 0 {
188+ rest = rest [:idx ]
189+ }
190+ rest = strings .TrimSpace (rest )
191+ if rest == "" {
192+ return AntigravityLatestVersion ()
193+ }
194+ return rest
195+ }
196+ }
197+ const legacyPrefix = "antigravity/"
198+ if ! strings .HasPrefix (lower , legacyPrefix ) {
178199 return AntigravityLatestVersion ()
179200 }
180- rest := base [len ("antigravity/" ):]
201+ rest := base [len (legacyPrefix ):]
181202 if idx := strings .IndexAny (rest , " \t " ); idx >= 0 {
182203 rest = rest [:idx ]
183204 }
@@ -188,104 +209,154 @@ func AntigravityVersionFromUserAgent(userAgent string) string {
188209 return rest
189210}
190211
212+ func antigravityCLIUpdaterManifestName () string {
213+ return strings .ReplaceAll (antigravityCLIPlatform , "/" , "_" )
214+ }
215+
191216func fetchAntigravityLatestVersion (ctx context.Context ) (string , error ) {
192217 if ctx == nil {
193218 ctx = context .Background ()
194219 }
195220
196221 client := & http.Client {Timeout : antigravityFetchTimeout }
197222
198- version , errHub := fetchAntigravityHubGCSLatestVersion (ctx , client )
199- if errHub == nil {
223+ version , errManifest := fetchAntigravityCLIUpdaterManifestVersion (ctx , client )
224+ if errManifest == nil {
200225 return version , nil
201226 }
202227
203- log .WithError (errHub ).Debug ("failed to fetch antigravity hub GCS version , trying legacy releases API " )
228+ log .WithError (errManifest ).Debug ("failed to fetch antigravity CLI updater manifest , trying CLI latest pointer " )
204229
205- version , errLegacy := fetchAntigravityLegacyLatestVersion (ctx , client )
206- if errLegacy == nil {
230+ version , errLatest := fetchAntigravityCLILatestVersion (ctx , client )
231+ if errLatest == nil {
207232 return version , nil
208233 }
209234
210- return "" , fmt .Errorf ("fetch antigravity hub GCS version: %v; fetch legacy releases: %w" , errHub , errLegacy )
235+ log .WithError (errLatest ).Debug ("failed to fetch antigravity CLI latest version, trying CLI GCS prefix list" )
236+
237+ version , errList := fetchAntigravityCLIGCSLatestVersion (ctx , client )
238+ if errList == nil {
239+ return version , nil
240+ }
241+
242+ return "" , fmt .Errorf ("fetch antigravity CLI updater manifest: %v; fetch antigravity CLI latest: %v; fetch antigravity CLI GCS version: %w" , errManifest , errLatest , errList )
211243}
212244
213- func fetchAntigravityHubGCSLatestVersion (ctx context.Context , client * http.Client ) (string , error ) {
214- httpReq , errReq := http .NewRequestWithContext (ctx , http .MethodGet , antigravityHubGCSListURL , nil )
245+ func fetchAntigravityCLIUpdaterManifestVersion (ctx context.Context , client * http.Client ) (string , error ) {
246+ manifestURL := fmt .Sprintf ("%s/%s.json" , strings .TrimSuffix (antigravityCLIUpdaterBaseURL , "/" ), antigravityCLIUpdaterManifestName ())
247+ httpReq , errReq := http .NewRequestWithContext (ctx , http .MethodGet , manifestURL , nil )
215248 if errReq != nil {
216- return "" , fmt .Errorf ("build antigravity hub GCS request: %w" , errReq )
249+ return "" , fmt .Errorf ("build antigravity CLI updater manifest request: %w" , errReq )
217250 }
218251
219252 resp , errDo := client .Do (httpReq )
220253 if errDo != nil {
221- return "" , fmt .Errorf ("fetch antigravity hub GCS list : %w" , errDo )
254+ return "" , fmt .Errorf ("fetch antigravity CLI updater manifest : %w" , errDo )
222255 }
223256 defer func () {
224257 if errClose := resp .Body .Close (); errClose != nil {
225- log .WithError (errClose ).Warn ("antigravity hub GCS response body close error" )
258+ log .WithError (errClose ).Warn ("antigravity CLI updater manifest response body close error" )
226259 }
227260 }()
228261
229262 if resp .StatusCode != http .StatusOK {
230- return "" , fmt .Errorf ("antigravity hub GCS list returned status %d" , resp .StatusCode )
263+ return "" , fmt .Errorf ("antigravity CLI updater manifest returned status %d" , resp .StatusCode )
231264 }
232265
233- var list antigravityHubGCSList
234- if errDecode := xml . NewDecoder ( resp . Body ). Decode ( & list ); errDecode != nil {
235- return "" , fmt .Errorf ("decode antigravity hub GCS list : %w" , errDecode )
266+ raw , errRead := io . ReadAll ( io . LimitReader ( resp . Body , 4096 ))
267+ if errRead != nil {
268+ return "" , fmt .Errorf ("read antigravity CLI updater manifest : %w" , errRead )
236269 }
237270
238- prefixes := make ([] string , 0 , len ( list . CommonPrefixes ))
239- for _ , commonPrefix := range list . CommonPrefixes {
240- prefixes = append ( prefixes , commonPrefix . Prefix )
271+ var manifest antigravityCLIUpdaterManifest
272+ if errDecode := json . Unmarshal ( raw , & manifest ); errDecode != nil {
273+ return "" , fmt . Errorf ( "decode antigravity CLI updater manifest: %w" , errDecode )
241274 }
242275
243- return latestAntigravityHubVersionFromPrefixes (prefixes )
276+ version := strings .TrimSpace (manifest .Version )
277+ if version == "" {
278+ return "" , errors .New ("antigravity CLI updater manifest returned empty version" )
279+ }
280+ if _ , ok := parseAntigravitySemVersion (version ); ! ok {
281+ return "" , fmt .Errorf ("antigravity CLI updater manifest returned invalid version %q" , version )
282+ }
283+ return version , nil
244284}
245285
246- func fetchAntigravityLegacyLatestVersion (ctx context.Context , client * http.Client ) (string , error ) {
247- httpReq , errReq := http .NewRequestWithContext (ctx , http .MethodGet , antigravityReleasesURL , nil )
286+ func fetchAntigravityCLILatestVersion (ctx context.Context , client * http.Client ) (string , error ) {
287+ httpReq , errReq := http .NewRequestWithContext (ctx , http .MethodGet , antigravityCLILatestURL , nil )
248288 if errReq != nil {
249- return "" , fmt .Errorf ("build antigravity releases request: %w" , errReq )
289+ return "" , fmt .Errorf ("build antigravity CLI latest request: %w" , errReq )
250290 }
251291
252292 resp , errDo := client .Do (httpReq )
253293 if errDo != nil {
254- return "" , fmt .Errorf ("fetch antigravity releases : %w" , errDo )
294+ return "" , fmt .Errorf ("fetch antigravity CLI latest : %w" , errDo )
255295 }
256296 defer func () {
257297 if errClose := resp .Body .Close (); errClose != nil {
258- log .WithError (errClose ).Warn ("antigravity releases response body close error" )
298+ log .WithError (errClose ).Warn ("antigravity CLI latest response body close error" )
259299 }
260300 }()
261301
262302 if resp .StatusCode != http .StatusOK {
263- return "" , fmt .Errorf ("antigravity releases API returned status %d" , resp .StatusCode )
303+ return "" , fmt .Errorf ("antigravity CLI latest returned status %d" , resp .StatusCode )
264304 }
265305
266- var releases []antigravityRelease
267- if errDecode := json .NewDecoder (resp .Body ).Decode (& releases ); errDecode != nil {
268- return "" , fmt .Errorf ("decode antigravity releases response: %w" , errDecode )
306+ raw , errRead := io .ReadAll (io .LimitReader (resp .Body , 256 ))
307+ if errRead != nil {
308+ return "" , fmt .Errorf ("read antigravity CLI latest: %w" , errRead )
309+ }
310+ version := strings .TrimSpace (string (raw ))
311+ if version == "" {
312+ return "" , errors .New ("antigravity CLI latest returned empty version" )
313+ }
314+ semVersion , ok := parseAntigravitySemVersion (version )
315+ if ! ok {
316+ return "" , fmt .Errorf ("antigravity CLI latest returned invalid version %q" , version )
269317 }
318+ return semVersion .raw , nil
319+ }
270320
271- if len (releases ) == 0 {
272- return "" , errors .New ("antigravity releases API returned empty list" )
321+ func fetchAntigravityCLIGCSLatestVersion (ctx context.Context , client * http.Client ) (string , error ) {
322+ httpReq , errReq := http .NewRequestWithContext (ctx , http .MethodGet , antigravityCLIGCSListURL , nil )
323+ if errReq != nil {
324+ return "" , fmt .Errorf ("build antigravity CLI GCS request: %w" , errReq )
273325 }
274326
275- version := releases [ 0 ]. Version
276- if version == "" {
277- return "" , errors . New ( " antigravity releases API returned empty version" )
327+ resp , errDo := client . Do ( httpReq )
328+ if errDo != nil {
329+ return "" , fmt . Errorf ( "fetch antigravity CLI GCS list: %w" , errDo )
278330 }
331+ defer func () {
332+ if errClose := resp .Body .Close (); errClose != nil {
333+ log .WithError (errClose ).Warn ("antigravity CLI GCS response body close error" )
334+ }
335+ }()
279336
280- return version , nil
337+ if resp .StatusCode != http .StatusOK {
338+ return "" , fmt .Errorf ("antigravity CLI GCS list returned status %d" , resp .StatusCode )
339+ }
340+
341+ var list antigravityGCSList
342+ if errDecode := xml .NewDecoder (resp .Body ).Decode (& list ); errDecode != nil {
343+ return "" , fmt .Errorf ("decode antigravity CLI GCS list: %w" , errDecode )
344+ }
345+
346+ prefixes := make ([]string , 0 , len (list .CommonPrefixes ))
347+ for _ , commonPrefix := range list .CommonPrefixes {
348+ prefixes = append (prefixes , commonPrefix .Prefix )
349+ }
350+
351+ return latestAntigravityCLIVersionFromPrefixes (prefixes )
281352}
282353
283- func latestAntigravityHubVersionFromPrefixes (prefixes []string ) (string , error ) {
354+ func latestAntigravityCLIVersionFromPrefixes (prefixes []string ) (string , error ) {
284355 var best antigravitySemVersion
285356 found := false
286357
287358 for _ , prefix := range prefixes {
288- version , ok := antigravityHubVersionFromPrefix (prefix )
359+ version , ok := antigravityCLIVersionFromPrefix (prefix )
289360 if ! ok {
290361 continue
291362 }
@@ -300,38 +371,52 @@ func latestAntigravityHubVersionFromPrefixes(prefixes []string) (string, error)
300371 }
301372
302373 if ! found {
303- return "" , errors .New ("antigravity hub GCS list contained no version prefixes" )
374+ return "" , errors .New ("antigravity-cli GCS list contained no version prefixes" )
304375 }
305376
306377 return best .raw , nil
307378}
308379
309- func antigravityHubVersionFromPrefix (prefix string ) (string , bool ) {
310- const hubPrefix = "antigravity-hub/"
311-
380+ func antigravityCLIVersionFromPrefix (prefix string ) (string , bool ) {
381+ const cliPrefix = "antigravity-cli/"
312382 prefix = strings .TrimSpace (prefix )
313383 prefix = strings .TrimSuffix (prefix , "/" )
314- if ! strings .HasPrefix (prefix , hubPrefix ) {
384+ if ! strings .HasPrefix (prefix , cliPrefix ) {
315385 return "" , false
316386 }
317387
318- name := strings .TrimPrefix (prefix , hubPrefix )
319- separator := strings .LastIndex (name , "-" )
320- if separator <= 0 || separator == len (name )- 1 {
388+ name := strings .TrimPrefix (prefix , cliPrefix )
389+ if name == "latest" || name == "test" || name == "tools" || strings .HasPrefix (name , "v" ) {
321390 return "" , false
322391 }
323392
324- version := strings .TrimSpace (name [:separator ])
325- executionID := name [separator + 1 :]
326- if version == "" || executionID == "" {
327- return "" , false
328- }
329- for _ , ch := range executionID {
330- if ch < '0' || ch > '9' {
331- return "" , false
393+ separator := strings .LastIndex (name , "-" )
394+ if separator > 0 && separator < len (name )- 1 {
395+ version := strings .TrimSpace (name [:separator ])
396+ executionID := name [separator + 1 :]
397+ if version != "" && executionID != "" {
398+ allDigits := true
399+ for _ , ch := range executionID {
400+ if ch < '0' || ch > '9' {
401+ allDigits = false
402+ break
403+ }
404+ }
405+ if allDigits {
406+ if _ , ok := parseAntigravitySemVersion (version ); ok {
407+ return version , true
408+ }
409+ }
332410 }
333411 }
334412
413+ version := strings .TrimSpace (name )
414+ if version == "" {
415+ return "" , false
416+ }
417+ if _ , ok := parseAntigravitySemVersion (version ); ! ok {
418+ return "" , false
419+ }
335420 return version , true
336421}
337422
0 commit comments