VibesAI-api 1.4.0__tar.gz → 1.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VibesAI-api
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Unofficial Python API client for vibes.ai
5
5
  Author-email: Ashiq Hussain Mir <imsrenpsycho@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VibesAI-api
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Unofficial Python API client for vibes.ai
5
5
  Author-email: Ashiq Hussain Mir <imsrenpsycho@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "VibesAI-api"
3
- version = "1.4.0"
3
+ version = "1.4.2"
4
4
  description = "Unofficial Python API client for vibes.ai"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
File without changes
@@ -42,9 +42,9 @@ class TestImports(unittest.TestCase):
42
42
  for name in vibes_api.__all__:
43
43
  self.assertTrue(hasattr(vibes_api, name), f"Missing export: {name}")
44
44
 
45
- def test_version_is_1_4_0(self):
45
+ def test_version_is_1_4_2(self):
46
46
  import vibes_api
47
- self.assertEqual(vibes_api.__version__, "1.4.0")
47
+ self.assertEqual(vibes_api.__version__, "1.4.2")
48
48
 
49
49
  def test_no_syntax_errors(self):
50
50
  import vibes_api.client
@@ -94,7 +94,7 @@ from .ingredients import (
94
94
  )
95
95
  from .composition import Composition
96
96
 
97
- __version__ = "1.4.0"
97
+ __version__ = "1.4.2"
98
98
  __all__ = [
99
99
  # Client
100
100
  "VibesClient",
@@ -264,6 +264,10 @@ class VibesClient:
264
264
 
265
265
  Returns True if the session was successfully restored.
266
266
  """
267
+ # Don't retry re-login if we're already in the middle of one
268
+ if self._session_expired:
269
+ return False
270
+
267
271
  self._session_expired = True
268
272
 
269
273
  if self.on_session_expired:
@@ -272,9 +276,10 @@ class VibesClient:
272
276
  except Exception:
273
277
  pass
274
278
 
275
- if self.auto_relogin:
279
+ if self.auto_relogin and self.auth_meta_cookies:
276
280
  new_cookie = self._attempt_oidc_relogin()
277
281
  if new_cookie:
282
+ # _set_cookie resets _session_expired to False
278
283
  self._set_cookie(new_cookie)
279
284
  if self.on_cookie_refresh:
280
285
  try:
@@ -283,33 +288,31 @@ class VibesClient:
283
288
  pass
284
289
  return True
285
290
 
291
+ # Reset the flag so future 401s can attempt re-login again
292
+ self._session_expired = False
286
293
  return False
287
294
 
288
295
  def _attempt_oidc_relogin(self) -> Optional[str]:
289
296
  """Attempt to re-login via the OIDC flow using auth.meta.com cookies.
290
297
 
291
- This follows the full OIDC redirect chain:
292
- 1. vibes.ai/api/meta-oidc/start gets oauth_csrf_token cookie
293
- 2. auth.meta.com/oidc → gets authorization code (using auth cookies)
294
- 3. auth.meta.ai/ecto → exchanges code for vibes.ai callback URL
295
- 4. vibes.ai/api/meta-oidc/callback → creates new session
296
-
297
- Requires ``auth_meta_cookies`` to be set (auth.meta.com cookies).
298
+ Follows the full OIDC redirect chain using requests.get() (not Session)
299
+ to avoid cookie/header interference between steps.
298
300
 
299
301
  Returns the new meta_session cookie value, or None if re-login failed.
300
302
  """
301
303
  if not self.auth_meta_cookies:
302
304
  return None
303
305
 
306
+ ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
307
+
304
308
  for attempt in range(3):
305
309
  try:
306
- # Step 1: Start OIDC flow at vibes.ai (get CSRF token + redirect URL)
307
- s1 = requests.Session()
308
- s1.headers.update({
309
- "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
310
- "Cookie": f"meta_session={self._current_meta_session};cookie_ack=true",
311
- })
312
- r1 = s1.get(f"{self.base_url}/api/meta-oidc/start", allow_redirects=False, timeout=15)
310
+ # Step 1: Start OIDC flow at vibes.ai
311
+ r1 = requests.get(
312
+ f"{self.base_url}/api/meta-oidc/start",
313
+ headers={"User-Agent": ua, "Cookie": "cookie_ack=true"},
314
+ allow_redirects=False, timeout=15,
315
+ )
313
316
  if r1.status_code not in (302, 307):
314
317
  if attempt < 2:
315
318
  time.sleep(5)
@@ -321,70 +324,68 @@ class VibesClient:
321
324
 
322
325
  # Extract oauth_csrf_token from Set-Cookie
323
326
  csrf_token = None
324
- set_cookie = r1.headers.get("Set-Cookie", "")
325
- for part in set_cookie.split(";"):
326
- part = part.strip()
327
- if part.startswith("oauth_csrf_token="):
328
- csrf_token = part[len("oauth_csrf_token="):]
327
+ for part in r1.headers.get("Set-Cookie", "").split(";"):
328
+ if part.strip().startswith("oauth_csrf_token="):
329
+ csrf_token = part.strip()[len("oauth_csrf_token="):]
329
330
  break
330
331
 
331
- # Step 2: auth.meta.com use ONLY auth.meta.com cookies (no vibes.ai cookies)
332
- s2 = requests.Session()
333
- s2.headers.update({
334
- "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
335
- "Cookie": self.auth_meta_cookies,
336
- })
337
- r2 = s2.get(redirect_url, allow_redirects=False, timeout=15)
332
+ # Step 2: auth.meta.com (with auth.meta.com cookies only)
333
+ r2 = requests.get(
334
+ redirect_url,
335
+ headers={"User-Agent": ua, "Cookie": self.auth_meta_cookies},
336
+ allow_redirects=False, timeout=15,
337
+ )
338
338
  if r2.status_code not in (302, 307):
339
- if attempt < 2:
340
- time.sleep(5)
341
- continue
339
+ # 400 = auth.meta.com rate limited or cookies expired
340
+ # Don't retry — makes rate limiting worse
342
341
  return None
343
342
  redirect2 = r2.headers.get("Location", "")
344
343
  if not redirect2 or "code=" not in redirect2:
345
344
  return None
346
345
 
347
- # Step 3: auth.meta.ai/ecto (code exchange) — no cookies needed
348
- s3 = requests.Session()
349
- s3.headers.update({
350
- "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
351
- })
352
- r3 = s3.get(redirect2, allow_redirects=False, timeout=15)
346
+ # Step 3: auth.meta.ai/ecto (code exchange)
347
+ r3 = requests.get(
348
+ redirect2,
349
+ headers={"User-Agent": ua},
350
+ allow_redirects=False, timeout=15,
351
+ )
353
352
  if r3.status_code not in (302, 307):
354
353
  return None
355
354
  redirect3 = r3.headers.get("Location", "")
356
355
  if not redirect3 or "vibes.ai" not in redirect3:
357
356
  return None
358
357
 
359
- # Step 4: vibes.ai callback need vibes.ai cookies + CSRF token
360
- s4 = requests.Session()
361
- s4.headers.update({
362
- "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
363
- })
364
- callback_cookie = f"meta_session={self._current_meta_session};cookie_ack=true"
358
+ # Step 4: vibes.ai callback (CSRF token only, no old session)
359
+ callback_cookie = "cookie_ack=true"
365
360
  if csrf_token:
366
361
  callback_cookie += f";oauth_csrf_token={csrf_token}"
367
- s4.headers.update({"Cookie": callback_cookie})
368
- r4 = s4.get(redirect3, allow_redirects=False, timeout=15)
362
+ r4 = requests.get(
363
+ redirect3,
364
+ headers={"User-Agent": ua, "Cookie": callback_cookie},
365
+ allow_redirects=False, timeout=15,
366
+ )
367
+
368
+ # Check Set-Cookie for new meta_session
369
+ set_cookie = r4.headers.get("Set-Cookie", "").replace(",", ";")
370
+ for part in set_cookie.split(";"):
371
+ part = part.strip()
372
+ if part.startswith("meta_session="):
373
+ val = part[len("meta_session="):]
374
+ if val and val != "deleted" and val != self._current_meta_session:
375
+ return val
369
376
 
370
- # Step 5: Follow final redirect
371
- r5 = None
377
+ # Step 5: Follow final redirect if no cookie in step 4
372
378
  final_redirect = r4.headers.get("Location", "")
373
379
  if final_redirect and "error=" not in final_redirect:
374
380
  if not final_redirect.startswith("http"):
375
381
  final_redirect = f"{self.base_url}{final_redirect}"
376
- s4.headers.update({"Cookie": f"meta_session={self._current_meta_session};cookie_ack=true"})
377
- r5 = s4.get(final_redirect, allow_redirects=True, timeout=15)
378
-
379
- # Check for new meta_session in Set-Cookie headers from step 4 and 5
380
- responses = [r4]
381
- if r5 is not None:
382
- responses.append(r5)
383
- for resp in responses:
384
- set_cookie = resp.headers.get("Set-Cookie", "")
385
- # Multiple cookies are separated by comma — replace with semicolon for parsing
386
- set_cookie = set_cookie.replace(",", ";")
387
- for part in set_cookie.split(";"):
382
+ r5 = requests.get(
383
+ final_redirect,
384
+ headers={"User-Agent": ua, "Cookie": "cookie_ack=true"},
385
+ allow_redirects=True, timeout=15,
386
+ )
387
+ set_cookie_5 = r5.headers.get("Set-Cookie", "").replace(",", ";")
388
+ for part in set_cookie_5.split(";"):
388
389
  part = part.strip()
389
390
  if part.startswith("meta_session="):
390
391
  val = part[len("meta_session="):]
@@ -514,9 +515,10 @@ class VibesClient:
514
515
  except VibesAPIError as e:
515
516
  if e.status == 401 and not self._session_expired:
516
517
  if self._handle_401():
517
- # Session was restored — retry the request
518
+ # Session was restored — retry the request ONCE
518
519
  resp = self.session.get(self._url(path), params=params, timeout=self.timeout, **kw)
519
520
  return self._check(resp)
521
+ # Re-login failed or not enabled — raise the original error
520
522
  raise
521
523
 
522
524
  def _post(self, path: str, json_body: Optional[dict] = None, **kw) -> dict:
File without changes
File without changes
File without changes