unitlab 2.3.16__tar.gz → 2.3.18__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.1
2
2
  Name: unitlab
3
- Version: 2.3.16
3
+ Version: 2.3.18
4
4
  Home-page: https://github.com/teamunitlab/unitlab-sdk
5
5
  Author: Unitlab Inc.
6
6
  Author-email: team@unitlab.ai
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="unitlab",
5
- version="2.3.16",
5
+ version="2.3.18",
6
6
  license="MIT",
7
7
  author="Unitlab Inc.",
8
8
  author_email="team@unitlab.ai",
@@ -142,12 +142,12 @@ class CloudflareAPITunnel:
142
142
  print(f"🔧 Configuring tunnel routes...")
143
143
 
144
144
  # Get current tunnel config first
145
- get_url = f"{self.api_base}/accounts/{self.account_id}/cfd_tunnel/{self.tunnel_id}/configurations"
145
+ get_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
146
146
 
147
147
  try:
148
148
  # Get existing config
149
149
  response = requests.get(get_url, headers=self.headers)
150
- current_config = response.json()
150
+ current_config = response.json() if response.status_code == 200 else {}
151
151
 
152
152
  # Build new ingress rules
153
153
  new_ingress = [
@@ -190,7 +190,7 @@ class CloudflareAPITunnel:
190
190
  }
191
191
  }
192
192
 
193
- put_url = f"{self.api_base}/accounts/{self.account_id}/cfd_tunnel/{self.tunnel_id}/configurations"
193
+ put_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{self.tunnel_id}/configurations"
194
194
  response = requests.put(put_url, headers=self.headers, json=config_data)
195
195
 
196
196
  if response.status_code == 200:
@@ -206,7 +206,7 @@ class CloudflareAPITunnel:
206
206
  print(" Assuming routes are configured in dashboard.")
207
207
  return True
208
208
 
209
- def create_device_tunnel(self):
209
+ def create_device_tunnel(self, retry_on_fail=True):
210
210
  """
211
211
  Create a unique tunnel for this device if it doesn't exist
212
212
  """
@@ -260,8 +260,13 @@ class CloudflareAPITunnel:
260
260
  print(f"❌ Failed to create tunnel: {create_response.text}")
261
261
  return None
262
262
  else:
263
- # Tunnel exists - update config file in case settings changed
264
- print(f"♻️ Updating configuration for existing tunnel")
263
+ # Tunnel exists - but we need to ensure it can be used
264
+ print(f"♻️ Found existing tunnel, setting up for use")
265
+
266
+ # Store tunnel info for later use
267
+ existing_tunnel['needs_token'] = True # Mark that we'll need to use token
268
+
269
+ # Configure tunnel routes (creates config file)
265
270
  self._configure_tunnel_routes(existing_tunnel['id'])
266
271
 
267
272
  # Ensure DNS records exist
@@ -353,11 +358,6 @@ class CloudflareAPITunnel:
353
358
  try:
354
359
  print("🚀 Starting Cloudflare tunnel...")
355
360
 
356
- # First, try to set up DNS and routes via API
357
- if self.api_token:
358
- self.create_dns_records()
359
- self.update_tunnel_config()
360
-
361
361
  # Ensure cloudflared is available
362
362
  cloudflared_path = self._ensure_cloudflared()
363
363
  if not cloudflared_path:
@@ -366,11 +366,33 @@ class CloudflareAPITunnel:
366
366
  # Create or get existing tunnel for this device
367
367
  device_tunnel = self.create_device_tunnel()
368
368
 
369
+ # Now set up DNS and routes via API after tunnel is created/found
370
+ if self.api_token and device_tunnel:
371
+ self.tunnel_id = device_tunnel['id'] # Ensure tunnel_id is set
372
+ self.create_dns_records()
373
+ self.update_tunnel_config()
374
+
375
+ # Check if we need to recreate the tunnel
376
+ if device_tunnel and device_tunnel.get('needs_recreation'):
377
+ print("🔄 Recreating tunnel due to access issues...")
378
+ # Delete the old tunnel
379
+ tunnel_id = device_tunnel['id']
380
+ delete_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}"
381
+ delete_response = requests.delete(delete_url, headers=self.headers)
382
+ if delete_response.status_code in [200, 204, 404]:
383
+ print(f" ✅ Deleted old tunnel")
384
+ # Try creating a new one with a timestamp suffix
385
+ import time
386
+ self.clean_device_id = f"{self.device_id.replace(' ', '').replace('-', '').replace('.', '').replace('_', '')[:24]}-{int(time.time())}"
387
+ device_tunnel = self.create_device_tunnel(retry_on_fail=False)
388
+ if device_tunnel:
389
+ print(f" ✅ Created new tunnel")
390
+
369
391
  if not device_tunnel:
370
392
  print("❌ Could not create/find device tunnel")
371
393
  # Fallback to shared tunnel if API fails
372
394
  print("⚠️ Falling back to shared tunnel...")
373
- service_token = "eyJhIjoiYzkxMTkyYWUyMGE1ZDQzZjY1ZTA4NzU1MGQ4ZGM4OWIiLCJ0IjoiMDc3N2ZjMTAtNDljNC00NzJkLTg2NjEtZjYwZDgwZDYxODRkIiwicyI6Ik9XRTNaak5tTVdVdE1tWTRaUzAwTmpoakLTazBaalF0WXpjek1tSm1ZVGt4WlRRMCJ9"
395
+ service_token = "eyJhIjoiYzkxMTkyYWUyMGE1ZDQzZjY1ZTA4NzU1MGQ4ZGM4OWIiLCJ0IjoiMDc3N2ZjMTAtNDljNC00NzJkLTg2NjEtZjYwZDgwZDYxODRkIiwicyI6Ik9XRTNaak5tTVdVdE1tWTRaUzAwTmpoakLTazBmalF0WXpjek1tSm1ZVGt4WlRRMCJ9"
374
396
  cmd = [
375
397
  cloudflared_path,
376
398
  "tunnel",
@@ -387,34 +409,73 @@ class CloudflareAPITunnel:
387
409
 
388
410
  # Check if credentials file exists
389
411
  creds_file = Path.home() / '.cloudflared' / f"{tunnel_id}.json"
412
+ config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
413
+ cmd = None # Initialize cmd
390
414
 
391
- if not creds_file.exists():
415
+ if not creds_file.exists() or device_tunnel.get('needs_token'):
392
416
  # Try to recreate credentials from stored secret
393
417
  if device_tunnel.get('tunnel_secret'):
394
418
  self._save_tunnel_credentials(device_tunnel)
419
+ # Continue to use credentials below
395
420
  else:
396
- print("⚠️ No credentials found, requesting from API...")
421
+ print("⚠️ No stored credentials, requesting tunnel token...")
397
422
  # Get token for this tunnel
398
423
  token_url = f"{self.api_base}/accounts/{self.account_id}/tunnels/{tunnel_id}/token"
424
+ print(f" Token URL: {token_url}")
425
+
399
426
  token_response = requests.get(token_url, headers=self.headers)
427
+ print(f" Token response status: {token_response.status_code}")
428
+
400
429
  if token_response.status_code == 200:
401
- token = token_response.json()['result']
402
- # Use token directly
403
- cmd = [
404
- cloudflared_path,
405
- "tunnel",
406
- "--no-autoupdate",
407
- "run",
408
- "--token",
409
- token
410
- ]
430
+ result = token_response.json()
431
+ if 'result' in result:
432
+ token = result['result']
433
+ print(f" ✅ Got tunnel token")
434
+ else:
435
+ print(f" ❌ No token in response: {result}")
436
+ return None
437
+
438
+ # Check if config file exists with ingress rules
439
+ if config_file.exists():
440
+ # Use token with config file for ingress rules
441
+ print(f" Using token with config file: {config_file}")
442
+ cmd = [
443
+ cloudflared_path,
444
+ "tunnel",
445
+ "--no-autoupdate",
446
+ "--config", str(config_file),
447
+ "run",
448
+ "--token", token
449
+ ]
450
+ else:
451
+ # Use token directly
452
+ print(f" Using token directly (no config file)")
453
+ cmd = [
454
+ cloudflared_path,
455
+ "tunnel",
456
+ "--no-autoupdate",
457
+ "run",
458
+ "--token", token
459
+ ]
411
460
  else:
412
- print("❌ Could not get tunnel token")
413
- return None
461
+ print(f"❌ Could not get tunnel token: {token_response.status_code}")
462
+ if token_response.text:
463
+ print(f" Error: {token_response.text}")
464
+
465
+ # If 404, tunnel doesn't exist or we don't have access
466
+ if token_response.status_code == 404:
467
+ print(f"🔄 Tunnel not accessible (404), will try to recreate...")
468
+ # Mark for recreation
469
+ device_tunnel['needs_recreation'] = True
470
+ return None
471
+ else:
472
+ print(f" ℹ️ Token endpoint returned {token_response.status_code}")
473
+ print(f" This might mean the tunnel needs to be recreated manually")
474
+ return None
414
475
 
415
- if creds_file.exists():
476
+ # Only use credentials if we haven't already set cmd with token
477
+ if cmd is None and creds_file.exists():
416
478
  # Check if config file exists
417
- config_file = Path.home() / '.cloudflared' / f'config-{tunnel_id}.yml'
418
479
  if config_file.exists():
419
480
  # Run tunnel with config file (includes routes)
420
481
  cmd = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unitlab
3
- Version: 2.3.16
3
+ Version: 2.3.18
4
4
  Home-page: https://github.com/teamunitlab/unitlab-sdk
5
5
  Author: Unitlab Inc.
6
6
  Author-email: team@unitlab.ai
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes