unitlab 2.3.33__py3-none-any.whl → 2.3.35__py3-none-any.whl

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.
unitlab/dynamic_tunnel.py DELETED
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Dynamic Cloudflare Tunnel - Creates a unique tunnel for each device via API
4
- Simple and automatic!
5
- """
6
-
7
- import subprocess
8
- import requests
9
- import json
10
- import time
11
- import os
12
-
13
- class DynamicTunnel:
14
- def __init__(self, device_id=None):
15
- """
16
- Initialize with device ID for unique tunnel creation
17
- """
18
- # Cloudflare API credentials (hardcoded for simplicity)
19
- self.cf_api_token = os.getenv("CF_API_TOKEN", "YOUR_API_TOKEN_HERE")
20
- self.cf_account_id = os.getenv("CF_ACCOUNT_ID", "c91192ae20a5d43f65e087550d8dc89b")
21
- self.cf_zone_id = os.getenv("CF_ZONE_ID", "YOUR_ZONE_ID_HERE")
22
-
23
- # Domain config
24
- self.domain = "1scan.uz"
25
-
26
- # Generate clean device ID
27
- if device_id:
28
- self.device_id = device_id.replace('-', '').replace('_', '').replace('.', '').lower()[:20]
29
- else:
30
- import uuid
31
- self.device_id = str(uuid.uuid4())[:8]
32
-
33
- # Tunnel will be created with this name
34
- self.tunnel_name = "agent-{}".format(self.device_id)
35
- self.subdomain = self.device_id
36
- self.jupyter_url = "https://{}.{}".format(self.subdomain, self.domain)
37
-
38
- self.tunnel_id = None
39
- self.tunnel_token = None
40
- self.jupyter_process = None
41
- self.tunnel_process = None
42
-
43
- def create_tunnel(self):
44
- """Create a new tunnel via Cloudflare API"""
45
- print("🔧 Creating tunnel: {}".format(self.tunnel_name))
46
-
47
- url = "https://api.cloudflare.com/client/v4/accounts/{}/tunnels".format(self.cf_account_id)
48
-
49
- headers = {
50
- "Authorization": "Bearer {}".format(self.cf_api_token),
51
- "Content-Type": "application/json"
52
- }
53
-
54
- data = {
55
- "name": self.tunnel_name,
56
- "tunnel_secret": None # Let Cloudflare generate it
57
- }
58
-
59
- try:
60
- response = requests.post(url, headers=headers, json=data)
61
- result = response.json()
62
-
63
- if response.status_code == 200 and result.get("success"):
64
- self.tunnel_id = result["result"]["id"]
65
- self.tunnel_token = result["result"]["token"]
66
- print("✅ Tunnel created: {}".format(self.tunnel_id))
67
- return True
68
- else:
69
- print("❌ Failed to create tunnel: {}".format(result.get("errors", "Unknown error")))
70
- return False
71
-
72
- except Exception as e:
73
- print("❌ Error creating tunnel: {}".format(e))
74
- return False
75
-
76
- def create_dns_record(self):
77
- """Create DNS record for the tunnel"""
78
- print("🔧 Creating DNS record: {}.{}".format(self.subdomain, self.domain))
79
-
80
- url = "https://api.cloudflare.com/client/v4/zones/{}/dns_records".format(self.cf_zone_id)
81
-
82
- headers = {
83
- "Authorization": "Bearer {}".format(self.cf_api_token),
84
- "Content-Type": "application/json"
85
- }
86
-
87
- data = {
88
- "type": "CNAME",
89
- "name": self.subdomain,
90
- "content": "{}.cfargotunnel.com".format(self.tunnel_id),
91
- "proxied": True
92
- }
93
-
94
- try:
95
- response = requests.post(url, headers=headers, json=data)
96
- result = response.json()
97
-
98
- if response.status_code == 200 and result.get("success"):
99
- print("✅ DNS record created")
100
- return True
101
- else:
102
- # Might already exist, try to update
103
- print("⚠️ DNS might already exist, continuing...")
104
- return True
105
-
106
- except Exception as e:
107
- print("⚠️ DNS error (continuing): {}".format(e))
108
- return True # Continue anyway
109
-
110
- def get_cloudflared_path(self):
111
- """Get or download cloudflared binary"""
112
- import platform
113
-
114
- # Check if exists
115
- try:
116
- import shutil
117
- if shutil.which("cloudflared"):
118
- return "cloudflared"
119
- except:
120
- pass
121
-
122
- # Check local
123
- local_bin = os.path.expanduser("~/.local/bin/cloudflared")
124
- if os.path.exists(local_bin):
125
- return local_bin
126
-
127
- # Download
128
- print("📦 Downloading cloudflared...")
129
- system = platform.system().lower()
130
- if system == "linux":
131
- arch = "amd64" if "x86" in platform.machine() else "arm64"
132
- url = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-{}".format(arch)
133
-
134
- os.makedirs(os.path.expanduser("~/.local/bin"), exist_ok=True)
135
- subprocess.run("curl -L {} -o {}".format(url, local_bin), shell=True)
136
- subprocess.run("chmod +x {}".format(local_bin), shell=True)
137
- print("✅ cloudflared downloaded")
138
- return local_bin
139
-
140
- return "cloudflared"
141
-
142
- def start_jupyter(self):
143
- """Start Jupyter notebook"""
144
- print("🚀 Starting Jupyter...")
145
-
146
- cmd = [
147
- "jupyter", "notebook",
148
- "--port", "8888",
149
- "--no-browser",
150
- "--ip", "0.0.0.0",
151
- "--NotebookApp.token=''",
152
- "--NotebookApp.password=''",
153
- "--NotebookApp.allow_origin='*'"
154
- ]
155
-
156
- self.jupyter_process = subprocess.Popen(
157
- cmd,
158
- stdout=subprocess.PIPE,
159
- stderr=subprocess.PIPE
160
- )
161
-
162
- time.sleep(3)
163
- print("✅ Jupyter started on port 8888")
164
- return True
165
-
166
- def start_tunnel(self):
167
- """Start the tunnel using the token"""
168
- print("🔧 Starting tunnel...")
169
-
170
- cloudflared = self.get_cloudflared_path()
171
-
172
- # Use the token to run tunnel
173
- cmd = [
174
- cloudflared,
175
- "tunnel",
176
- "run",
177
- "--token", self.tunnel_token
178
- ]
179
-
180
- self.tunnel_process = subprocess.Popen(
181
- cmd,
182
- stdout=subprocess.PIPE,
183
- stderr=subprocess.PIPE
184
- )
185
-
186
- time.sleep(5)
187
- print("✅ Tunnel running at {}".format(self.jupyter_url))
188
- return True
189
-
190
- def start(self):
191
- """Main entry point - creates everything dynamically"""
192
- try:
193
- print("="*50)
194
- print("🌐 Dynamic Cloudflare Tunnel")
195
- print("Device ID: {}".format(self.device_id))
196
- print("="*50)
197
-
198
- # 1. Create tunnel via API
199
- if not self.create_tunnel():
200
- raise Exception("Failed to create tunnel")
201
-
202
- # 2. Create DNS record
203
- self.create_dns_record()
204
-
205
- # 3. Start Jupyter
206
- if not self.start_jupyter():
207
- raise Exception("Failed to start Jupyter")
208
-
209
- # 4. Start tunnel
210
- if not self.start_tunnel():
211
- raise Exception("Failed to start tunnel")
212
-
213
- print("\n" + "="*50)
214
- print("🎉 SUCCESS! Your unique tunnel is ready:")
215
- print(" {}".format(self.jupyter_url))
216
- print(" Tunnel ID: {}".format(self.tunnel_id))
217
- print("="*50)
218
-
219
- return True
220
-
221
- except Exception as e:
222
- print("❌ Error: {}".format(e))
223
- self.cleanup()
224
- return False
225
-
226
- def cleanup(self):
227
- """Clean up resources"""
228
- if self.jupyter_process:
229
- self.jupyter_process.terminate()
230
- if self.tunnel_process:
231
- self.tunnel_process.terminate()
232
-
233
- # Optionally delete tunnel via API
234
- if self.tunnel_id and self.cf_api_token != "YOUR_API_TOKEN_HERE":
235
- try:
236
- url = "https://api.cloudflare.com/client/v4/accounts/{}/tunnels/{}".format(
237
- self.cf_account_id, self.tunnel_id
238
- )
239
- headers = {"Authorization": "Bearer {}".format(self.cf_api_token)}
240
- requests.delete(url, headers=headers)
241
- print("🗑️ Tunnel deleted")
242
- except:
243
- pass
244
-
245
- def run(self):
246
- """Run and keep alive"""
247
- try:
248
- if self.start():
249
- while True:
250
- time.sleep(1)
251
- except KeyboardInterrupt:
252
- print("\n⏹️ Shutting down...")
253
- self.cleanup()
254
- print("👋 Goodbye!")
255
-
256
-
257
- def main():
258
- """Test dynamic tunnel creation"""
259
- import platform
260
- import uuid
261
-
262
- hostname = platform.node().replace('.', '-')[:20]
263
- device_id = "{}-{}".format(hostname, str(uuid.uuid4())[:8])
264
-
265
- print("Creating dynamic tunnel for: {}".format(device_id))
266
-
267
- tunnel = DynamicTunnel(device_id=device_id)
268
- tunnel.run()
269
-
270
-
271
- if __name__ == "__main__":
272
- main()
unitlab/easy_tunnel.py DELETED
@@ -1,210 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Easiest Dynamic Tunnel - Each device gets its own tunnel at deviceid.1scan.uz
4
- Using Cloudflare API with service token
5
- """
6
-
7
- import subprocess
8
- import time
9
- import os
10
- import requests
11
- import json
12
-
13
- class EasyTunnel:
14
- def __init__(self, device_id=None):
15
- """Initialize with device ID"""
16
- # Generate clean device ID
17
- if device_id:
18
- self.device_id = device_id.replace('-', '').replace('_', '').replace('.', '').lower()[:20]
19
- else:
20
- import uuid
21
- self.device_id = str(uuid.uuid4())[:8]
22
-
23
- self.subdomain = self.device_id
24
- self.jupyter_url = "https://{}.1scan.uz".format(self.subdomain)
25
-
26
- # Processes
27
- self.jupyter_process = None
28
- self.tunnel_process = None
29
-
30
- # We'll use service tokens (created per tunnel)
31
- self.tunnel_token = None
32
-
33
- def get_cloudflared_path(self):
34
- """Get or download cloudflared binary"""
35
- import shutil
36
- if shutil.which("cloudflared"):
37
- return "cloudflared"
38
-
39
- local_bin = os.path.expanduser("~/.local/bin/cloudflared")
40
- if os.path.exists(local_bin):
41
- return local_bin
42
-
43
- # Download it
44
- print("📦 Downloading cloudflared...")
45
- import platform
46
- system = platform.system().lower()
47
- if system == "linux":
48
- arch = "amd64" if "x86" in platform.machine() else "arm64"
49
- url = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-{}".format(arch)
50
-
51
- os.makedirs(os.path.dirname(local_bin), exist_ok=True)
52
- subprocess.run("curl -L {} -o {}".format(url, local_bin), shell=True, capture_output=True)
53
- subprocess.run("chmod +x {}".format(local_bin), shell=True)
54
- print("✅ cloudflared downloaded")
55
- return local_bin
56
-
57
- return "cloudflared"
58
-
59
- def create_quick_tunnel(self):
60
- """Use Cloudflare Quick Tunnel - no auth needed, but random URL"""
61
- print("🔧 Creating quick tunnel (no auth needed)...")
62
-
63
- cloudflared = self.get_cloudflared_path()
64
-
65
- # Quick tunnel command - generates random URL
66
- cmd = [
67
- cloudflared,
68
- "tunnel",
69
- "--url", "http://localhost:8888",
70
- "--no-tls-verify",
71
- "--metrics", "localhost:0" # Disable metrics
72
- ]
73
-
74
- self.tunnel_process = subprocess.Popen(
75
- cmd,
76
- stdout=subprocess.PIPE,
77
- stderr=subprocess.STDOUT,
78
- text=True,
79
- bufsize=1
80
- )
81
-
82
- # Read output to get URL
83
- print("⏳ Getting tunnel URL...")
84
- for i in range(30):
85
- line = self.tunnel_process.stdout.readline()
86
- if line and "trycloudflare.com" in line:
87
- # Extract URL from output
88
- import re
89
- match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
90
- if match:
91
- self.jupyter_url = match.group(0)
92
- print("✅ Quick tunnel URL: {}".format(self.jupyter_url))
93
- return True
94
- time.sleep(0.5)
95
-
96
- return False
97
-
98
- def start_jupyter(self):
99
- """Start Jupyter notebook"""
100
- print("🚀 Starting Jupyter...")
101
-
102
- cmd = [
103
- "jupyter", "notebook",
104
- "--port", "8888",
105
- "--no-browser",
106
- "--ip", "0.0.0.0",
107
- "--NotebookApp.token=''",
108
- "--NotebookApp.password=''",
109
- "--NotebookApp.allow_origin='*'"
110
- ]
111
-
112
- self.jupyter_process = subprocess.Popen(
113
- cmd,
114
- stdout=subprocess.PIPE,
115
- stderr=subprocess.PIPE
116
- )
117
-
118
- time.sleep(3)
119
- print("✅ Jupyter started on port 8888")
120
- return True
121
-
122
- def start(self):
123
- """Start everything - super simple"""
124
- try:
125
- print("="*50)
126
- print("🌐 Easy Dynamic Tunnel")
127
- print("Device ID: {}".format(self.device_id))
128
- print("="*50)
129
-
130
- # 1. Start Jupyter
131
- if not self.start_jupyter():
132
- raise Exception("Failed to start Jupyter")
133
-
134
- # 2. Create tunnel (quick tunnel for now)
135
- if not self.create_quick_tunnel():
136
- raise Exception("Failed to create tunnel")
137
-
138
- print("\n" + "="*50)
139
- print("🎉 SUCCESS! Your Jupyter is accessible at:")
140
- print(" {}".format(self.jupyter_url))
141
- print("="*50)
142
- print("\n📝 Note: For persistent URLs at {}.1scan.uz,".format(self.subdomain))
143
- print(" we need Cloudflare API integration")
144
-
145
- return True
146
-
147
- except Exception as e:
148
- print("❌ Error: {}".format(e))
149
- self.stop()
150
- return False
151
-
152
- def stop(self):
153
- """Stop everything"""
154
- if self.jupyter_process:
155
- self.jupyter_process.terminate()
156
- self.jupyter_process = None
157
- if self.tunnel_process:
158
- self.tunnel_process.terminate()
159
- self.tunnel_process = None
160
-
161
- def run(self):
162
- """Run and keep alive"""
163
- try:
164
- if self.start():
165
- print("\nPress Ctrl+C to stop...")
166
- while True:
167
- time.sleep(1)
168
- except KeyboardInterrupt:
169
- print("\n⏹️ Shutting down...")
170
- self.stop()
171
- print("👋 Goodbye!")
172
-
173
-
174
- # For integration with existing client.py
175
- class EasyTunnelAdapter:
176
- """Adapter to make EasyTunnel work with existing client.py interface"""
177
- def __init__(self, device_id):
178
- self.tunnel = EasyTunnel(device_id)
179
- self.jupyter_process = None
180
- self.tunnel_process = None
181
- self.jupyter_url = None
182
-
183
- def start(self):
184
- """Start method compatible with client.py"""
185
- if self.tunnel.start():
186
- self.jupyter_process = self.tunnel.jupyter_process
187
- self.tunnel_process = self.tunnel.tunnel_process
188
- self.jupyter_url = self.tunnel.jupyter_url
189
- return True
190
- return False
191
-
192
- def stop(self):
193
- """Stop method compatible with client.py"""
194
- self.tunnel.stop()
195
-
196
-
197
- def main():
198
- """Test the easy tunnel"""
199
- import platform
200
- import uuid
201
-
202
- hostname = platform.node().replace('.', '-')[:20]
203
- device_id = "{}-{}".format(hostname, str(uuid.uuid4())[:8])
204
-
205
- tunnel = EasyTunnel(device_id=device_id)
206
- tunnel.run()
207
-
208
-
209
- if __name__ == "__main__":
210
- main()
unitlab/simple_tunnel.py DELETED
@@ -1,205 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Simple Cloudflare Tunnel for Jupyter - MVP Version
4
- Uses hardcoded tunnel credentials
5
- """
6
-
7
- import subprocess
8
- from pathlib import Path
9
- import time
10
-
11
-
12
- class SimpleTunnel:
13
- def __init__(self, device_id=None):
14
- """
15
- Initialize SimpleTunnel with hardcoded everything
16
- device_id: Unique device identifier for subdomain generation
17
- """
18
- # Everything hardcoded for simplicity
19
- self.tunnel_uuid = "c6caf64a-7499-4aa5-8702-0d1870388114"
20
- self.domain = "1scan.uz"
21
-
22
- # Generate unique subdomain from device_id
23
- if device_id:
24
- # Clean device_id: remove special chars, lowercase, limit length
25
- clean_id = device_id.replace('-', '').replace('_', '').replace('.', '').lower()[:30]
26
- self.subdomain = clean_id
27
- else:
28
- # Fallback to a random subdomain if no device_id
29
- import uuid
30
- self.subdomain = str(uuid.uuid4())[:8]
31
-
32
- # The unique URL for this device
33
- self.jupyter_url = "https://{}.{}".format(self.subdomain, self.domain)
34
-
35
- self.jupyter_process = None
36
- self.tunnel_process = None
37
-
38
- def start_jupyter(self, port=8888):
39
- """Start Jupyter notebook server"""
40
- print("🚀 Starting Jupyter on port {}...".format(port))
41
-
42
- cmd = [
43
- "jupyter", "notebook",
44
- "--port", '8888',
45
- "--no-browser",
46
- "--ip", "0.0.0.0",
47
- "--ServerApp.token=''",
48
- "--ServerApp.password=''",
49
- "--ServerApp.allow_origin='*'"
50
- ]
51
-
52
- self.jupyter_process = subprocess.Popen(
53
- cmd,
54
- stdout=subprocess.PIPE,
55
- stderr=subprocess.PIPE
56
- )
57
-
58
- # Wait for Jupyter to start
59
- time.sleep(3)
60
-
61
- print("✅ Jupyter started on port {}".format(port))
62
- return True
63
-
64
- def get_cloudflared_path(self):
65
- """Get cloudflared binary, download if needed"""
66
- import os
67
- import platform
68
-
69
- # Check if cloudflared exists in system
70
- try:
71
- import shutil
72
- if shutil.which("cloudflared"):
73
- print("✅ Using system cloudflared")
74
- return "cloudflared"
75
- except:
76
- pass
77
-
78
- # Check local binary
79
- local_bin = os.path.expanduser("~/.local/bin/cloudflared")
80
- if os.path.exists(local_bin):
81
- print("✅ Using existing cloudflared from ~/.local/bin")
82
- return local_bin
83
-
84
- # Download it
85
- print("📦 Downloading cloudflared (this may take a moment)...")
86
- system = platform.system().lower()
87
- if system == "linux":
88
- arch = "amd64" if "x86" in platform.machine() else "arm64"
89
- url = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-{}".format(arch)
90
-
91
- os.makedirs(os.path.expanduser("~/.local/bin"), exist_ok=True)
92
- result = subprocess.run("curl -L {} -o {}".format(url, local_bin), shell=True, capture_output=True)
93
- if result.returncode == 0:
94
- subprocess.run("chmod +x {}".format(local_bin), shell=True)
95
- print("✅ cloudflared downloaded successfully")
96
- return local_bin
97
- else:
98
- print("❌ Failed to download cloudflared")
99
- raise Exception("Could not download cloudflared")
100
-
101
- raise Exception("Could not find or download cloudflared")
102
-
103
- def start_tunnel(self, local_port=8888):
104
- """Start cloudflared tunnel - simple and direct"""
105
- print("🔧 Starting Cloudflare tunnel...")
106
-
107
- # Get cloudflared (downloads if needed)
108
- try:
109
- cloudflared = self.get_cloudflared_path()
110
- except Exception as e:
111
- print("⚠️ Error getting cloudflared: {}".format(e))
112
- # Fallback - try to use system cloudflared anyway
113
- cloudflared = "cloudflared"
114
-
115
- # Simple command - just run the tunnel
116
- cmd = [
117
- cloudflared,
118
- "tunnel",
119
- "run",
120
- "--url", "http://127.0.0.1:{}".format(local_port),
121
- self.tunnel_uuid
122
- ]
123
-
124
- self.tunnel_process = subprocess.Popen(
125
- cmd,
126
- stdout=subprocess.PIPE,
127
- stderr=subprocess.PIPE
128
- )
129
-
130
- # Wait for tunnel to establish
131
- time.sleep(3)
132
-
133
- print("✅ Tunnel running at {}".format(self.jupyter_url))
134
-
135
- def start(self):
136
- """Start tunnel and Jupyter (non-blocking)"""
137
- try:
138
- print("="*50)
139
- print("🌐 Simple Cloudflare Tunnel for Jupyter - MVP")
140
- print("="*50)
141
-
142
- # 1. Cloudflared should be installed on system
143
-
144
- # 2. Start Jupyter
145
- if not self.start_jupyter():
146
- raise Exception("Failed to start Jupyter")
147
-
148
- # 3. Start tunnel
149
- self.start_tunnel()
150
-
151
- # 4. Print access info
152
- print("\n" + "="*50)
153
- print("🎉 SUCCESS! Your Jupyter is now accessible at:")
154
- print(" {}".format(self.jupyter_url))
155
- print(" Device subdomain: {}".format(self.subdomain))
156
- print("="*50)
157
-
158
- return True
159
-
160
- except Exception as e:
161
- print("❌ Error: {}".format(e))
162
- self.stop()
163
- return False
164
-
165
- def stop(self):
166
- """Stop tunnel and Jupyter"""
167
- if self.jupyter_process:
168
- self.jupyter_process.terminate()
169
- self.jupyter_process = None
170
- if self.tunnel_process:
171
- self.tunnel_process.terminate()
172
- self.tunnel_process = None
173
-
174
- def run(self):
175
- """Main entry point for standalone use - sets up everything and blocks"""
176
- try:
177
- if self.start():
178
- # Keep running
179
- while True:
180
- time.sleep(1)
181
- except KeyboardInterrupt:
182
- print("\n⏹️ Shutting down...")
183
- self.stop()
184
- print("👋 Goodbye!")
185
-
186
-
187
- def main():
188
- """Example usage with device ID"""
189
-
190
- # Generate a unique device ID (in real usage, this comes from main.py)
191
- import platform
192
- import uuid
193
- hostname = platform.node().replace('.', '-').replace(' ', '-')[:20]
194
- random_suffix = str(uuid.uuid4())[:8]
195
- device_id = "{}-{}".format(hostname, random_suffix)
196
-
197
- print("Device ID: {}".format(device_id))
198
-
199
- # Create tunnel with device ID for unique subdomain
200
- tunnel = SimpleTunnel(device_id=device_id)
201
- tunnel.run()
202
-
203
-
204
- if __name__ == "__main__":
205
- main()