nexuscompute-sdk 2.0.0__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.
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexuscompute-sdk
3
+ Version: 2.0.0
4
+ Summary: NexusCompute Unified SDK — python client for renting compute and hosting provider nodes
5
+ Author: NexusCompute
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Requires-Dist: requests>=2.25.0
11
+ Requires-Dist: psutil>=5.8.0
12
+ Dynamic: author
13
+ Dynamic: classifier
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
@@ -0,0 +1,8 @@
1
+ from .client import Renter
2
+ from .provider import Provider, detect_hardware
3
+
4
+ __all__ = [
5
+ "Renter",
6
+ "Provider",
7
+ "detect_hardware",
8
+ ]
@@ -0,0 +1,165 @@
1
+ import sys
2
+ import argparse
3
+ import json
4
+ from .client import Renter
5
+ from .provider import Provider, detect_hardware
6
+
7
+ def renter_cli():
8
+ parser = argparse.ArgumentParser(description="NexusCompute Renter CLI Client")
9
+ parser.add_argument("--api-key", help="API key for authentication")
10
+ parser.add_argument("--token", help="Bearer token for authentication")
11
+ parser.add_argument("--api-url", default="http://localhost:3002/api/v1", help="Nexus API Endpoint URL")
12
+
13
+ subparsers = parser.add_subparsers(dest="command", required=True)
14
+
15
+ # signup
16
+ signup_parser = subparsers.add_parser("signup", help="Create a marketplace account")
17
+ signup_parser.add_argument("-e", "--email", required=True, help="Account email")
18
+ signup_parser.add_argument("-p", "--password", required=True, help="Account password")
19
+
20
+ # login
21
+ login_parser = subparsers.add_parser("login", help="Authenticate and get token")
22
+ login_parser.add_argument("-e", "--email", required=True, help="Account email")
23
+ login_parser.add_argument("-p", "--password", required=True, help="Account password")
24
+
25
+ # pay
26
+ pay_parser = subparsers.add_parser("pay", help="Add USD balance via Square")
27
+ pay_parser.add_argument("-a", "--amount", default="5.00", help="Amount in USD")
28
+ pay_parser.add_argument("-n", "--nonce", default="cnon:card-nonce-ok", help="Square nonce")
29
+
30
+ # train
31
+ train_parser = subparsers.add_parser("train", help="Submit a training job")
32
+ train_parser.add_argument("-m", "--model", default="gemma-4-e2b-it", help="Model name")
33
+ train_parser.add_argument("-d", "--dataset", default="synthetic", help="Dataset name")
34
+ train_parser.add_argument("-e", "--epochs", default="3", help="Training epochs")
35
+ train_parser.add_argument("-c", "--compute", default="local", help="local or colab compute")
36
+ train_parser.add_argument("-p", "--prompt", help="Natural language training prompt")
37
+
38
+ # status
39
+ status_parser = subparsers.add_parser("status", help="Check wallet, jobs, or job status")
40
+ status_parser.add_argument("-j", "--job", help="Specific job ID")
41
+
42
+ # contribute
43
+ contribute_parser = subparsers.add_parser("contribute", help="Open an agent contribution task")
44
+ contribute_parser.add_argument("-a", "--agent", required=True, help="Partner agent name/id")
45
+ contribute_parser.add_argument("-t", "--task", required=True, help="Contribution task description")
46
+ contribute_parser.add_argument("-j", "--job", help="Linked training job ID")
47
+
48
+ # list-hardware
49
+ subparsers.add_parser("list-hardware", help="List all active providers / compute hardware")
50
+
51
+ # get-wallet
52
+ subparsers.add_parser("get-wallet", help="Check renter wallet token balance")
53
+
54
+ # benchmark
55
+ benchmark_parser = subparsers.add_parser("benchmark", help="Run benchmark on target hardware")
56
+ benchmark_parser.add_argument("-H", "--hardware", required=True, help="Target hardware node ID")
57
+ benchmark_parser.add_argument("-s", "--size", default="512", help="Matrix size")
58
+ benchmark_parser.add_argument("-i", "--iterations", default="3", help="Number of iterations")
59
+ benchmark_parser.add_argument("-b", "--backend", default="cpu", help="Execution backend")
60
+
61
+ # gemm
62
+ gemm_parser = subparsers.add_parser("gemm", help="Submit General Matrix Multiply operation")
63
+ gemm_parser.add_argument("-H", "--hardware", required=True, help="Target hardware node ID")
64
+ gemm_parser.add_argument("-a", "--matrix-a", required=True, help="Matrix A (JSON nested array, e.g. [[1,2]])")
65
+ gemm_parser.add_argument("-b", "--matrix-b", required=True, help="Matrix B (JSON nested array, e.g. [[3,4]])")
66
+ gemm_parser.add_argument("-t", "--transpose-a", action="store_true", help="Transpose matrix A")
67
+ gemm_parser.add_argument("-u", "--transpose-b", action="store_true", help="Transpose matrix B")
68
+ gemm_parser.add_argument("-k", "--backend", default="cpu", help="Execution backend")
69
+
70
+ args = parser.parse_args()
71
+
72
+ client = Renter(api_key=args.api_key, token=args.token, base_url=args.api_url)
73
+
74
+ try:
75
+ if args.command == "signup":
76
+ res = client.register(args.email, args.password)
77
+ print(f"Signed up successfully as {res.get('user', {}).get('email')}")
78
+ print(json.dumps(res, indent=2))
79
+ elif args.command == "login":
80
+ res = client.login(args.email, args.password)
81
+ print(f"Logged in successfully. Session token saved.")
82
+ print(json.dumps(res, indent=2))
83
+ elif args.command == "pay":
84
+ res = client.pay(args.amount, args.nonce)
85
+ print(json.dumps(res, indent=2))
86
+ elif args.command == "train":
87
+ payload = {
88
+ "model": args.model,
89
+ "dataset": args.dataset,
90
+ "epochs": int(args.epochs),
91
+ "computeTarget": args.compute,
92
+ "prompt": args.prompt
93
+ }
94
+ res = client.train(payload)
95
+ print(json.dumps(res, indent=2))
96
+ elif args.command == "status":
97
+ if args.job:
98
+ res = client.get_job(args.job)
99
+ else:
100
+ res = client.list_jobs()
101
+ print(json.dumps(res, indent=2))
102
+ elif args.command == "contribute":
103
+ payload = {
104
+ "agentId": args.agent,
105
+ "taskDescription": args.task
106
+ }
107
+ if args.job:
108
+ payload["linkedJobId"] = args.job
109
+ res = client.contribute(payload)
110
+ print(json.dumps(res, indent=2))
111
+ elif args.command == "list-hardware":
112
+ res = client.list_hardware()
113
+ print(json.dumps(res, indent=2))
114
+ elif args.command == "get-wallet":
115
+ res = client.get_wallet()
116
+ print(json.dumps(res, indent=2))
117
+ elif args.command == "benchmark":
118
+ payload = {
119
+ "hardwareId": args.hardware,
120
+ "size": int(args.size),
121
+ "iterations": int(args.iterations),
122
+ "backend": args.backend
123
+ }
124
+ res = client.quote_train(payload) if False else client._request("POST", "/ops/benchmark", json=payload)
125
+ print(json.dumps(res, indent=2))
126
+ elif args.command == "gemm":
127
+ payload = {
128
+ "hardwareId": args.hardware,
129
+ "a": json.loads(args.matrix_a),
130
+ "b": json.loads(args.matrix_b),
131
+ "transposeA": args.transpose_a,
132
+ "transposeB": args.transpose_b,
133
+ "backend": args.backend
134
+ }
135
+ res = client._request("POST", "/ops/gemm", json=payload)
136
+ print(json.dumps(res, indent=2))
137
+ except Exception as e:
138
+ print(f"Error: {e}", file=sys.stderr)
139
+ sys.exit(1)
140
+
141
+
142
+ def provider_cli():
143
+ parser = argparse.ArgumentParser(description="NexusCompute Provider Agent Daemon")
144
+ parser.add_argument("--token", help="Hardware node connection token")
145
+ parser.add_argument("--api-url", default="http://localhost:3002/api/v1", help="Nexus API Endpoint URL")
146
+ parser.add_argument("--detect", action="store_true", help="Detect system specifications and print them as JSON")
147
+
148
+ args = parser.parse_args()
149
+
150
+ if args.detect:
151
+ hw = detect_hardware()
152
+ print(json.dumps(hw, indent=2))
153
+ sys.exit(0)
154
+
155
+ if not args.token:
156
+ parser.print_help()
157
+ print("\nError: --token is required to run the provider agent.", file=sys.stderr)
158
+ sys.exit(1)
159
+
160
+ try:
161
+ agent = Provider(token=args.token, api_url=args.api_url)
162
+ agent.start()
163
+ except Exception as e:
164
+ print(f"Fatal: {e}", file=sys.stderr)
165
+ sys.exit(1)
@@ -0,0 +1,137 @@
1
+ import requests
2
+ import time
3
+
4
+ class Renter:
5
+ def __init__(self, api_key=None, token=None, base_url="http://localhost:3002/api/v1"):
6
+ self.api_key = api_key
7
+ self.token = token
8
+ self.base_url = base_url.rstrip("/")
9
+
10
+ def _headers(self):
11
+ headers = {"Content-Type": "application/json"}
12
+ if self.api_key:
13
+ headers["X-API-Key"] = self.api_key
14
+ elif self.token:
15
+ headers["Authorization"] = f"Bearer {self.token}"
16
+ return headers
17
+
18
+ def _request(self, method, path, json=None, params=None):
19
+ url = f"{self.base_url}{path}"
20
+ res = requests.request(method, url, json=json, params=params, headers=self._headers())
21
+ if not res.ok:
22
+ try:
23
+ err_msg = res.json().get("error", f"HTTP {res.status_code}")
24
+ except Exception:
25
+ err_msg = f"HTTP {res.status_code}"
26
+ raise RuntimeError(err_msg)
27
+ try:
28
+ return res.json()
29
+ except Exception:
30
+ return {}
31
+
32
+ def login(self, email, password):
33
+ data = self._request("POST", "/auth/login", json={"email": email, "password": password})
34
+ self.token = data.get("token")
35
+ return data
36
+
37
+ def register(self, email, password):
38
+ data = self._request("POST", "/auth/register", json={"email": email, "password": password})
39
+ self.token = data.get("token")
40
+ return data
41
+
42
+ def create_api_key(self, name):
43
+ return self._request("POST", "/auth/api-keys", json={"name": name})
44
+
45
+ def list_hardware(self, **params):
46
+ return self._request("GET", "/hardware", params=params)
47
+
48
+ def get_hardware(self, hardware_id):
49
+ return self._request("GET", f"/hardware/{hardware_id}")
50
+
51
+ def get_priority_tiers(self):
52
+ return self._request("GET", "/network/priority-tiers")
53
+
54
+ def get_exchange_rate(self):
55
+ return self._request("GET", "/exchange/rate")
56
+
57
+ def get_wallet(self):
58
+ return self._request("GET", "/exchange/wallet")
59
+
60
+ def exchange_usd_to_nexus(self, usd_amount):
61
+ return self._request("POST", "/exchange/usd-to-nexus", json={"usdAmount": usd_amount})
62
+
63
+ def get_exchange_history(self, limit=20):
64
+ return self._request("GET", "/exchange/history", params={"limit": limit})
65
+
66
+ def submit_job(self, target, operation, params=None, backend=None, priority=None, priority_tier=None, scheduled_at=None):
67
+ if params is None:
68
+ params = {}
69
+ if isinstance(target, dict):
70
+ hardware_id = target.get("hardwareId")
71
+ priority_tier = target.get("priorityTier", priority_tier) or "standard"
72
+ else:
73
+ hardware_id = target
74
+ priority_tier = priority_tier or "standard"
75
+
76
+ if not hardware_id:
77
+ raise ValueError("hardwareId is required")
78
+
79
+ body = {
80
+ "hardwareId": hardware_id,
81
+ "priorityTier": priority_tier,
82
+ "operation": operation,
83
+ "params": params
84
+ }
85
+ if backend:
86
+ body["backend"] = backend
87
+ if priority:
88
+ body["priority"] = priority
89
+ if scheduled_at:
90
+ body["scheduledAt"] = scheduled_at
91
+
92
+ return self._request("POST", "/jobs", json=body)
93
+
94
+ def get_job(self, job_id):
95
+ return self._request("GET", f"/jobs/{job_id}")
96
+
97
+ def wait_for_job(self, job_id, max_ms=60000, poll_ms=250):
98
+ start = time.time()
99
+ while (time.time() - start) * 1000 < max_ms:
100
+ job = self.get_job(job_id)
101
+ if job.get("status") == "completed":
102
+ return job
103
+ if job.get("status") == "failed":
104
+ raise RuntimeError(job.get("error", "Job failed"))
105
+ time.sleep(poll_ms / 1000.0)
106
+ raise TimeoutError("Timed out waiting for job completion")
107
+
108
+ def get_network_stats(self):
109
+ return self._request("GET", "/network/stats")
110
+
111
+ def get_pricing(self):
112
+ return self._request("GET", "/network/pricing")
113
+
114
+ def get_backends(self):
115
+ return self._request("GET", "/ops/backends")
116
+
117
+ def pay(self, amount_usd, source_id):
118
+ return self._request("POST", "/payments/charge", json={
119
+ "amountUsd": float(amount_usd),
120
+ "sourceId": source_id
121
+ })
122
+
123
+ def train(self, payload):
124
+ return self._request("POST", "/marketplace/train", json=payload)
125
+
126
+ def quote_train(self, payload):
127
+ return self._request("POST", "/marketplace/train/quote", json=payload)
128
+
129
+ def list_jobs(self):
130
+ return self._request("GET", "/marketplace/train")
131
+
132
+ def contribute(self, payload):
133
+ return self._request("POST", "/marketplace/contribute", json=payload)
134
+
135
+ def list_contributions(self):
136
+ return self._request("GET", "/marketplace/contribute")
137
+
@@ -0,0 +1,214 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import shutil
5
+ import socket
6
+ import platform
7
+ import subprocess
8
+ import requests
9
+ import psutil
10
+
11
+ def detect_hardware():
12
+ # 1. Total CPU cores and system memory
13
+ total_mem_gb = round(psutil.virtual_memory().total / (1024 ** 3), 1)
14
+ free_mem_gb = round(psutil.virtual_memory().available / (1024 ** 3), 1)
15
+ cpu_count = psutil.cpu_count(logical=False) or 1
16
+
17
+ hardware = {
18
+ "type": "cpu",
19
+ "model": platform.processor() or "Generic CPU",
20
+ "cores": cpu_count,
21
+ "memory": {
22
+ "totalGb": total_mem_gb,
23
+ "freeGb": free_mem_gb
24
+ },
25
+ "gpus": []
26
+ }
27
+
28
+ # 2. Try to detect NVIDIA GPUs via nvidia-smi
29
+ nvidia_smi = shutil.which("nvidia-smi")
30
+ if nvidia_smi:
31
+ try:
32
+ output = subprocess.check_output([
33
+ nvidia_smi,
34
+ "--query-gpu=name,memory.total,memory.free,utilization.gpu,utilization.memory,temperature.gpu,power.draw",
35
+ "--format=csv,noheader,nounits"
36
+ ], encoding="utf-8")
37
+
38
+ gpus = []
39
+ for line in output.strip().splitlines():
40
+ parts = [p.strip() for p in line.split(",")]
41
+ if len(parts) >= 7:
42
+ gpu_name = parts[0]
43
+ total_vram = float(parts[1])
44
+ free_vram = float(parts[2])
45
+ gpu_util = float(parts[3])
46
+ mem_util = float(parts[4])
47
+ temp = float(parts[5])
48
+ power = float(parts[6])
49
+
50
+ gpus.append({
51
+ "name": gpu_name,
52
+ "vramTotalMb": total_vram,
53
+ "vramFreeMb": free_vram,
54
+ "utilization": gpu_util,
55
+ "memoryUsedPercent": mem_util,
56
+ "temperature": temp,
57
+ "powerDraw": power
58
+ })
59
+ if gpus:
60
+ hardware["type"] = "gpu"
61
+ hardware["model"] = gpus[0]["name"]
62
+ hardware["vramGb"] = round(sum(g["vramTotalMb"] for g in gpus) / 1024.0, 1)
63
+ hardware["gpus"] = gpus
64
+ except Exception:
65
+ pass
66
+
67
+ return hardware
68
+
69
+
70
+ class Provider:
71
+ def __init__(self, token, api_url="http://localhost:3002/api/v1", detect_interval_secs=60, heartbeat_secs=15, poll_secs=5):
72
+ self.token = token
73
+ self.api_url = api_url.rstrip("/")
74
+ self.detect_interval_secs = detect_interval_secs
75
+ self.heartbeat_secs = heartbeat_secs
76
+ self.poll_secs = poll_secs
77
+ self.node_id = None
78
+ self.cached_hardware = None
79
+ self.cached_hardware_at = 0
80
+
81
+ def _headers(self):
82
+ return {
83
+ "Content-Type": "application/json",
84
+ "X-Node-Token": self.token
85
+ }
86
+
87
+ def _request(self, method, path, json=None):
88
+ url = f"{self.api_url}{path}"
89
+ res = requests.request(method, url, json=json, headers=self._headers())
90
+ if not res.ok:
91
+ try:
92
+ err_msg = res.json().get("error", f"HTTP {res.status_code}")
93
+ except Exception:
94
+ err_msg = f"HTTP {res.status_code}"
95
+ raise RuntimeError(err_msg)
96
+ try:
97
+ return res.json()
98
+ except Exception:
99
+ return {}
100
+
101
+ def get_hardware_detection(self):
102
+ now = time.time()
103
+ if not self.cached_hardware or now - self.cached_hardware_at > self.detect_interval_secs:
104
+ self.cached_hardware = detect_hardware()
105
+ self.cached_hardware_at = now
106
+ hw = self.cached_hardware
107
+ gpus_text = f", {len(hw['gpus'])} GPU(s)" if hw["gpus"] else ""
108
+ print(f"[detect] {hw['type']} {hw['model']} — {hw['memory']['totalGb']} GB RAM, {hw['cores']} cores{gpus_text}")
109
+ return self.cached_hardware
110
+
111
+ def build_telemetry(self, hardware):
112
+ gpus = hardware.get("gpus", [])
113
+ if gpus:
114
+ gpu_util = sum(g["utilization"] for g in gpus) / len(gpus)
115
+ gpu_mem = sum(g["memoryUsedPercent"] for g in gpus) / len(gpus)
116
+ temp = sum(g["temperature"] for g in gpus) / len(gpus)
117
+ power = sum(g["powerDraw"] for g in gpus)
118
+ return {
119
+ "gpuUtilization": round(gpu_util, 1),
120
+ "gpuMemory": round(gpu_mem, 1),
121
+ "temperature": round(temp, 1),
122
+ "powerDraw": round(power, 1),
123
+ "cpuCompute": 0,
124
+ "cpuMemory": round(((hardware["memory"]["totalGb"] - hardware["memory"]["freeGb"]) / hardware["memory"]["totalGb"]) * 100, 1)
125
+ }
126
+ else:
127
+ return {
128
+ "gpuUtilization": 0,
129
+ "gpuMemory": 0,
130
+ "temperature": 0,
131
+ "powerDraw": 0,
132
+ "cpuCompute": round(psutil.cpu_percent(), 1),
133
+ "cpuMemory": round(((hardware["memory"]["totalGb"] - hardware["memory"]["freeGb"]) / hardware["memory"]["totalGb"]) * 100, 1)
134
+ }
135
+
136
+ def collect_metrics(self):
137
+ hardware = self.get_hardware_detection()
138
+ return {
139
+ "hostname": socket.gethostname(),
140
+ "platform": sys.platform,
141
+ "cpus": psutil.cpu_count(logical=True),
142
+ "memoryUsedMb": round(psutil.Process(os.getpid()).memory_info().rss / (1024 ** 2), 1),
143
+ "uptime": round(time.time() - psutil.boot_time()),
144
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
145
+ "hardware": hardware,
146
+ "telemetry": self.build_telemetry(hardware),
147
+ "relay": {
148
+ "host": "localhost",
149
+ "port": 0,
150
+ "url": None
151
+ }
152
+ }
153
+
154
+ def heartbeat(self):
155
+ metrics = self.collect_metrics()
156
+ data = self._request("POST", "/nodes/heartbeat", json={"metrics": metrics})
157
+ if data.get("nodeId"):
158
+ self.node_id = data["nodeId"]
159
+ return data
160
+
161
+ def poll_jobs(self):
162
+ try:
163
+ data = self._request("GET", "/nodes/jobs/next")
164
+ if data.get("nodeId"):
165
+ self.node_id = data["nodeId"]
166
+ job = data.get("job")
167
+ if not job or job.get("status") != "running":
168
+ return
169
+
170
+ print(f"[job] received job {job.get('operation')} ({job.get('id')})")
171
+
172
+ # Simple mock execution for Python SDK provider
173
+ try:
174
+ print(f"[job] executing {job.get('operation')}...")
175
+ time.sleep(1) # Simulation
176
+ result = {"status": "success", "message": "Python SDK mock execution successful"}
177
+
178
+ self._request("POST", f"/nodes/jobs/{job.get('id')}/complete", json={
179
+ "result": result,
180
+ "ttftMs": 100
181
+ })
182
+ print(f"[job] completed {job.get('id')}")
183
+ except Exception as ex:
184
+ self._request("POST", f"/nodes/jobs/{job.get('id')}/complete", json={
185
+ "error": str(ex)
186
+ })
187
+ print(f"[job] failed {job.get('id')}: {ex}")
188
+ except Exception as e:
189
+ print(f"[job] poll error: {e}")
190
+
191
+ def start(self):
192
+ print("NexusCompute Python Provider SDK starting")
193
+ print(f" Platform: {platform.platform()}")
194
+ print(f" API: {self.api_url}")
195
+
196
+ first = self.heartbeat()
197
+ print(f" Node ID: {self.node_id or first.get('nodeId')} (assigned by server)")
198
+
199
+ last_heartbeat = time.time()
200
+ last_poll = time.time()
201
+
202
+ print("Provider agent running. Press Ctrl+C to stop.")
203
+ try:
204
+ while True:
205
+ now = time.time()
206
+ if now - last_heartbeat >= self.heartbeat_secs:
207
+ self.heartbeat()
208
+ last_heartbeat = now
209
+ if now - last_poll >= self.poll_secs:
210
+ self.poll_jobs()
211
+ last_poll = now
212
+ time.sleep(1)
213
+ except KeyboardInterrupt:
214
+ print("\nStopping provider agent...")
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: nexuscompute-sdk
3
+ Version: 2.0.0
4
+ Summary: NexusCompute Unified SDK — python client for renting compute and hosting provider nodes
5
+ Author: NexusCompute
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Requires-Dist: requests>=2.25.0
11
+ Requires-Dist: psutil>=5.8.0
12
+ Dynamic: author
13
+ Dynamic: classifier
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
@@ -0,0 +1,11 @@
1
+ setup.py
2
+ nexus_sdk/__init__.py
3
+ nexus_sdk/cli.py
4
+ nexus_sdk/client.py
5
+ nexus_sdk/provider.py
6
+ nexuscompute_sdk.egg-info/PKG-INFO
7
+ nexuscompute_sdk.egg-info/SOURCES.txt
8
+ nexuscompute_sdk.egg-info/dependency_links.txt
9
+ nexuscompute_sdk.egg-info/entry_points.txt
10
+ nexuscompute_sdk.egg-info/requires.txt
11
+ nexuscompute_sdk.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ nexus-provider = nexus_sdk.cli:provider_cli
3
+ nexus-renter = nexus_sdk.cli:renter_cli
@@ -0,0 +1,2 @@
1
+ requests>=2.25.0
2
+ psutil>=5.8.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,25 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="nexuscompute-sdk",
5
+ version="2.0.0",
6
+ description="NexusCompute Unified SDK — python client for renting compute and hosting provider nodes",
7
+ author="NexusCompute",
8
+ packages=find_packages(),
9
+ install_requires=[
10
+ "requests>=2.25.0",
11
+ "psutil>=5.8.0",
12
+ ],
13
+ entry_points={
14
+ "console_scripts": [
15
+ "nexus-renter=nexus_sdk.cli:renter_cli",
16
+ "nexus-provider=nexus_sdk.cli:provider_cli",
17
+ ]
18
+ },
19
+ classifiers=[
20
+ "Programming Language :: Python :: 3",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: OS Independent",
23
+ ],
24
+ python_requires=">=3.8",
25
+ )