golem-vm-provider 0.1.50__tar.gz → 0.1.51__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.
Files changed (43) hide show
  1. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/PKG-INFO +29 -1
  2. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/README.md +28 -0
  3. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/main.py +178 -0
  4. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/pyproject.toml +1 -1
  5. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/__init__.py +0 -0
  6. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/api/__init__.py +0 -0
  7. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/api/models.py +0 -0
  8. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/api/routes.py +0 -0
  9. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/config.py +0 -0
  10. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/container.py +0 -0
  11. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/data/deployments/l2.json +0 -0
  12. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/__init__.py +0 -0
  13. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/advertiser.py +0 -0
  14. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/golem_base_advertiser.py +0 -0
  15. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/golem_base_utils.py +0 -0
  16. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/multi_advertiser.py +0 -0
  17. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/resource_monitor.py +0 -0
  18. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/resource_tracker.py +0 -0
  19. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/discovery/service.py +0 -0
  20. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/network/port_verifier.py +0 -0
  21. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/payments/blockchain_service.py +0 -0
  22. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/payments/monitor.py +0 -0
  23. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/payments/stream_map.py +0 -0
  24. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/security/ethereum.py +0 -0
  25. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/security/faucet.py +0 -0
  26. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/service.py +0 -0
  27. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/__init__.py +0 -0
  28. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/ascii_art.py +0 -0
  29. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/logging.py +0 -0
  30. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/port_display.py +0 -0
  31. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/pricing.py +0 -0
  32. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/retry.py +0 -0
  33. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/utils/setup.py +0 -0
  34. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/__init__.py +0 -0
  35. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/cloud_init.py +0 -0
  36. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/models.py +0 -0
  37. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/multipass.py +0 -0
  38. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/multipass_adapter.py +0 -0
  39. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/name_mapper.py +0 -0
  40. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/port_manager.py +0 -0
  41. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/provider.py +0 -0
  42. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/provider/vm/proxy_manager.py +0 -0
  43. {golem_vm_provider-0.1.50 → golem_vm_provider-0.1.51}/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.50
3
+ Version: 0.1.51
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():
@@ -284,6 +286,108 @@ def streams_show(vm_id: str = typer.Argument(..., help="VM id (requestor_name)")
284
286
  print(f"Error: {e}")
285
287
  raise typer.Exit(code=1)
286
288
 
289
+ @streams_app.command("earnings")
290
+ def streams_earnings(json_out: bool = typer.Option(False, "--json", help="Output in JSON")):
291
+ """Summarize provider earnings: vested, withdrawn, and withdrawable totals."""
292
+ from .container import Container
293
+ from .config import settings
294
+ from .payments.blockchain_service import StreamPaymentReader
295
+ import json as _json
296
+ try:
297
+ c = Container()
298
+ c.config.from_pydantic(settings)
299
+ stream_map = c.stream_map()
300
+ reader = StreamPaymentReader(settings.POLYGON_RPC_URL, settings.STREAM_PAYMENT_ADDRESS)
301
+ items = asyncio.run(stream_map.all_items())
302
+ now = int(reader.web3.eth.get_block("latest")["timestamp"]) if items else 0
303
+ rows = []
304
+ total_vested = 0
305
+ total_withdrawn = 0
306
+ total_withdrawable = 0
307
+ for vm_id, stream_id in items.items():
308
+ try:
309
+ s = reader.get_stream(int(stream_id))
310
+ vested = max(min(now, int(s["stopTime"])) - int(s["startTime"]), 0) * int(s["ratePerSecond"]) # type: ignore
311
+ withdrawable = max(int(vested) - int(s["withdrawn"]), 0)
312
+ total_vested += int(vested)
313
+ total_withdrawn += int(s["withdrawn"]) # type: ignore
314
+ total_withdrawable += int(withdrawable)
315
+ rows.append({
316
+ "vm_id": vm_id,
317
+ "stream_id": int(stream_id),
318
+ "vested": int(vested),
319
+ "withdrawn": int(s["withdrawn"]),
320
+ "withdrawable": int(withdrawable),
321
+ })
322
+ except Exception as e:
323
+ rows.append({"vm_id": vm_id, "stream_id": int(stream_id), "error": str(e)})
324
+ out = {
325
+ "streams": rows,
326
+ "totals": {
327
+ "vested": int(total_vested),
328
+ "withdrawn": int(total_withdrawn),
329
+ "withdrawable": int(total_withdrawable),
330
+ }
331
+ }
332
+ if json_out:
333
+ print(_json.dumps(out, indent=2))
334
+ return
335
+ print("\nEarnings summary (wei):")
336
+ print(f" Vested total : {total_vested}")
337
+ print(f" Withdrawn total : {total_withdrawn}")
338
+ print(f" Withdrawable total: {total_withdrawable}")
339
+ if rows:
340
+ print("\nPer-stream:")
341
+ for r in rows:
342
+ if "error" in r:
343
+ print(f"- {r['vm_id']} [{r['stream_id']}] ERROR: {r['error']}")
344
+ else:
345
+ print(f"- {r['vm_id']} [{r['stream_id']}]: vested={r['vested']} withdrawn={r['withdrawn']} withdrawable={r['withdrawable']}")
346
+ except Exception as e:
347
+ print(f"Error: {e}")
348
+ raise typer.Exit(code=1)
349
+
350
+
351
+ @streams_app.command("withdraw")
352
+ def streams_withdraw(
353
+ vm_id: str = typer.Option(None, "--vm-id", help="Withdraw for a single VM id"),
354
+ all_streams: bool = typer.Option(False, "--all", help="Withdraw for all mapped streams"),
355
+ ):
356
+ """Withdraw vested funds for one or all streams."""
357
+ from .container import Container
358
+ from .config import settings
359
+ try:
360
+ if not vm_id and not all_streams:
361
+ print("Specify --vm-id or --all")
362
+ raise typer.Exit(code=1)
363
+ c = Container()
364
+ c.config.from_pydantic(settings)
365
+ stream_map = c.stream_map()
366
+ client = c.stream_client()
367
+ targets = []
368
+ if all_streams:
369
+ items = asyncio.run(stream_map.all_items())
370
+ for vid, sid in items.items():
371
+ targets.append((vid, int(sid)))
372
+ else:
373
+ sid = asyncio.run(stream_map.get(vm_id))
374
+ if sid is None:
375
+ print("No stream mapped for this VM.")
376
+ raise typer.Exit(code=1)
377
+ targets.append((vm_id, int(sid)))
378
+ results = []
379
+ for vid, sid in targets:
380
+ try:
381
+ tx = client.withdraw(int(sid))
382
+ results.append((vid, sid, tx))
383
+ print(f"Withdrew stream {sid} for VM {vid}: tx={tx}")
384
+ except Exception as e:
385
+ print(f"Failed to withdraw stream {sid} for VM {vid}: {e}")
386
+ # no JSON aggregation here; use earnings for structured output
387
+ except Exception as e:
388
+ print(f"Error: {e}")
389
+ raise typer.Exit(code=1)
390
+
287
391
  @cli.command()
288
392
  def start(
289
393
  no_verify_port: bool = typer.Option(False, "--no-verify-port", help="Skip provider port verification."),
@@ -331,6 +435,80 @@ def _write_env_vars(path: str, updates: dict):
331
435
  with open(path, "w") as f:
332
436
  f.writelines(out)
333
437
 
438
+
439
+ @config_app.command("withdraw")
440
+ def config_withdraw(
441
+ enable: bool = typer.Option(None, "--enable", help="Enable/disable auto-withdraw (true/false)"),
442
+ interval: int = typer.Option(None, "--interval", help="Withdraw interval in seconds (e.g., 1800)"),
443
+ min_wei: int = typer.Option(None, "--min-wei", help="Only withdraw when >= this wei amount"),
444
+ dev: bool = typer.Option(False, "--dev", help="Write to .env.dev instead of .env"),
445
+ ):
446
+ """Configure provider auto-withdraw settings and persist to .env(.dev)."""
447
+ from .config import settings
448
+ env_path = _env_path_for(dev)
449
+ updates = {}
450
+ if enable is not None:
451
+ updates["GOLEM_PROVIDER_STREAM_WITHDRAW_ENABLED"] = str(enable).lower()
452
+ settings.STREAM_WITHDRAW_ENABLED = bool(enable)
453
+ if interval is not None:
454
+ if interval < 0:
455
+ raise typer.BadParameter("--interval must be >= 0")
456
+ updates["GOLEM_PROVIDER_STREAM_WITHDRAW_INTERVAL_SECONDS"] = int(interval)
457
+ try:
458
+ settings.STREAM_WITHDRAW_INTERVAL_SECONDS = int(interval)
459
+ except Exception:
460
+ pass
461
+ if min_wei is not None:
462
+ if min_wei < 0:
463
+ raise typer.BadParameter("--min-wei must be >= 0")
464
+ updates["GOLEM_PROVIDER_STREAM_MIN_WITHDRAW_WEI"] = int(min_wei)
465
+ try:
466
+ settings.STREAM_MIN_WITHDRAW_WEI = int(min_wei)
467
+ except Exception:
468
+ pass
469
+ if not updates:
470
+ print("No changes (use --enable/--interval/--min-wei)")
471
+ raise typer.Exit(code=0)
472
+ _write_env_vars(env_path, updates)
473
+ print(f"Updated withdraw settings in {env_path}")
474
+
475
+
476
+ @config_app.command("monitor")
477
+ def config_monitor(
478
+ enable: bool = typer.Option(None, "--enable", help="Enable/disable stream monitor (true/false)"),
479
+ interval: int = typer.Option(None, "--interval", help="Monitor interval in seconds (e.g., 30)"),
480
+ min_remaining: int = typer.Option(None, "--min-remaining", help="Minimum remaining runway to keep VM running (seconds)"),
481
+ dev: bool = typer.Option(False, "--dev", help="Write to .env.dev instead of .env"),
482
+ ):
483
+ """Configure provider stream monitor and persist to .env(.dev)."""
484
+ from .config import settings
485
+ env_path = _env_path_for(dev)
486
+ updates = {}
487
+ if enable is not None:
488
+ updates["GOLEM_PROVIDER_STREAM_MONITOR_ENABLED"] = str(enable).lower()
489
+ settings.STREAM_MONITOR_ENABLED = bool(enable)
490
+ if interval is not None:
491
+ if interval < 0:
492
+ raise typer.BadParameter("--interval must be >= 0")
493
+ updates["GOLEM_PROVIDER_STREAM_MONITOR_INTERVAL_SECONDS"] = int(interval)
494
+ try:
495
+ settings.STREAM_MONITOR_INTERVAL_SECONDS = int(interval)
496
+ except Exception:
497
+ pass
498
+ if min_remaining is not None:
499
+ if min_remaining < 0:
500
+ raise typer.BadParameter("--min-remaining must be >= 0")
501
+ updates["GOLEM_PROVIDER_STREAM_MIN_REMAINING_SECONDS"] = int(min_remaining)
502
+ try:
503
+ settings.STREAM_MIN_REMAINING_SECONDS = int(min_remaining)
504
+ except Exception:
505
+ pass
506
+ if not updates:
507
+ print("No changes (use --enable/--interval/--min-remaining)")
508
+ raise typer.Exit(code=0)
509
+ _write_env_vars(env_path, updates)
510
+ print(f"Updated monitor settings in {env_path}")
511
+
334
512
  def _print_pricing_examples(glm_usd):
335
513
  from decimal import Decimal
336
514
  from .utils.pricing import calculate_monthly_cost, calculate_monthly_cost_usd
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "golem-vm-provider"
3
- version = "0.1.50"
3
+ version = "0.1.51"
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"