unitlab 2.3.40__tar.gz → 2.3.42__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: unitlab
3
- Version: 2.3.40
3
+ Version: 2.3.42
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.40",
5
+ version="2.3.42",
6
6
  license="MIT",
7
7
  author="Unitlab Inc.",
8
8
  author_email="team@unitlab.ai",
@@ -122,7 +122,7 @@ def dataset_download(
122
122
  def run_agent(
123
123
  api_key: API_KEY,
124
124
  device_id: Annotated[str, typer.Option(help="Device ID")] = None,
125
- base_domain: Annotated[str, typer.Option(help="Base domain for tunnels")] = "1scan.uz",
125
+ base_domain: Annotated[str, typer.Option(help="Base domain for tunnels")] = "api-dev.unitlab.ai",
126
126
 
127
127
  ):
128
128
 
@@ -14,7 +14,7 @@ from fastapi import FastAPI
14
14
  import uvicorn
15
15
  import threading
16
16
  import psutil
17
-
17
+ import secrets
18
18
 
19
19
  api = FastAPI()
20
20
 
@@ -38,7 +38,7 @@ class PersistentTunnel:
38
38
 
39
39
  # Clean device ID for subdomain
40
40
  if device_id:
41
- self.device_id = device_id.replace('-', '').replace('_', '').replace('.', '').lower()[:20]
41
+ self.device_id = device_id.replace('_', '').replace('.', '').lower()[:30]
42
42
  else:
43
43
  import uuid
44
44
  self.device_id = str(uuid.uuid4())[:8]
@@ -67,23 +67,23 @@ class PersistentTunnel:
67
67
  "Content-Type": "application/json"
68
68
  }
69
69
 
70
- def get_or_create_tunnel(self):
71
- """Always create a new tunnel with unique name to avoid conflicts"""
72
- # Generate unique tunnel name to avoid conflicts
73
- import uuid
74
- unique_suffix = str(uuid.uuid4())[:8]
75
- self.tunnel_name = "agent-{}-{}".format(self.device_id, unique_suffix)
76
- print("🔧 Creating tunnel: {}...".format(self.tunnel_name))
77
-
78
- # Always create new tunnel
79
- return self.create_new_tunnel()
80
-
70
+ # def get_or_create_tunnel(self):
71
+ # """Always create a new tunnel with unique name to avoid conflicts"""
72
+ # # Generate unique tunnel name to avoid conflicts
73
+ # import uuid
74
+ # unique_suffix = str(uuid.uuid4())[:8]
75
+ # self.tunnel_name = "agent-{}-{}".format(self.device_id, unique_suffix)
76
+ # print("🔧 Creating tunnel: {}...".format(self.tunnel_name))
77
+
78
+ # # Always create new tunnel
79
+ # return self.create_new_tunnel()
80
+
81
+
81
82
  def create_new_tunnel(self):
82
- """Create a brand new tunnel"""
83
+ """Create a new tunnel via Cloudflare API"""
83
84
  print("🔧 Creating new tunnel: {}...".format(self.tunnel_name))
84
85
 
85
86
  # Generate random tunnel secret (32 bytes)
86
- import secrets
87
87
  tunnel_secret = base64.b64encode(secrets.token_bytes(32)).decode()
88
88
 
89
89
  url = "https://api.cloudflare.com/client/v4/accounts/{}/cfd_tunnel".format(self.cf_account_id)
@@ -107,12 +107,13 @@ class PersistentTunnel:
107
107
  "TunnelID": self.tunnel_id
108
108
  }
109
109
 
110
- # Save credentials to file with tunnel name (not ID) for consistency
110
+ # Save credentials to file
111
111
  cred_file = "/tmp/tunnel-{}.json".format(self.tunnel_name)
112
112
  with open(cred_file, 'w') as f:
113
113
  json.dump(self.tunnel_credentials, f)
114
114
 
115
115
  print("✅ Tunnel created: {}".format(self.tunnel_id))
116
+ print("✅ Credentials saved to: {}".format(cred_file))
116
117
  return cred_file
117
118
  else:
118
119
  print("❌ Failed to create tunnel: {}".format(response.text))
@@ -150,26 +151,26 @@ class PersistentTunnel:
150
151
  return False
151
152
 
152
153
  # First, check if SSH DNS record exists and delete it
153
- print("🔍 Checking for existing SSH DNS record: {}.{}".format(self.ssh_subdomain, self.domain))
154
- list_url = "{}?name={}.{}".format(url, self.ssh_subdomain, self.domain)
155
- list_response = requests.get(list_url, headers=headers)
156
-
157
- if list_response.status_code == 200:
158
- records = list_response.json().get("result", [])
159
- print("Found {} existing DNS records".format(len(records)))
160
- print('this is new version')
161
- for record in records:
162
- if record["name"] == "{}.{}".format(self.ssh_subdomain, self.domain):
163
- record_id = record["id"]
164
- print("🗑️ Deleting old SSH DNS record: {}".format(record_id))
165
- delete_url = "{}/{}".format(url, record_id)
166
- delete_response = requests.delete(delete_url, headers=headers)
167
- if delete_response.status_code in [200, 204]:
168
- print("✅ Deleted old SSH DNS record")
169
- else:
170
- print("⚠️ Could not delete old SSH DNS record: {}".format(delete_response.text[:200]))
171
- else:
172
- print("⚠️ Could not list DNS records: {}".format(list_response.text[:200]))
154
+ # print("🔍 Checking for existing SSH DNS record: {}.{}".format(self.ssh_subdomain, self.domain))
155
+ # list_url = "{}?name={}.{}".format(url, self.ssh_subdomain, self.domain)
156
+ # list_response = requests.get(list_url, headers=headers)
157
+
158
+ # if list_response.status_code == 200:
159
+ # records = list_response.json().get("result", [])
160
+ # print("Found {} existing DNS records".format(len(records)))
161
+ # print('this is new version')
162
+ # for record in records:
163
+ # if record["name"] == "{}.{}".format(self.ssh_subdomain, self.domain):
164
+ # record_id = record["id"]
165
+ # print("🗑️ Deleting old SSH DNS record: {}".format(record_id))
166
+ # delete_url = "{}/{}".format(url, record_id)
167
+ # delete_response = requests.delete(delete_url, headers=headers)
168
+ # if delete_response.status_code in [200, 204]:
169
+ # print("✅ Deleted old SSH DNS record")
170
+ # else:
171
+ # print("⚠️ Could not delete old SSH DNS record: {}".format(delete_response.text[:200]))
172
+ # else:
173
+ # print("⚠️ Could not list DNS records: {}".format(list_response.text[:200]))
173
174
 
174
175
  # Wait a moment for DNS deletion to propagate
175
176
  time.sleep(2)
@@ -262,15 +263,20 @@ class PersistentTunnel:
262
263
  f.write("credentials-file: {}\n\n".format(cred_file))
263
264
  f.write("ingress:\n")
264
265
 
265
- # SSH service on dedicated subdomain (s{deviceid}.unitlab-ai.com)
266
+ # SSH service on dedicated subdomain (s{deviceid}.unitlab-ai.com)
266
267
  f.write(" - hostname: {}.{}\n".format(self.ssh_subdomain, self.domain))
267
268
  f.write(" service: ssh://localhost:22\n")
268
269
 
270
+
271
+
272
+
269
273
  # API (more specific path goes first)
270
274
  f.write(" - hostname: {}.{}\n".format(self.subdomain, self.domain))
271
275
  f.write(" path: /api-agent/*\n")
272
276
  f.write(" service: http://localhost:8001\n")
273
277
 
278
+
279
+
274
280
  # Jupyter (general hostname for HTTP)
275
281
  f.write(" - hostname: {}.{}\n".format(self.subdomain, self.domain))
276
282
  f.write(" service: http://localhost:8888\n")
@@ -440,7 +446,7 @@ class PersistentTunnel:
440
446
  # API credentials are hardcoded, so we're ready to go
441
447
 
442
448
  # 1. Get existing or create new tunnel via API
443
- cred_file = self.get_or_create_tunnel()
449
+ cred_file = self.create_new_tunnel()
444
450
 
445
451
 
446
452
  # 2. Create DNS record
@@ -490,6 +496,12 @@ class PersistentTunnel:
490
496
  self.jupyter_process.terminate()
491
497
  if self.tunnel_process:
492
498
  self.tunnel_process.terminate()
499
+ try:
500
+ self.tunnel_process.wait(timeout=5)
501
+ except subprocess.TimeoutExpired:
502
+ self.tunnel_process.kill()
503
+ self.tunnel_process.wait()
504
+ print("✅ Tunnel stopped")
493
505
 
494
506
  # # Optionally delete tunnel when stopping
495
507
  # if self.tunnel_id:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitlab
3
- Version: 2.3.40
3
+ Version: 2.3.42
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