@createlex/createlexgenai 1.0.0
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.
- package/README.md +272 -0
- package/bin/createlex.js +5 -0
- package/package.json +45 -0
- package/python/activity_tracker.py +280 -0
- package/python/fastmcp.py +768 -0
- package/python/mcp_server_stdio.py +4720 -0
- package/python/requirements.txt +7 -0
- package/python/subscription_validator.py +199 -0
- package/python/ue_native_handler.py +573 -0
- package/python/ui_slice_host.py +637 -0
- package/src/cli.js +109 -0
- package/src/commands/config.js +56 -0
- package/src/commands/connect.js +100 -0
- package/src/commands/exec.js +148 -0
- package/src/commands/login.js +111 -0
- package/src/commands/logout.js +17 -0
- package/src/commands/serve.js +237 -0
- package/src/commands/setup.js +65 -0
- package/src/commands/status.js +126 -0
- package/src/commands/tools.js +133 -0
- package/src/core/auth-manager.js +147 -0
- package/src/core/config-store.js +81 -0
- package/src/core/discovery.js +71 -0
- package/src/core/ide-configurator.js +189 -0
- package/src/core/remote-execution.js +228 -0
- package/src/core/subscription.js +176 -0
- package/src/core/unreal-connection.js +318 -0
- package/src/core/web-remote-control.js +243 -0
- package/src/utils/logger.js +66 -0
- package/src/utils/python-manager.js +142 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
import requests
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
class SubscriptionValidator:
|
|
10
|
+
def __init__(self, api_endpoint="https://your-api.com/validate", secret_key=None):
|
|
11
|
+
self.api_endpoint = api_endpoint
|
|
12
|
+
self.secret_key = secret_key or os.environ.get("SUBSCRIPTION_SECRET", "default-secret")
|
|
13
|
+
self.cache_file = os.path.expanduser("~/.unrealgenai/subscription_cache.json")
|
|
14
|
+
self.cache_duration = 24 * 60 * 60 # 24 hours in seconds
|
|
15
|
+
|
|
16
|
+
def generate_machine_id(self):
|
|
17
|
+
"""Generate unique machine identifier"""
|
|
18
|
+
import platform
|
|
19
|
+
import uuid
|
|
20
|
+
|
|
21
|
+
# Combine multiple machine identifiers
|
|
22
|
+
machine_info = f"{platform.node()}-{platform.machine()}-{platform.processor()}"
|
|
23
|
+
try:
|
|
24
|
+
mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
|
|
25
|
+
for elements in range(0,2*6,2)][::-1])
|
|
26
|
+
machine_info += f"-{mac_address}"
|
|
27
|
+
except:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
return hashlib.sha256(machine_info.encode()).hexdigest()[:16]
|
|
31
|
+
|
|
32
|
+
def create_signature(self, data):
|
|
33
|
+
"""Create HMAC signature for data"""
|
|
34
|
+
message = json.dumps(data, sort_keys=True)
|
|
35
|
+
return hmac.new(
|
|
36
|
+
self.secret_key.encode(),
|
|
37
|
+
message.encode(),
|
|
38
|
+
hashlib.sha256
|
|
39
|
+
).hexdigest()
|
|
40
|
+
|
|
41
|
+
def load_cached_validation(self):
|
|
42
|
+
"""Load cached validation result"""
|
|
43
|
+
try:
|
|
44
|
+
if os.path.exists(self.cache_file):
|
|
45
|
+
with open(self.cache_file, 'r') as f:
|
|
46
|
+
cache_data = json.load(f)
|
|
47
|
+
|
|
48
|
+
# Check if cache is still valid
|
|
49
|
+
cached_time = cache_data.get('timestamp', 0)
|
|
50
|
+
if time.time() - cached_time < self.cache_duration:
|
|
51
|
+
return cache_data.get('valid', False)
|
|
52
|
+
except:
|
|
53
|
+
pass
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def save_cached_validation(self, is_valid):
|
|
57
|
+
"""Save validation result to cache"""
|
|
58
|
+
try:
|
|
59
|
+
os.makedirs(os.path.dirname(self.cache_file), exist_ok=True)
|
|
60
|
+
cache_data = {
|
|
61
|
+
'valid': is_valid,
|
|
62
|
+
'timestamp': time.time()
|
|
63
|
+
}
|
|
64
|
+
with open(self.cache_file, 'w') as f:
|
|
65
|
+
json.dump(cache_data, f)
|
|
66
|
+
except:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def validate_subscription(self, license_key=None, user_email=None):
|
|
70
|
+
"""Validate subscription with server"""
|
|
71
|
+
# First check cache
|
|
72
|
+
cached_result = self.load_cached_validation()
|
|
73
|
+
if cached_result is not None:
|
|
74
|
+
return cached_result
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
machine_id = self.generate_machine_id()
|
|
78
|
+
|
|
79
|
+
# Prepare validation data
|
|
80
|
+
validation_data = {
|
|
81
|
+
'license_key': license_key or os.environ.get("LICENSE_KEY"),
|
|
82
|
+
'user_email': user_email or os.environ.get("USER_EMAIL"),
|
|
83
|
+
'machine_id': machine_id,
|
|
84
|
+
'timestamp': int(time.time()),
|
|
85
|
+
'product': 'unrealgenai-mcp'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Create signature
|
|
89
|
+
signature = self.create_signature(validation_data)
|
|
90
|
+
validation_data['signature'] = signature
|
|
91
|
+
|
|
92
|
+
# Make API request
|
|
93
|
+
response = requests.post(
|
|
94
|
+
self.api_endpoint,
|
|
95
|
+
json=validation_data,
|
|
96
|
+
timeout=10,
|
|
97
|
+
headers={'Content-Type': 'application/json'}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if response.status_code == 200:
|
|
101
|
+
result = response.json()
|
|
102
|
+
is_valid = result.get('valid', False)
|
|
103
|
+
|
|
104
|
+
# Cache the result
|
|
105
|
+
self.save_cached_validation(is_valid)
|
|
106
|
+
return is_valid
|
|
107
|
+
else:
|
|
108
|
+
# Server error - check if we have a recent cache
|
|
109
|
+
return self.handle_validation_error()
|
|
110
|
+
|
|
111
|
+
except requests.RequestException:
|
|
112
|
+
# Network error - check if we have a recent cache
|
|
113
|
+
return self.handle_validation_error()
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"Validation error: {e}")
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def handle_validation_error(self):
|
|
119
|
+
"""Handle validation errors with grace period"""
|
|
120
|
+
try:
|
|
121
|
+
if os.path.exists(self.cache_file):
|
|
122
|
+
with open(self.cache_file, 'r') as f:
|
|
123
|
+
cache_data = json.load(f)
|
|
124
|
+
|
|
125
|
+
# Allow 7 days grace period for network issues
|
|
126
|
+
cached_time = cache_data.get('timestamp', 0)
|
|
127
|
+
grace_period = 7 * 24 * 60 * 60 # 7 days
|
|
128
|
+
|
|
129
|
+
if time.time() - cached_time < grace_period:
|
|
130
|
+
return cache_data.get('valid', False)
|
|
131
|
+
except:
|
|
132
|
+
pass
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def get_subscription_info(self):
|
|
136
|
+
"""Get detailed subscription information"""
|
|
137
|
+
try:
|
|
138
|
+
if os.path.exists(self.cache_file):
|
|
139
|
+
with open(self.cache_file, 'r') as f:
|
|
140
|
+
cache_data = json.load(f)
|
|
141
|
+
|
|
142
|
+
cached_time = cache_data.get('timestamp', 0)
|
|
143
|
+
is_valid = cache_data.get('valid', False)
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
'valid': is_valid,
|
|
147
|
+
'last_checked': datetime.fromtimestamp(cached_time).isoformat(),
|
|
148
|
+
'expires_in_hours': max(0, (self.cache_duration - (time.time() - cached_time)) / 3600)
|
|
149
|
+
}
|
|
150
|
+
except:
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
'valid': False,
|
|
155
|
+
'last_checked': 'Never',
|
|
156
|
+
'expires_in_hours': 0
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Usage example:
|
|
160
|
+
def check_subscription():
|
|
161
|
+
"""Main subscription check function"""
|
|
162
|
+
# Development / testing bypasses
|
|
163
|
+
dev_mode = os.environ.get('DEV_MODE')
|
|
164
|
+
node_env = os.environ.get('NODE_ENV')
|
|
165
|
+
if dev_mode == 'true' or node_env == 'development':
|
|
166
|
+
print("Development mode detected - bypassing subscription validation")
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
# Explicit bypass via environment variable
|
|
170
|
+
if os.environ.get('BYPASS_SUBSCRIPTION') == 'true':
|
|
171
|
+
print("BYPASS_SUBSCRIPTION=true - bypassing subscription validation")
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
# Bridge (Electron app) may validate the subscription already and set this flag
|
|
175
|
+
if os.environ.get('BRIDGE_SUBSCRIPTION_VALIDATED') == 'true':
|
|
176
|
+
print("Bridge has already validated subscription - bypassing local validation")
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
validator = SubscriptionValidator()
|
|
180
|
+
|
|
181
|
+
# Try to validate
|
|
182
|
+
is_valid = validator.validate_subscription()
|
|
183
|
+
|
|
184
|
+
if not is_valid:
|
|
185
|
+
print("❌ Invalid or expired subscription!")
|
|
186
|
+
print("Please contact support or renew your license.")
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
print("✅ Subscription validated successfully!")
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
# Integration with MCP server
|
|
193
|
+
def require_subscription(func):
|
|
194
|
+
"""Decorator to require valid subscription for MCP tools"""
|
|
195
|
+
def wrapper(*args, **kwargs):
|
|
196
|
+
if not check_subscription():
|
|
197
|
+
return "❌ This tool requires a valid subscription. Please contact support."
|
|
198
|
+
return func(*args, **kwargs)
|
|
199
|
+
return wrapper
|