request-vm-on-golem 0.1.55__tar.gz → 0.1.57__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 (28) hide show
  1. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/PKG-INFO +4 -4
  2. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/README.md +3 -3
  3. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/pyproject.toml +1 -1
  4. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/config.py +30 -3
  5. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/run.py +9 -3
  6. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/services/provider_service.py +11 -2
  7. request_vm_on_golem-0.1.57/requestor/settings.py +49 -0
  8. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/__init__.py +0 -0
  9. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/api/main.py +0 -0
  10. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/cli/__init__.py +0 -0
  11. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/cli/commands.py +0 -0
  12. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/data/deployments/l2.json +0 -0
  13. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/db/__init__.py +0 -0
  14. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/db/sqlite.py +0 -0
  15. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/errors.py +0 -0
  16. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/payments/blockchain_service.py +0 -0
  17. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/payments/monitor.py +0 -0
  18. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/provider/__init__.py +0 -0
  19. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/provider/client.py +0 -0
  20. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/security/faucet.py +0 -0
  21. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/services/__init__.py +0 -0
  22. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/services/database_service.py +0 -0
  23. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/services/ssh_service.py +0 -0
  24. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/services/vm_service.py +0 -0
  25. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/ssh/__init__.py +0 -0
  26. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/ssh/manager.py +0 -0
  27. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/utils/logging.py +0 -0
  28. {request_vm_on_golem-0.1.55 → request_vm_on_golem-0.1.57}/requestor/utils/spinner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: request-vm-on-golem
3
- Version: 0.1.55
3
+ Version: 0.1.57
4
4
  Summary: VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network
5
5
  Keywords: golem,vm,cloud,decentralized,cli
6
6
  Author: Phillip Jensen
@@ -296,12 +296,12 @@ Then, run any `golem` command. For example: `golem vm providers`
296
296
  Alternatively, you can prepend the environment variables directly to the command:
297
297
 
298
298
  ```bash
299
- GOLEM_REQUESTOR_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true" poetry run golem vm providers
299
+ GOLEM_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true" poetry run golem vm providers
300
300
  ```
301
301
 
302
302
  ### Mode vs. Network
303
303
 
304
- - Development Mode (`GOLEM_REQUESTOR_ENVIRONMENT=development`)
304
+ - Development Mode (`GOLEM_ENVIRONMENT=development`)
305
305
  - Improves local workflows: prefixes central discovery URL with `DEVMODE-` and, when using the central driver, maps provider IPs to `localhost` for easier testing.
306
306
  - Does not determine chain selection.
307
307
 
@@ -428,7 +428,7 @@ export GOLEM_REQUESTOR_SSH_KEY_DIR="/path/to/keys"
428
428
  export GOLEM_REQUESTOR_DB_PATH="/path/to/database.db"
429
429
 
430
430
  # Environment Mode (defaults to "production")
431
- export GOLEM_REQUESTOR_ENVIRONMENT="development" # Optional: Switch to development mode
431
+ export GOLEM_ENVIRONMENT="development" # Optional: Switch to development mode
432
432
  export GOLEM_REQUESTOR_FORCE_LOCALHOST="true" # Optional: Force localhost in development mode
433
433
  export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; optional filter for listing/creation
434
434
  ```
@@ -255,12 +255,12 @@ Then, run any `golem` command. For example: `golem vm providers`
255
255
  Alternatively, you can prepend the environment variables directly to the command:
256
256
 
257
257
  ```bash
258
- GOLEM_REQUESTOR_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true" poetry run golem vm providers
258
+ GOLEM_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true" poetry run golem vm providers
259
259
  ```
260
260
 
261
261
  ### Mode vs. Network
262
262
 
263
- - Development Mode (`GOLEM_REQUESTOR_ENVIRONMENT=development`)
263
+ - Development Mode (`GOLEM_ENVIRONMENT=development`)
264
264
  - Improves local workflows: prefixes central discovery URL with `DEVMODE-` and, when using the central driver, maps provider IPs to `localhost` for easier testing.
265
265
  - Does not determine chain selection.
266
266
 
@@ -387,7 +387,7 @@ export GOLEM_REQUESTOR_SSH_KEY_DIR="/path/to/keys"
387
387
  export GOLEM_REQUESTOR_DB_PATH="/path/to/database.db"
388
388
 
389
389
  # Environment Mode (defaults to "production")
390
- export GOLEM_REQUESTOR_ENVIRONMENT="development" # Optional: Switch to development mode
390
+ export GOLEM_ENVIRONMENT="development" # Optional: Switch to development mode
391
391
  export GOLEM_REQUESTOR_FORCE_LOCALHOST="true" # Optional: Force localhost in development mode
392
392
  export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; optional filter for listing/creation
393
393
  ```
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "request-vm-on-golem"
3
- version = "0.1.55"
3
+ version = "0.1.57"
4
4
  description = "VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network"
5
5
  authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
6
6
  readme = "README.md"
@@ -22,7 +22,7 @@ def ensure_config() -> None:
22
22
  created = True
23
23
 
24
24
  if not env_file.exists():
25
- env_file.write_text("GOLEM_REQUESTOR_ENVIRONMENT=production\n")
25
+ env_file.write_text("GOLEM_ENVIRONMENT=production\n")
26
26
  created = True
27
27
 
28
28
  private_key = ssh_dir / "id_rsa"
@@ -91,6 +91,29 @@ class RequestorConfig(BaseSettings):
91
91
  return f"DEVMODE-{v}"
92
92
  return v
93
93
 
94
+ @field_validator("environment", mode="before")
95
+ @classmethod
96
+ def prefer_global_env(cls, v: str) -> str:
97
+ """Prefer unified GOLEM_ENVIRONMENT when provided; fallback to service-specific env."""
98
+ ge = os.environ.get("GOLEM_ENVIRONMENT")
99
+ if ge:
100
+ return ge
101
+ return v
102
+
103
+ @field_validator("network", mode="before")
104
+ @classmethod
105
+ def default_network_for_dev(cls, v: str, info: ValidationInfo) -> str:
106
+ """Use 'testnet' by default when in development environment unless explicitly set."""
107
+ if v:
108
+ return v
109
+ try:
110
+ env = info.data.get("environment")
111
+ except Exception:
112
+ env = os.environ.get("GOLEM_ENVIRONMENT")
113
+ if (env or "").lower() == "development":
114
+ return "testnet"
115
+ return v or "mainnet"
116
+
94
117
  # Golem Base Settings
95
118
  golem_base_rpc_url: str = Field(
96
119
  default="https://ethwarsaw.holesky.golemdb.io/rpc",
@@ -101,8 +124,8 @@ class RequestorConfig(BaseSettings):
101
124
  description="Golem Base WebSocket URL"
102
125
  )
103
126
  advertisement_interval: int = Field(
104
- default=240,
105
- description="Advertisement interval in seconds (should match provider)"
127
+ default=3600,
128
+ description="Advertisement interval in seconds (should match provider's GOLEM_BASE_ADVERTISEMENT_INTERVAL)"
106
129
  )
107
130
  ethereum_private_key: str = Field(
108
131
  default="0x0000000000000000000000000000000000000000000000000000000000000001",
@@ -323,6 +346,10 @@ class RequestorConfig(BaseSettings):
323
346
  # Allow overriding to dev mode with golem_dev_mode
324
347
  if os.environ.get('golem_dev_mode', 'false').lower() in ('true', '1', 't'):
325
348
  kwargs['environment'] = "development"
349
+ # Fallback: if requestor env not provided but provider env signals development, mirror it
350
+ if not kwargs.get('environment') and os.environ.get('GOLEM_ENVIRONMENT'):
351
+ # Mirror unified GOLEM_ENVIRONMENT into requestor environment if not explicitly provided
352
+ kwargs['environment'] = os.environ.get('GOLEM_ENVIRONMENT')
326
353
 
327
354
  # Set dependent paths before validation
328
355
  if 'ssh_key_dir' not in kwargs:
@@ -48,9 +48,15 @@ def check_requirements() -> bool:
48
48
  def main():
49
49
  """Run the requestor CLI."""
50
50
  try:
51
- # Load environment variables from .env.dev file if it exists, otherwise use .env
52
- dev_env_path = Path(__file__).parent.parent / '.env.dev'
53
- env_path = dev_env_path if dev_env_path.exists() else Path(__file__).parent.parent / '.env'
51
+ # Load environment variables based on unified environment
52
+ env_mode = (os.environ.get('GOLEM_ENVIRONMENT') or os.environ.get('GOLEM_REQUESTOR_ENVIRONMENT') or '').lower()
53
+ base_dir = Path(__file__).parent.parent
54
+ env_file = '.env.dev' if env_mode == 'development' else '.env'
55
+ env_path = base_dir / env_file
56
+ # If chosen file does not exist, fallback to the other
57
+ if not env_path.exists():
58
+ alt = base_dir / ('.env' if env_file == '.env.dev' else '.env.dev')
59
+ env_path = alt if alt.exists() else env_path
54
60
  load_dotenv(dotenv_path=env_path)
55
61
  logger.info(f"Loading environment variables from: {env_path}")
56
62
 
@@ -272,13 +272,22 @@ class ProviderService:
272
272
  )
273
273
 
274
274
  async def _format_block_timestamp(self, block_number: int) -> str:
275
- """Format a block number into a human-readable 'time ago' string."""
275
+ """Format a block number into a human-readable 'time ago' string.
276
+
277
+ The Golem Base chain uses ~2 seconds per block.
278
+ We also guard against negative diffs that can occur when adverts are
279
+ extended before expiry (expires_at increases), which would otherwise
280
+ make the derived "created_at" appear in the future.
281
+ """
276
282
  if not self.golem_base_client:
277
283
  return "N/A"
278
284
  try:
279
285
  latest_block = await self.golem_base_client.http_client().eth.get_block('latest')
280
286
  block_diff = latest_block.number - block_number
281
- seconds_ago = block_diff * 2 # Approximate block time
287
+ if block_diff < 0:
288
+ block_diff = 0
289
+ # Approximate time: ~2s per block
290
+ seconds_ago = block_diff * 2
282
291
 
283
292
  if seconds_ago < 60:
284
293
  return f"{int(seconds_ago)}s ago"
@@ -0,0 +1,49 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Literal, TypedDict
4
+
5
+ from .config import config
6
+
7
+
8
+ class _Prefs(TypedDict, total=False):
9
+ price_display: Literal["fiat", "native"]
10
+
11
+
12
+ _PREFS_FILE = config.base_dir / "settings.json"
13
+
14
+
15
+ def _read_prefs() -> _Prefs:
16
+ try:
17
+ if _PREFS_FILE.exists():
18
+ data = json.loads(_PREFS_FILE.read_text())
19
+ if isinstance(data, dict):
20
+ return data # type: ignore[return-value]
21
+ except Exception:
22
+ pass
23
+ return {}
24
+
25
+
26
+ def _write_prefs(p: _Prefs) -> None:
27
+ try:
28
+ _PREFS_FILE.parent.mkdir(parents=True, exist_ok=True)
29
+ tmp = Path(str(_PREFS_FILE) + ".tmp")
30
+ tmp.write_text(json.dumps(p, indent=2))
31
+ tmp.replace(_PREFS_FILE)
32
+ except Exception:
33
+ # Best-effort persistence; ignore write errors
34
+ pass
35
+
36
+
37
+ def get_price_display() -> Literal["fiat", "native"]:
38
+ prefs = _read_prefs()
39
+ mode = prefs.get("price_display")
40
+ if mode in ("fiat", "native"):
41
+ return mode
42
+ return "fiat"
43
+
44
+
45
+ def set_price_display(mode: Literal["fiat", "native"]) -> None:
46
+ prefs = _read_prefs()
47
+ prefs["price_display"] = mode
48
+ _write_prefs(prefs)
49
+