golem-vm-provider 0.1.50__tar.gz → 0.1.52__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.
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/PKG-INFO +29 -1
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/README.md +28 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/main.py +324 -5
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/pricing.py +8 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/pyproject.toml +1 -1
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/__init__.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/api/__init__.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/api/models.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/api/routes.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/config.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/container.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/data/deployments/l2.json +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/__init__.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/advertiser.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/golem_base_advertiser.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/golem_base_utils.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/multi_advertiser.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/resource_monitor.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/resource_tracker.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/service.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/network/port_verifier.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/payments/blockchain_service.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/payments/monitor.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/payments/stream_map.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/security/ethereum.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/security/faucet.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/service.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/__init__.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/ascii_art.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/logging.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/port_display.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/retry.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/utils/setup.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/__init__.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/cloud_init.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/models.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/multipass.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/multipass_adapter.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/name_mapper.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/port_manager.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/provider.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/proxy_manager.py +0 -0
- {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/vm/service.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: golem-vm-provider
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.52
|
4
4
|
Summary: VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network
|
5
5
|
Keywords: golem,vm,provider,cloud,decentralized
|
6
6
|
Author: Phillip Jensen
|
@@ -436,6 +436,34 @@ poetry run golem-provider streams list --json
|
|
436
436
|
poetry run golem-provider streams show <vm_id>
|
437
437
|
```
|
438
438
|
|
439
|
+
- Summarize earnings and withdrawable amounts:
|
440
|
+
|
441
|
+
```bash
|
442
|
+
poetry run golem-provider streams earnings
|
443
|
+
# or JSON
|
444
|
+
poetry run golem-provider streams earnings --json
|
445
|
+
```
|
446
|
+
|
447
|
+
- Withdraw vested funds:
|
448
|
+
|
449
|
+
```bash
|
450
|
+
# One VM by id
|
451
|
+
poetry run golem-provider streams withdraw --vm-id <vm_id>
|
452
|
+
|
453
|
+
# All mapped streams
|
454
|
+
poetry run golem-provider streams withdraw --all
|
455
|
+
```
|
456
|
+
|
457
|
+
Configure monitor and withdraw via CLI:
|
458
|
+
|
459
|
+
```bash
|
460
|
+
# Set monitor to require 1h remaining, check every 30s
|
461
|
+
poetry run golem-provider config monitor --enable true --interval 30 --min-remaining 3600
|
462
|
+
|
463
|
+
# Enable auto-withdraw every 15 minutes when >= 1e15 wei
|
464
|
+
poetry run golem-provider config withdraw --enable true --interval 900 --min-wei 1000000000000000
|
465
|
+
```
|
466
|
+
|
439
467
|
### Resource Advertisement Flow
|
440
468
|
|
441
469
|
```mermaid
|
@@ -391,6 +391,34 @@ poetry run golem-provider streams list --json
|
|
391
391
|
poetry run golem-provider streams show <vm_id>
|
392
392
|
```
|
393
393
|
|
394
|
+
- Summarize earnings and withdrawable amounts:
|
395
|
+
|
396
|
+
```bash
|
397
|
+
poetry run golem-provider streams earnings
|
398
|
+
# or JSON
|
399
|
+
poetry run golem-provider streams earnings --json
|
400
|
+
```
|
401
|
+
|
402
|
+
- Withdraw vested funds:
|
403
|
+
|
404
|
+
```bash
|
405
|
+
# One VM by id
|
406
|
+
poetry run golem-provider streams withdraw --vm-id <vm_id>
|
407
|
+
|
408
|
+
# All mapped streams
|
409
|
+
poetry run golem-provider streams withdraw --all
|
410
|
+
```
|
411
|
+
|
412
|
+
Configure monitor and withdraw via CLI:
|
413
|
+
|
414
|
+
```bash
|
415
|
+
# Set monitor to require 1h remaining, check every 30s
|
416
|
+
poetry run golem-provider config monitor --enable true --interval 30 --min-remaining 3600
|
417
|
+
|
418
|
+
# Enable auto-withdraw every 15 minutes when >= 1e15 wei
|
419
|
+
poetry run golem-provider config withdraw --enable true --interval 900 --min-wei 1000000000000000
|
420
|
+
```
|
421
|
+
|
394
422
|
### Resource Advertisement Flow
|
395
423
|
|
396
424
|
```mermaid
|
@@ -132,6 +132,8 @@ streams_app = typer.Typer(help="Inspect payment streams")
|
|
132
132
|
cli.add_typer(pricing_app, name="pricing")
|
133
133
|
cli.add_typer(wallet_app, name="wallet")
|
134
134
|
cli.add_typer(streams_app, name="streams")
|
135
|
+
config_app = typer.Typer(help="Configure stream monitoring and withdrawals")
|
136
|
+
cli.add_typer(config_app, name="config")
|
135
137
|
|
136
138
|
@cli.callback()
|
137
139
|
def main():
|
@@ -189,6 +191,9 @@ def streams_list(json_out: bool = typer.Option(False, "--json", help="Output in
|
|
189
191
|
from .container import Container
|
190
192
|
from .config import settings
|
191
193
|
from .payments.blockchain_service import StreamPaymentReader
|
194
|
+
from .utils.pricing import fetch_glm_usd_price, fetch_eth_usd_price
|
195
|
+
from decimal import Decimal
|
196
|
+
from web3 import Web3
|
192
197
|
import json as _json
|
193
198
|
try:
|
194
199
|
if not settings.STREAM_PAYMENT_ADDRESS or settings.STREAM_PAYMENT_ADDRESS == "0x0000000000000000000000000000000000000000":
|
@@ -211,6 +216,7 @@ def streams_list(json_out: bool = typer.Option(False, "--json", help="Output in
|
|
211
216
|
rows.append({
|
212
217
|
"vm_id": vm_id,
|
213
218
|
"stream_id": int(stream_id),
|
219
|
+
"token": str(s.get("token")),
|
214
220
|
"recipient": s["recipient"],
|
215
221
|
"start": int(s["startTime"]),
|
216
222
|
"stop": int(s["stopTime"]),
|
@@ -230,12 +236,66 @@ def streams_list(json_out: bool = typer.Option(False, "--json", help="Output in
|
|
230
236
|
if not rows:
|
231
237
|
print("No streams mapped.")
|
232
238
|
return
|
233
|
-
|
239
|
+
# Prepare human-friendly display (ETH/GLM + USD)
|
240
|
+
ZERO = "0x0000000000000000000000000000000000000000"
|
241
|
+
# Cache prices so we don't query per-row
|
242
|
+
price_cache: dict[str, Optional[Decimal]] = {"ETH": None, "GLM": None}
|
243
|
+
# Determine which symbols are present
|
244
|
+
symbols_present = set()
|
234
245
|
for r in rows:
|
235
246
|
if "error" in r:
|
236
|
-
|
237
|
-
|
238
|
-
|
247
|
+
continue
|
248
|
+
token_addr = (r.get("token") or "").lower()
|
249
|
+
sym = "ETH" if token_addr == ZERO.lower() else "GLM"
|
250
|
+
symbols_present.add(sym)
|
251
|
+
if "ETH" in symbols_present:
|
252
|
+
price_cache["ETH"] = fetch_eth_usd_price()
|
253
|
+
if "GLM" in symbols_present:
|
254
|
+
price_cache["GLM"] = fetch_glm_usd_price()
|
255
|
+
|
256
|
+
# Build table rows
|
257
|
+
table_rows = []
|
258
|
+
for r in rows:
|
259
|
+
if "error" in r:
|
260
|
+
table_rows.append([r["vm_id"], str(r["stream_id"]), "—", "ERROR", r.get("error", ""), "—"])
|
261
|
+
continue
|
262
|
+
token_addr = (r.get("token") or "").lower()
|
263
|
+
sym = "ETH" if token_addr == ZERO.lower() else "GLM"
|
264
|
+
withdrawable_eth = Decimal(str(Web3.from_wei(int(r["withdrawable"]), "ether")))
|
265
|
+
withdrawable_str = f"{withdrawable_eth:.6f} {sym}"
|
266
|
+
price = price_cache.get(sym)
|
267
|
+
usd_str = "—"
|
268
|
+
if price is not None:
|
269
|
+
try:
|
270
|
+
usd_val = (withdrawable_eth * price).quantize(Decimal("0.01"))
|
271
|
+
usd_str = f"${usd_val}"
|
272
|
+
except Exception:
|
273
|
+
usd_str = "—"
|
274
|
+
table_rows.append([
|
275
|
+
r["vm_id"],
|
276
|
+
str(r["stream_id"]),
|
277
|
+
f"{int(r['remaining'])}s",
|
278
|
+
"yes" if r["verified"] else "no",
|
279
|
+
withdrawable_str,
|
280
|
+
usd_str,
|
281
|
+
])
|
282
|
+
|
283
|
+
headers = ["VM", "Stream", "Remaining", "Verified", "Withdrawable", "USD"]
|
284
|
+
# Compute column widths
|
285
|
+
cols = len(headers)
|
286
|
+
col_widths = [len(h) for h in headers]
|
287
|
+
for row in table_rows:
|
288
|
+
for i in range(cols):
|
289
|
+
col_widths[i] = max(col_widths[i], len(str(row[i])))
|
290
|
+
|
291
|
+
def fmt_row(values: list[str]) -> str:
|
292
|
+
return " ".join(str(values[i]).ljust(col_widths[i]) for i in range(cols))
|
293
|
+
|
294
|
+
print("\nStreams")
|
295
|
+
print(fmt_row(headers))
|
296
|
+
print(" ".join("-" * w for w in col_widths))
|
297
|
+
for row in table_rows:
|
298
|
+
print(fmt_row(row))
|
239
299
|
except Exception as e:
|
240
300
|
print(f"Error: {e}")
|
241
301
|
raise typer.Exit(code=1)
|
@@ -247,6 +307,9 @@ def streams_show(vm_id: str = typer.Argument(..., help="VM id (requestor_name)")
|
|
247
307
|
from .container import Container
|
248
308
|
from .config import settings
|
249
309
|
from .payments.blockchain_service import StreamPaymentReader
|
310
|
+
from .utils.pricing import fetch_glm_usd_price, fetch_eth_usd_price
|
311
|
+
from decimal import Decimal
|
312
|
+
from web3 import Web3
|
250
313
|
import json as _json
|
251
314
|
try:
|
252
315
|
c = Container()
|
@@ -279,7 +342,189 @@ def streams_show(vm_id: str = typer.Argument(..., help="VM id (requestor_name)")
|
|
279
342
|
if json_out:
|
280
343
|
print(_json.dumps(out, indent=2))
|
281
344
|
else:
|
282
|
-
|
345
|
+
ZERO = "0x0000000000000000000000000000000000000000"
|
346
|
+
token_addr = (s.get("token") or "").lower()
|
347
|
+
sym = "ETH" if token_addr == ZERO.lower() else "GLM"
|
348
|
+
nat = Decimal(str(Web3.from_wei(int(withdrawable), "ether")))
|
349
|
+
price = fetch_eth_usd_price() if sym == "ETH" else fetch_glm_usd_price()
|
350
|
+
usd_str = "—"
|
351
|
+
if price is not None:
|
352
|
+
try:
|
353
|
+
usd_val = (nat * price).quantize(Decimal("0.01"))
|
354
|
+
usd_str = f"${usd_val}"
|
355
|
+
except Exception:
|
356
|
+
usd_str = "—"
|
357
|
+
def _fmt_seconds(sec: int) -> str:
|
358
|
+
m, s2 = divmod(int(sec), 60)
|
359
|
+
h, m = divmod(m, 60)
|
360
|
+
d, h = divmod(h, 24)
|
361
|
+
parts = []
|
362
|
+
if d: parts.append(f"{d}d")
|
363
|
+
if h: parts.append(f"{h}h")
|
364
|
+
if m and not d: parts.append(f"{m}m")
|
365
|
+
if s2 and not d and not h and not m: parts.append(f"{s2}s")
|
366
|
+
return " ".join(parts) or "0s"
|
367
|
+
# Pretty single-record display
|
368
|
+
print("\nStream Details")
|
369
|
+
headers = ["VM", "Stream", "Remaining", "Verified", "Withdrawable", "USD"]
|
370
|
+
cols = [
|
371
|
+
vm_id,
|
372
|
+
str(sid),
|
373
|
+
_fmt_seconds(remaining),
|
374
|
+
"yes" if ok else "no",
|
375
|
+
f"{nat:.6f} {sym}",
|
376
|
+
usd_str,
|
377
|
+
]
|
378
|
+
w = [max(len(headers[i]), len(str(cols[i]))) for i in range(len(headers))]
|
379
|
+
print(" ".join(headers[i].ljust(w[i]) for i in range(len(w))))
|
380
|
+
print(" ".join("-" * wi for wi in w))
|
381
|
+
print(" ".join(str(cols[i]).ljust(w[i]) for i in range(len(w))))
|
382
|
+
except Exception as e:
|
383
|
+
print(f"Error: {e}")
|
384
|
+
raise typer.Exit(code=1)
|
385
|
+
|
386
|
+
@streams_app.command("earnings")
|
387
|
+
def streams_earnings(json_out: bool = typer.Option(False, "--json", help="Output in JSON")):
|
388
|
+
"""Summarize provider earnings: vested, withdrawn, and withdrawable totals."""
|
389
|
+
from .container import Container
|
390
|
+
from .config import settings
|
391
|
+
from .payments.blockchain_service import StreamPaymentReader
|
392
|
+
from .utils.pricing import fetch_glm_usd_price, fetch_eth_usd_price
|
393
|
+
from decimal import Decimal
|
394
|
+
from web3 import Web3
|
395
|
+
import json as _json
|
396
|
+
try:
|
397
|
+
c = Container()
|
398
|
+
c.config.from_pydantic(settings)
|
399
|
+
stream_map = c.stream_map()
|
400
|
+
reader = StreamPaymentReader(settings.POLYGON_RPC_URL, settings.STREAM_PAYMENT_ADDRESS)
|
401
|
+
items = asyncio.run(stream_map.all_items())
|
402
|
+
now = int(reader.web3.eth.get_block("latest")["timestamp"]) if items else 0
|
403
|
+
rows = []
|
404
|
+
total_vested = 0
|
405
|
+
total_withdrawn = 0
|
406
|
+
total_withdrawable = 0
|
407
|
+
ZERO = "0x0000000000000000000000000000000000000000"
|
408
|
+
sums_native: dict[str, Decimal] = {"ETH": Decimal("0"), "GLM": Decimal("0")}
|
409
|
+
for vm_id, stream_id in items.items():
|
410
|
+
try:
|
411
|
+
s = reader.get_stream(int(stream_id))
|
412
|
+
vested = max(min(now, int(s["stopTime"])) - int(s["startTime"]), 0) * int(s["ratePerSecond"]) # type: ignore
|
413
|
+
withdrawable = max(int(vested) - int(s["withdrawn"]), 0)
|
414
|
+
total_vested += int(vested)
|
415
|
+
total_withdrawn += int(s["withdrawn"]) # type: ignore
|
416
|
+
total_withdrawable += int(withdrawable)
|
417
|
+
sym = "ETH" if (s.get("token") or "").lower() == ZERO.lower() else "GLM"
|
418
|
+
sums_native[sym] += Decimal(str(Web3.from_wei(int(withdrawable), "ether")))
|
419
|
+
rows.append({
|
420
|
+
"vm_id": vm_id,
|
421
|
+
"stream_id": int(stream_id),
|
422
|
+
"token": str(s.get("token")),
|
423
|
+
"vested": int(vested),
|
424
|
+
"withdrawn": int(s["withdrawn"]),
|
425
|
+
"withdrawable": int(withdrawable),
|
426
|
+
})
|
427
|
+
except Exception as e:
|
428
|
+
rows.append({"vm_id": vm_id, "stream_id": int(stream_id), "error": str(e)})
|
429
|
+
out = {
|
430
|
+
"streams": rows,
|
431
|
+
"totals": {
|
432
|
+
"vested": int(total_vested),
|
433
|
+
"withdrawn": int(total_withdrawn),
|
434
|
+
"withdrawable": int(total_withdrawable),
|
435
|
+
}
|
436
|
+
}
|
437
|
+
if json_out:
|
438
|
+
print(_json.dumps(out, indent=2))
|
439
|
+
return
|
440
|
+
# Human summary by token with USD
|
441
|
+
price_eth = fetch_eth_usd_price()
|
442
|
+
price_glm = fetch_glm_usd_price()
|
443
|
+
def _fmt_usd(amount_native: Decimal, price: Optional[Decimal]) -> str:
|
444
|
+
if price is None:
|
445
|
+
return "—"
|
446
|
+
try:
|
447
|
+
return f"${(amount_native * price).quantize(Decimal('0.01'))}"
|
448
|
+
except Exception:
|
449
|
+
return "—"
|
450
|
+
print("\nEarnings Summary")
|
451
|
+
headers = ["Token", "Withdrawable", "USD"]
|
452
|
+
data_rows = [
|
453
|
+
["ETH", f"{sums_native['ETH']:.6f} ETH", _fmt_usd(sums_native["ETH"], price_eth)],
|
454
|
+
["GLM", f"{sums_native['GLM']:.6f} GLM", _fmt_usd(sums_native["GLM"], price_glm)],
|
455
|
+
]
|
456
|
+
# Table widths
|
457
|
+
w = [len(h) for h in headers]
|
458
|
+
for r in data_rows:
|
459
|
+
for i in range(3):
|
460
|
+
w[i] = max(w[i], len(str(r[i])))
|
461
|
+
print(" ".join(headers[i].ljust(w[i]) for i in range(3)))
|
462
|
+
print(" ".join("-" * wi for wi in w))
|
463
|
+
for r in data_rows:
|
464
|
+
print(" ".join(str(r[i]).ljust(w[i]) for i in range(3)))
|
465
|
+
# Per stream table
|
466
|
+
if rows:
|
467
|
+
table = []
|
468
|
+
for r in rows:
|
469
|
+
if "error" in r:
|
470
|
+
table.append([r["vm_id"], str(r["stream_id"]), "ERROR", r.get("error", "")])
|
471
|
+
continue
|
472
|
+
sym = "ETH" if (r.get("token") or "").lower() == ZERO.lower() else "GLM"
|
473
|
+
nat = Decimal(str(Web3.from_wei(int(r["withdrawable"]), "ether")))
|
474
|
+
price = price_eth if sym == "ETH" else price_glm
|
475
|
+
usd = _fmt_usd(nat, price)
|
476
|
+
table.append([r["vm_id"], str(r["stream_id"]), f"{nat:.6f} {sym}", usd])
|
477
|
+
h2 = ["VM", "Stream", "Withdrawable", "USD"]
|
478
|
+
w2 = [len(x) for x in h2]
|
479
|
+
for row in table:
|
480
|
+
for i in range(4):
|
481
|
+
w2[i] = max(w2[i], len(str(row[i])))
|
482
|
+
print("\nPer Stream")
|
483
|
+
print(" ".join(h2[i].ljust(w2[i]) for i in range(4)))
|
484
|
+
print(" ".join("-" * wi for wi in w2))
|
485
|
+
for row in table:
|
486
|
+
print(" ".join(str(row[i]).ljust(w2[i]) for i in range(4)))
|
487
|
+
except Exception as e:
|
488
|
+
print(f"Error: {e}")
|
489
|
+
raise typer.Exit(code=1)
|
490
|
+
|
491
|
+
|
492
|
+
@streams_app.command("withdraw")
|
493
|
+
def streams_withdraw(
|
494
|
+
vm_id: str = typer.Option(None, "--vm-id", help="Withdraw for a single VM id"),
|
495
|
+
all_streams: bool = typer.Option(False, "--all", help="Withdraw for all mapped streams"),
|
496
|
+
):
|
497
|
+
"""Withdraw vested funds for one or all streams."""
|
498
|
+
from .container import Container
|
499
|
+
from .config import settings
|
500
|
+
try:
|
501
|
+
if not vm_id and not all_streams:
|
502
|
+
print("Specify --vm-id or --all")
|
503
|
+
raise typer.Exit(code=1)
|
504
|
+
c = Container()
|
505
|
+
c.config.from_pydantic(settings)
|
506
|
+
stream_map = c.stream_map()
|
507
|
+
client = c.stream_client()
|
508
|
+
targets = []
|
509
|
+
if all_streams:
|
510
|
+
items = asyncio.run(stream_map.all_items())
|
511
|
+
for vid, sid in items.items():
|
512
|
+
targets.append((vid, int(sid)))
|
513
|
+
else:
|
514
|
+
sid = asyncio.run(stream_map.get(vm_id))
|
515
|
+
if sid is None:
|
516
|
+
print("No stream mapped for this VM.")
|
517
|
+
raise typer.Exit(code=1)
|
518
|
+
targets.append((vm_id, int(sid)))
|
519
|
+
results = []
|
520
|
+
for vid, sid in targets:
|
521
|
+
try:
|
522
|
+
tx = client.withdraw(int(sid))
|
523
|
+
results.append((vid, sid, tx))
|
524
|
+
print(f"Withdrew stream {sid} for VM {vid}: tx={tx}")
|
525
|
+
except Exception as e:
|
526
|
+
print(f"Failed to withdraw stream {sid} for VM {vid}: {e}")
|
527
|
+
# no JSON aggregation here; use earnings for structured output
|
283
528
|
except Exception as e:
|
284
529
|
print(f"Error: {e}")
|
285
530
|
raise typer.Exit(code=1)
|
@@ -331,6 +576,80 @@ def _write_env_vars(path: str, updates: dict):
|
|
331
576
|
with open(path, "w") as f:
|
332
577
|
f.writelines(out)
|
333
578
|
|
579
|
+
|
580
|
+
@config_app.command("withdraw")
|
581
|
+
def config_withdraw(
|
582
|
+
enable: bool = typer.Option(None, "--enable", help="Enable/disable auto-withdraw (true/false)"),
|
583
|
+
interval: int = typer.Option(None, "--interval", help="Withdraw interval in seconds (e.g., 1800)"),
|
584
|
+
min_wei: int = typer.Option(None, "--min-wei", help="Only withdraw when >= this wei amount"),
|
585
|
+
dev: bool = typer.Option(False, "--dev", help="Write to .env.dev instead of .env"),
|
586
|
+
):
|
587
|
+
"""Configure provider auto-withdraw settings and persist to .env(.dev)."""
|
588
|
+
from .config import settings
|
589
|
+
env_path = _env_path_for(dev)
|
590
|
+
updates = {}
|
591
|
+
if enable is not None:
|
592
|
+
updates["GOLEM_PROVIDER_STREAM_WITHDRAW_ENABLED"] = str(enable).lower()
|
593
|
+
settings.STREAM_WITHDRAW_ENABLED = bool(enable)
|
594
|
+
if interval is not None:
|
595
|
+
if interval < 0:
|
596
|
+
raise typer.BadParameter("--interval must be >= 0")
|
597
|
+
updates["GOLEM_PROVIDER_STREAM_WITHDRAW_INTERVAL_SECONDS"] = int(interval)
|
598
|
+
try:
|
599
|
+
settings.STREAM_WITHDRAW_INTERVAL_SECONDS = int(interval)
|
600
|
+
except Exception:
|
601
|
+
pass
|
602
|
+
if min_wei is not None:
|
603
|
+
if min_wei < 0:
|
604
|
+
raise typer.BadParameter("--min-wei must be >= 0")
|
605
|
+
updates["GOLEM_PROVIDER_STREAM_MIN_WITHDRAW_WEI"] = int(min_wei)
|
606
|
+
try:
|
607
|
+
settings.STREAM_MIN_WITHDRAW_WEI = int(min_wei)
|
608
|
+
except Exception:
|
609
|
+
pass
|
610
|
+
if not updates:
|
611
|
+
print("No changes (use --enable/--interval/--min-wei)")
|
612
|
+
raise typer.Exit(code=0)
|
613
|
+
_write_env_vars(env_path, updates)
|
614
|
+
print(f"Updated withdraw settings in {env_path}")
|
615
|
+
|
616
|
+
|
617
|
+
@config_app.command("monitor")
|
618
|
+
def config_monitor(
|
619
|
+
enable: bool = typer.Option(None, "--enable", help="Enable/disable stream monitor (true/false)"),
|
620
|
+
interval: int = typer.Option(None, "--interval", help="Monitor interval in seconds (e.g., 30)"),
|
621
|
+
min_remaining: int = typer.Option(None, "--min-remaining", help="Minimum remaining runway to keep VM running (seconds)"),
|
622
|
+
dev: bool = typer.Option(False, "--dev", help="Write to .env.dev instead of .env"),
|
623
|
+
):
|
624
|
+
"""Configure provider stream monitor and persist to .env(.dev)."""
|
625
|
+
from .config import settings
|
626
|
+
env_path = _env_path_for(dev)
|
627
|
+
updates = {}
|
628
|
+
if enable is not None:
|
629
|
+
updates["GOLEM_PROVIDER_STREAM_MONITOR_ENABLED"] = str(enable).lower()
|
630
|
+
settings.STREAM_MONITOR_ENABLED = bool(enable)
|
631
|
+
if interval is not None:
|
632
|
+
if interval < 0:
|
633
|
+
raise typer.BadParameter("--interval must be >= 0")
|
634
|
+
updates["GOLEM_PROVIDER_STREAM_MONITOR_INTERVAL_SECONDS"] = int(interval)
|
635
|
+
try:
|
636
|
+
settings.STREAM_MONITOR_INTERVAL_SECONDS = int(interval)
|
637
|
+
except Exception:
|
638
|
+
pass
|
639
|
+
if min_remaining is not None:
|
640
|
+
if min_remaining < 0:
|
641
|
+
raise typer.BadParameter("--min-remaining must be >= 0")
|
642
|
+
updates["GOLEM_PROVIDER_STREAM_MIN_REMAINING_SECONDS"] = int(min_remaining)
|
643
|
+
try:
|
644
|
+
settings.STREAM_MIN_REMAINING_SECONDS = int(min_remaining)
|
645
|
+
except Exception:
|
646
|
+
pass
|
647
|
+
if not updates:
|
648
|
+
print("No changes (use --enable/--interval/--min-remaining)")
|
649
|
+
raise typer.Exit(code=0)
|
650
|
+
_write_env_vars(env_path, updates)
|
651
|
+
print(f"Updated monitor settings in {env_path}")
|
652
|
+
|
334
653
|
def _print_pricing_examples(glm_usd):
|
335
654
|
from decimal import Decimal
|
336
655
|
from .utils.pricing import calculate_monthly_cost, calculate_monthly_cost_usd
|
@@ -47,6 +47,14 @@ def fetch_glm_usd_price() -> Optional[Decimal]:
|
|
47
47
|
return _coingecko_simple_price(settings.COINGECKO_IDS)
|
48
48
|
|
49
49
|
|
50
|
+
def fetch_eth_usd_price() -> Optional[Decimal]:
|
51
|
+
"""Fetch the current ETH price in USD from CoinGecko.
|
52
|
+
|
53
|
+
Uses the canonical "ethereum" id.
|
54
|
+
"""
|
55
|
+
return _coingecko_simple_price("ethereum")
|
56
|
+
|
57
|
+
|
50
58
|
def usd_to_glm(usd_amount: Decimal, glm_usd: Decimal) -> Decimal:
|
51
59
|
if glm_usd <= 0:
|
52
60
|
raise ValueError("Invalid GLM/USD price")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "golem-vm-provider"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.52"
|
4
4
|
description = "VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network"
|
5
5
|
authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
|
6
6
|
readme = "README.md"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/golem_base_advertiser.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/golem_base_utils.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/multi_advertiser.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/resource_monitor.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/discovery/resource_tracker.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.50 → golem_vm_provider-0.1.52}/provider/payments/blockchain_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|