request-vm-on-golem 0.1.51__tar.gz → 0.1.53__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 (27) hide show
  1. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/PKG-INFO +65 -36
  2. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/README.md +64 -35
  3. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/pyproject.toml +1 -1
  4. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/cli/commands.py +52 -9
  5. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/config.py +112 -16
  6. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/security/faucet.py +5 -2
  7. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/services/provider_service.py +16 -3
  8. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/__init__.py +0 -0
  9. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/api/main.py +0 -0
  10. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/cli/__init__.py +0 -0
  11. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/data/deployments/l2.json +0 -0
  12. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/db/__init__.py +0 -0
  13. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/db/sqlite.py +0 -0
  14. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/errors.py +0 -0
  15. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/payments/blockchain_service.py +0 -0
  16. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/payments/monitor.py +0 -0
  17. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/provider/__init__.py +0 -0
  18. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/provider/client.py +0 -0
  19. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/run.py +0 -0
  20. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/services/__init__.py +0 -0
  21. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/services/database_service.py +0 -0
  22. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/services/ssh_service.py +0 -0
  23. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/services/vm_service.py +0 -0
  24. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/ssh/__init__.py +0 -0
  25. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/ssh/manager.py +0 -0
  26. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/requestor/utils/logging.py +0 -0
  27. {request_vm_on_golem-0.1.51 → request_vm_on_golem-0.1.53}/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.51
3
+ Version: 0.1.53
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
@@ -41,7 +41,46 @@ Description-Content-Type: text/markdown
41
41
 
42
42
  # VM on Golem Requestor
43
43
 
44
- A sophisticated command-line interface for managing virtual machines on the Golem Network. The requestor works in tandem with provider nodes to create and manage VMs with secure SSH access.
44
+ Rent compute on demand like Airbnb for servers. The `golem` CLI helps you discover providers, fund pay‑as‑you‑go streams, launch VMs, and connect via SSH.
45
+
46
+ ## Quick Start (Rent a VM)
47
+
48
+ 1) Install:
49
+
50
+ ```bash
51
+ pip install request-vm-on-golem
52
+ ```
53
+
54
+ 2) Find providers (testnet by default):
55
+
56
+ ```bash
57
+ golem vm providers
58
+ ```
59
+
60
+ 3) Create a VM (auto‑opens a payment stream if needed):
61
+
62
+ ```bash
63
+ golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20
64
+ ```
65
+
66
+ 4) SSH in:
67
+
68
+ ```bash
69
+ golem vm ssh my-vm
70
+ ```
71
+
72
+ Check your installed version and whether an update is available:
73
+
74
+ ```bash
75
+ golem version
76
+ ```
77
+
78
+ 5) Stop or destroy when done:
79
+
80
+ ```bash
81
+ golem vm stop my-vm
82
+ golem vm destroy my-vm
83
+ ```
45
84
 
46
85
  ## Architecture Overview
47
86
 
@@ -141,7 +180,7 @@ CLI helpers
141
180
  - Open a stream for a planned VM (computes rate from provider pricing):
142
181
 
143
182
  ```bash
144
- poetry run golem vm stream open \
183
+ golem vm stream open \
145
184
  --provider-id 0xProvider \
146
185
  --cpu 2 --memory 4 --storage 20 \
147
186
  --hours 1
@@ -152,39 +191,39 @@ poetry run golem vm stream open \
152
191
 
153
192
  ```bash
154
193
  # Add 3 hours at prior rate
155
- poetry run golem vm stream topup --stream-id 123 --hours 3
194
+ golem vm stream topup --stream-id 123 --hours 3
156
195
 
157
196
  # Or specify exact GLM amount
158
- poetry run golem vm stream topup --stream-id 123 --glm 25.0
197
+ golem vm stream topup --stream-id 123 --glm 25.0
159
198
  ```
160
199
 
161
200
  - Check stream status via provider (by VM name recorded in your DB):
162
201
 
163
202
  ```bash
164
- poetry run golem vm stream status my-vm
203
+ golem vm stream status my-vm
165
204
  # add --json for machine-readable output
166
205
  ```
167
206
 
168
207
  - Inspect a stream directly on-chain:
169
208
 
170
209
  ```bash
171
- poetry run golem vm stream inspect --stream-id 123
210
+ golem vm stream inspect --stream-id 123
172
211
  ```
173
212
 
174
213
  - Stopping or destroying a VM ends the stream:
175
214
 
176
215
  ```bash
177
216
  # Stop VM and terminate payment stream (best-effort)
178
- poetry run golem vm stop my-vm
217
+ golem vm stop my-vm
179
218
 
180
219
  # Destroy VM and terminate stream
181
- poetry run golem vm destroy my-vm
220
+ golem vm destroy my-vm
182
221
  ```
183
222
 
184
223
  - Create a VM and attach an existing stream (no auto-streams are created by the requestor):
185
224
 
186
225
  ```bash
187
- poetry run golem vm create my-vm \
226
+ golem vm create my-vm \
188
227
  --provider-id 0xProvider \
189
228
  --cpu 2 --memory 4 --storage 20 \
190
229
  --stream-id 123
@@ -192,7 +231,8 @@ poetry run golem vm create my-vm \
192
231
 
193
232
  Environment (env prefix `GOLEM_REQUESTOR_`):
194
233
 
195
- - `polygon_rpc_url` — EVM RPC URL (default L2 RPC)
234
+ - `payments_network` — Payments network profile (defaults to `l2.holesky`). Profiles provide RPC + faucet defaults.
235
+ - `polygon_rpc_url` — EVM RPC URL (defaults from `payments_network` profile; can be overridden)
196
236
  - `stream_payment_address` — StreamPayment address (defaults from `contracts/deployments/l2.json`; overridden by provider info)
197
237
  - `glm_token_address` — Token address (defaults from `contracts/deployments/l2.json`; zero address means native ETH)
198
238
  - Optional override of deployments directory: set `GOLEM_DEPLOYMENTS_DIR` to a folder containing `l2.json`.
@@ -210,16 +250,16 @@ Monitoring and auto top-up:
210
250
  - The requestor API runs a background monitor that keeps each running VM’s stream funded with at least 1 hour runway (configurable). It checks every 30s and tops up to the target runway.
211
251
  - Configure via env (prefix `GOLEM_REQUESTOR_`): `stream_monitor_enabled` (default true), `stream_monitor_interval_seconds` (default 30), `stream_min_remaining_seconds` (default 3600), `stream_topup_target_seconds` (default 3600).
212
252
 
213
- ## Faucet (L2 ETH)
253
+ ## Faucet (Testnet only)
214
254
 
215
255
  - Request L2 test ETH to cover stream transactions:
216
256
 
217
257
  ```bash
218
- poetry run golem wallet faucet
258
+ golem wallet faucet
219
259
  ```
220
260
 
221
261
  - Defaults:
222
- - Faucet: `https://l2.holesky.golemdb.io/faucet`
262
+ - Faucet URL and enablement come from the active `payments_network` profile. On `mainnet` (or other profiles without faucet) the command is disabled.
223
263
  - CAPTCHA: `https://cap.gobas.me/05381a2cef5e`
224
264
  - Override with env: `GOLEM_REQUESTOR_l2_faucet_url`, `GOLEM_REQUESTOR_captcha_url`, `GOLEM_REQUESTOR_captcha_api_key`.
225
265
 
@@ -227,7 +267,7 @@ poetry run golem wallet faucet
227
267
 
228
268
  ```bash
229
269
  # Install using pip
230
- pip install golem-vm-requestor
270
+ pip install request-vm-on-golem
231
271
 
232
272
  # Or install from source
233
273
  git clone https://github.com/golem/vm-on-golem.git
@@ -249,11 +289,7 @@ First, source the development environment variables:
249
289
  source .env.dev
250
290
  ```
251
291
 
252
- Then, run any `golem` command. For example:
253
-
254
- ```bash
255
- poetry run golem vm providers
256
- ```
292
+ Then, run any `golem` command. For example: `golem vm providers`
257
293
 
258
294
  ### Prepending variables
259
295
 
@@ -270,19 +306,15 @@ GOLEM_REQUESTOR_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true"
270
306
  - Does not determine chain selection.
271
307
 
272
308
  - Network Selection (`--network` or `GOLEM_REQUESTOR_NETWORK`)
273
- - Filters Golem Base discovery results by `golem_network=testnet|mainnet`.
274
- - Combine with the appropriate RPC envs (`GOLEM_REQUESTOR_GOLEM_BASE_RPC_URL`, `GOLEM_REQUESTOR_GOLEM_BASE_WS_URL`) and any contract addresses.
275
- - Independent from dev ergonomics.
309
+ - Filters results by `testnet|mainnet`. Defaults are sensible; most users don’t need to change anything.
310
+
311
+ - Payments Network (`GOLEM_REQUESTOR_PAYMENTS_NETWORK`)
312
+ - Selects the payments chain profile (e.g., `l2.holesky`, `mainnet`) used for streaming payments; sets default RPC and faucet behavior.
313
+ - Provider discovery filters by this payments network via `vm providers` unless `--all-payments` is supplied. Override payments filter with `--payments-network <name>`.
276
314
 
277
315
  Examples:
278
- - List providers on mainnet without changing env:
279
- ```bash
280
- poetry run golem vm providers --network mainnet
281
- ```
282
- - Create a VM while targeting testnet:
283
- ```bash
284
- poetry run golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20 --network testnet
285
- ```
316
+ - List providers on mainnet without changing env: `golem vm providers --network mainnet`
317
+ - Create a VM while targeting testnet: `golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20 --network testnet`
286
318
 
287
319
  ## Usage
288
320
 
@@ -388,9 +420,6 @@ The requestor uses a hierarchical configuration system:
388
420
  1. Environment Variables:
389
421
 
390
422
  ```bash
391
- # Discovery Service
392
- export GOLEM_REQUESTOR_DISCOVERY_URL="http://discovery.golem.network:9001"
393
-
394
423
  # Base Directory (default: ~/.golem)
395
424
  export GOLEM_REQUESTOR_BASE_DIR="/path/to/golem/dir"
396
425
 
@@ -401,7 +430,7 @@ export GOLEM_REQUESTOR_DB_PATH="/path/to/database.db"
401
430
  # Environment Mode (defaults to "production")
402
431
  export GOLEM_REQUESTOR_ENVIRONMENT="development" # Optional: Switch to development mode
403
432
  export GOLEM_REQUESTOR_FORCE_LOCALHOST="true" # Optional: Force localhost in development mode
404
- export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; filters Golem Base results by annotation
433
+ export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; optional filter for listing/creation
405
434
  ```
406
435
 
407
436
  2. Directory Structure:
@@ -438,7 +467,7 @@ Local state is maintained in SQLite:
438
467
 
439
468
  The requestor communicates with providers through:
440
469
 
441
- 1. Discovery service for provider location
470
+ 1. Network discovery (uses sane defaults; no setup required for most users)
442
471
  2. Direct API calls for VM management
443
472
  3. SSH proxy system for secure access
444
473
  4. Resource tracking for capacity management
@@ -1,6 +1,45 @@
1
1
  # VM on Golem Requestor
2
2
 
3
- A sophisticated command-line interface for managing virtual machines on the Golem Network. The requestor works in tandem with provider nodes to create and manage VMs with secure SSH access.
3
+ Rent compute on demand like Airbnb for servers. The `golem` CLI helps you discover providers, fund pay‑as‑you‑go streams, launch VMs, and connect via SSH.
4
+
5
+ ## Quick Start (Rent a VM)
6
+
7
+ 1) Install:
8
+
9
+ ```bash
10
+ pip install request-vm-on-golem
11
+ ```
12
+
13
+ 2) Find providers (testnet by default):
14
+
15
+ ```bash
16
+ golem vm providers
17
+ ```
18
+
19
+ 3) Create a VM (auto‑opens a payment stream if needed):
20
+
21
+ ```bash
22
+ golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20
23
+ ```
24
+
25
+ 4) SSH in:
26
+
27
+ ```bash
28
+ golem vm ssh my-vm
29
+ ```
30
+
31
+ Check your installed version and whether an update is available:
32
+
33
+ ```bash
34
+ golem version
35
+ ```
36
+
37
+ 5) Stop or destroy when done:
38
+
39
+ ```bash
40
+ golem vm stop my-vm
41
+ golem vm destroy my-vm
42
+ ```
4
43
 
5
44
  ## Architecture Overview
6
45
 
@@ -100,7 +139,7 @@ CLI helpers
100
139
  - Open a stream for a planned VM (computes rate from provider pricing):
101
140
 
102
141
  ```bash
103
- poetry run golem vm stream open \
142
+ golem vm stream open \
104
143
  --provider-id 0xProvider \
105
144
  --cpu 2 --memory 4 --storage 20 \
106
145
  --hours 1
@@ -111,39 +150,39 @@ poetry run golem vm stream open \
111
150
 
112
151
  ```bash
113
152
  # Add 3 hours at prior rate
114
- poetry run golem vm stream topup --stream-id 123 --hours 3
153
+ golem vm stream topup --stream-id 123 --hours 3
115
154
 
116
155
  # Or specify exact GLM amount
117
- poetry run golem vm stream topup --stream-id 123 --glm 25.0
156
+ golem vm stream topup --stream-id 123 --glm 25.0
118
157
  ```
119
158
 
120
159
  - Check stream status via provider (by VM name recorded in your DB):
121
160
 
122
161
  ```bash
123
- poetry run golem vm stream status my-vm
162
+ golem vm stream status my-vm
124
163
  # add --json for machine-readable output
125
164
  ```
126
165
 
127
166
  - Inspect a stream directly on-chain:
128
167
 
129
168
  ```bash
130
- poetry run golem vm stream inspect --stream-id 123
169
+ golem vm stream inspect --stream-id 123
131
170
  ```
132
171
 
133
172
  - Stopping or destroying a VM ends the stream:
134
173
 
135
174
  ```bash
136
175
  # Stop VM and terminate payment stream (best-effort)
137
- poetry run golem vm stop my-vm
176
+ golem vm stop my-vm
138
177
 
139
178
  # Destroy VM and terminate stream
140
- poetry run golem vm destroy my-vm
179
+ golem vm destroy my-vm
141
180
  ```
142
181
 
143
182
  - Create a VM and attach an existing stream (no auto-streams are created by the requestor):
144
183
 
145
184
  ```bash
146
- poetry run golem vm create my-vm \
185
+ golem vm create my-vm \
147
186
  --provider-id 0xProvider \
148
187
  --cpu 2 --memory 4 --storage 20 \
149
188
  --stream-id 123
@@ -151,7 +190,8 @@ poetry run golem vm create my-vm \
151
190
 
152
191
  Environment (env prefix `GOLEM_REQUESTOR_`):
153
192
 
154
- - `polygon_rpc_url` — EVM RPC URL (default L2 RPC)
193
+ - `payments_network` — Payments network profile (defaults to `l2.holesky`). Profiles provide RPC + faucet defaults.
194
+ - `polygon_rpc_url` — EVM RPC URL (defaults from `payments_network` profile; can be overridden)
155
195
  - `stream_payment_address` — StreamPayment address (defaults from `contracts/deployments/l2.json`; overridden by provider info)
156
196
  - `glm_token_address` — Token address (defaults from `contracts/deployments/l2.json`; zero address means native ETH)
157
197
  - Optional override of deployments directory: set `GOLEM_DEPLOYMENTS_DIR` to a folder containing `l2.json`.
@@ -169,16 +209,16 @@ Monitoring and auto top-up:
169
209
  - The requestor API runs a background monitor that keeps each running VM’s stream funded with at least 1 hour runway (configurable). It checks every 30s and tops up to the target runway.
170
210
  - Configure via env (prefix `GOLEM_REQUESTOR_`): `stream_monitor_enabled` (default true), `stream_monitor_interval_seconds` (default 30), `stream_min_remaining_seconds` (default 3600), `stream_topup_target_seconds` (default 3600).
171
211
 
172
- ## Faucet (L2 ETH)
212
+ ## Faucet (Testnet only)
173
213
 
174
214
  - Request L2 test ETH to cover stream transactions:
175
215
 
176
216
  ```bash
177
- poetry run golem wallet faucet
217
+ golem wallet faucet
178
218
  ```
179
219
 
180
220
  - Defaults:
181
- - Faucet: `https://l2.holesky.golemdb.io/faucet`
221
+ - Faucet URL and enablement come from the active `payments_network` profile. On `mainnet` (or other profiles without faucet) the command is disabled.
182
222
  - CAPTCHA: `https://cap.gobas.me/05381a2cef5e`
183
223
  - Override with env: `GOLEM_REQUESTOR_l2_faucet_url`, `GOLEM_REQUESTOR_captcha_url`, `GOLEM_REQUESTOR_captcha_api_key`.
184
224
 
@@ -186,7 +226,7 @@ poetry run golem wallet faucet
186
226
 
187
227
  ```bash
188
228
  # Install using pip
189
- pip install golem-vm-requestor
229
+ pip install request-vm-on-golem
190
230
 
191
231
  # Or install from source
192
232
  git clone https://github.com/golem/vm-on-golem.git
@@ -208,11 +248,7 @@ First, source the development environment variables:
208
248
  source .env.dev
209
249
  ```
210
250
 
211
- Then, run any `golem` command. For example:
212
-
213
- ```bash
214
- poetry run golem vm providers
215
- ```
251
+ Then, run any `golem` command. For example: `golem vm providers`
216
252
 
217
253
  ### Prepending variables
218
254
 
@@ -229,19 +265,15 @@ GOLEM_REQUESTOR_ENVIRONMENT="development" GOLEM_REQUESTOR_FORCE_LOCALHOST="true"
229
265
  - Does not determine chain selection.
230
266
 
231
267
  - Network Selection (`--network` or `GOLEM_REQUESTOR_NETWORK`)
232
- - Filters Golem Base discovery results by `golem_network=testnet|mainnet`.
233
- - Combine with the appropriate RPC envs (`GOLEM_REQUESTOR_GOLEM_BASE_RPC_URL`, `GOLEM_REQUESTOR_GOLEM_BASE_WS_URL`) and any contract addresses.
234
- - Independent from dev ergonomics.
268
+ - Filters results by `testnet|mainnet`. Defaults are sensible; most users don’t need to change anything.
269
+
270
+ - Payments Network (`GOLEM_REQUESTOR_PAYMENTS_NETWORK`)
271
+ - Selects the payments chain profile (e.g., `l2.holesky`, `mainnet`) used for streaming payments; sets default RPC and faucet behavior.
272
+ - Provider discovery filters by this payments network via `vm providers` unless `--all-payments` is supplied. Override payments filter with `--payments-network <name>`.
235
273
 
236
274
  Examples:
237
- - List providers on mainnet without changing env:
238
- ```bash
239
- poetry run golem vm providers --network mainnet
240
- ```
241
- - Create a VM while targeting testnet:
242
- ```bash
243
- poetry run golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20 --network testnet
244
- ```
275
+ - List providers on mainnet without changing env: `golem vm providers --network mainnet`
276
+ - Create a VM while targeting testnet: `golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20 --network testnet`
245
277
 
246
278
  ## Usage
247
279
 
@@ -347,9 +379,6 @@ The requestor uses a hierarchical configuration system:
347
379
  1. Environment Variables:
348
380
 
349
381
  ```bash
350
- # Discovery Service
351
- export GOLEM_REQUESTOR_DISCOVERY_URL="http://discovery.golem.network:9001"
352
-
353
382
  # Base Directory (default: ~/.golem)
354
383
  export GOLEM_REQUESTOR_BASE_DIR="/path/to/golem/dir"
355
384
 
@@ -360,7 +389,7 @@ export GOLEM_REQUESTOR_DB_PATH="/path/to/database.db"
360
389
  # Environment Mode (defaults to "production")
361
390
  export GOLEM_REQUESTOR_ENVIRONMENT="development" # Optional: Switch to development mode
362
391
  export GOLEM_REQUESTOR_FORCE_LOCALHOST="true" # Optional: Force localhost in development mode
363
- export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; filters Golem Base results by annotation
392
+ export GOLEM_REQUESTOR_NETWORK="testnet" # Or "mainnet"; optional filter for listing/creation
364
393
  ```
365
394
 
366
395
  2. Directory Structure:
@@ -397,7 +426,7 @@ Local state is maintained in SQLite:
397
426
 
398
427
  The requestor communicates with providers through:
399
428
 
400
- 1. Discovery service for provider location
429
+ 1. Network discovery (uses sane defaults; no setup required for most users)
401
430
  2. Direct API calls for VM management
402
431
  3. SSH proxy system for secure access
403
432
  4. Resource tracking for capacity management
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "request-vm-on-golem"
3
- version = "0.1.51"
3
+ version = "0.1.53"
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"
@@ -5,6 +5,7 @@ import json
5
5
  from typing import Optional
6
6
  from pathlib import Path
7
7
  import subprocess
8
+ import os
8
9
  import aiohttp
9
10
  from tabulate import tabulate
10
11
  import uvicorn
@@ -89,6 +90,33 @@ def cli(network: str | None):
89
90
  pass
90
91
 
91
92
 
93
+ @cli.command(name="version")
94
+ def version_cmd():
95
+ """Show installed and latest versions from PyPI."""
96
+ pkg = "request-vm-on-golem"
97
+ try:
98
+ current = metadata.version(pkg)
99
+ except Exception:
100
+ current = "unknown"
101
+ latest = None
102
+ # Avoid network during pytest
103
+ if not os.environ.get("PYTEST_CURRENT_TEST"):
104
+ try:
105
+ import json as _json
106
+ from urllib.request import urlopen
107
+ with urlopen(f"https://pypi.org/pypi/{pkg}/json", timeout=5) as resp:
108
+ data = _json.loads(resp.read().decode("utf-8"))
109
+ latest = data.get("info", {}).get("version")
110
+ except Exception:
111
+ latest = None
112
+
113
+ if latest and latest != current:
114
+ click.echo(f"Requestor CLI: {current} (update available: {latest})")
115
+ click.echo("Update: pip install -U request-vm-on-golem")
116
+ else:
117
+ click.echo(f"Requestor CLI: {current} (up-to-date)" if latest else f"Requestor CLI: {current}")
118
+
119
+
92
120
  @cli.group()
93
121
  def vm():
94
122
  """VM management commands"""
@@ -101,11 +129,13 @@ def vm():
101
129
  @click.option('--storage', type=int, help='Minimum disk (GB) required')
102
130
  @click.option('--country', help='Preferred provider country')
103
131
  @click.option('--driver', type=click.Choice(['central', 'golem-base']), default=None, help='Discovery driver to use')
132
+ @click.option('--payments-network', type=str, default=None, help='Filter by payments network profile (default: current config)')
133
+ @click.option('--all-payments', is_flag=True, help='Do not filter by payments network (show all)')
104
134
  @click.option('--json', 'as_json', is_flag=True, help='Output in JSON format')
105
135
  @click.option('--network', type=click.Choice(['testnet', 'mainnet']), default=None,
106
136
  help='Override network filter for this command')
107
137
  @async_command
108
- async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str], as_json: bool, network: Optional[str] = None):
138
+ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str], payments_network: Optional[str] = None, all_payments: bool = False, as_json: bool = False, network: Optional[str] = None):
109
139
  """List available providers matching requirements."""
110
140
  try:
111
141
  if network:
@@ -124,7 +154,8 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
124
154
 
125
155
  # Determine the discovery driver being used
126
156
  discovery_driver = driver or config.discovery_driver
127
- logger.process(f"Querying discovery service via {discovery_driver} (network={config.network})")
157
+ eff_pn = payments_network if payments_network is not None else getattr(config, 'payments_network', None)
158
+ logger.process(f"Querying discovery via {discovery_driver} (network={config.network}, payments={eff_pn if not all_payments else 'ALL'})")
128
159
 
129
160
  # Initialize provider service
130
161
  provider_service = ProviderService()
@@ -132,13 +163,21 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
132
163
  # If a full spec is provided, enable per-provider estimate display
133
164
  if cpu and memory and storage:
134
165
  provider_service.estimate_spec = (cpu, memory, storage)
135
- providers = await provider_service.find_providers(
136
- cpu=cpu,
137
- memory=memory,
138
- storage=storage,
139
- country=country,
140
- driver=driver
141
- )
166
+ try:
167
+ providers = await provider_service.find_providers(
168
+ cpu=cpu,
169
+ memory=memory,
170
+ storage=storage,
171
+ country=country,
172
+ driver=driver,
173
+ payments_network=eff_pn,
174
+ include_all_payments=bool(all_payments),
175
+ )
176
+ except TypeError:
177
+ # Backward compatibility with older/dummy service stubs in tests
178
+ providers = await provider_service.find_providers(
179
+ cpu=cpu, memory=memory, storage=storage, country=country, driver=driver
180
+ )
142
181
 
143
182
  if not providers:
144
183
  logger.warning("No providers found matching criteria")
@@ -606,6 +645,10 @@ def wallet():
606
645
  async def wallet_faucet():
607
646
  """Request L2 faucet funds for the requestor's payment address."""
608
647
  try:
648
+ if not getattr(config, 'faucet_enabled', False):
649
+ logger.warning("Faucet is disabled for the current payments network.")
650
+ click.echo(json.dumps({"error": "faucet_disabled", "network": getattr(config, 'payments_network', None)}, indent=2))
651
+ return
609
652
  from ..security.faucet import L2FaucetService
610
653
  from eth_account import Account
611
654
  acct = Account.from_key(config.ethereum_private_key)
@@ -54,6 +54,13 @@ class RequestorConfig(BaseSettings):
54
54
  description="Target network: 'testnet' or 'mainnet'"
55
55
  )
56
56
 
57
+ # Payments chain selection (modular network profiles)
58
+ # Keep current standard as l2.holesky
59
+ payments_network: str = Field(
60
+ default="l2.holesky",
61
+ description="Payments network profile (e.g., 'l2.holesky', 'kaolin.holesky', 'mainnet')"
62
+ )
63
+
57
64
  # Development Settings
58
65
  force_localhost: bool = Field(
59
66
  default=False,
@@ -102,8 +109,8 @@ class RequestorConfig(BaseSettings):
102
109
 
103
110
  # Payments (EVM RPC)
104
111
  polygon_rpc_url: str = Field(
105
- default="https://l2.holesky.golemdb.io/rpc",
106
- description="EVM RPC URL for streaming payments (L2 by default)"
112
+ default="",
113
+ description="EVM RPC URL for streaming payments; defaults from payments_network profile"
107
114
  )
108
115
  stream_payment_address: str = Field(
109
116
  default="",
@@ -130,10 +137,10 @@ class RequestorConfig(BaseSettings):
130
137
  default=3600,
131
138
  description="Target runway after top-up (seconds)"
132
139
  )
133
- # Faucet settings (L2 payments)
140
+ # Faucet settings (payments)
134
141
  l2_faucet_url: str = Field(
135
- default="https://l2.holesky.golemdb.io/faucet",
136
- description="L2 faucet base URL (no trailing /api)"
142
+ default="",
143
+ description="Faucet base URL (no trailing /api). Only used on testnets. Defaults from payments_network profile"
137
144
  )
138
145
  captcha_url: str = Field(
139
146
  default="https://cap.gobas.me",
@@ -150,8 +157,8 @@ class RequestorConfig(BaseSettings):
150
157
 
151
158
  @field_validator("polygon_rpc_url", mode='before')
152
159
  @classmethod
153
- def prefer_alt_env(cls, v: str) -> str:
154
- # Accept alt aliases
160
+ def prefer_alt_env(cls, v: str, info: ValidationInfo) -> str:
161
+ # Accept alt aliases overriding the profile
155
162
  for key in (
156
163
  "GOLEM_REQUESTOR_l2_rpc_url",
157
164
  "GOLEM_REQUESTOR_L2_RPC_URL",
@@ -160,22 +167,45 @@ class RequestorConfig(BaseSettings):
160
167
  ):
161
168
  if os.environ.get(key):
162
169
  return os.environ[key]
163
- return v
170
+ if v:
171
+ return v
172
+ # Default from payments profile
173
+ pn = info.data.get("payments_network") or "l2.holesky"
174
+ return RequestorConfig._profile_defaults(pn)["rpc_url"]
175
+
176
+ @field_validator("l2_faucet_url", mode='before')
177
+ @classmethod
178
+ def default_faucet_env(cls, v: str, info: ValidationInfo) -> str:
179
+ for key in (
180
+ "GOLEM_REQUESTOR_l2_faucet_url",
181
+ "GOLEM_REQUESTOR_L2_FAUCET_URL",
182
+ ):
183
+ if os.environ.get(key):
184
+ return os.environ[key]
185
+ if v:
186
+ return v
187
+ pn = info.data.get("payments_network") or "l2.holesky"
188
+ return RequestorConfig._profile_defaults(pn).get("faucet_url", "")
164
189
 
165
190
  @staticmethod
166
- def _load_l2_deployment() -> tuple[str | None, str | None]:
191
+ def _load_deployment(network: str) -> tuple[str | None, str | None]:
167
192
  try:
168
193
  base = os.environ.get("GOLEM_DEPLOYMENTS_DIR")
169
194
  if base:
170
- path = Path(base) / "l2.json"
195
+ path = Path(base) / f"{RequestorConfig._deployment_basename(network)}.json"
171
196
  else:
172
197
  # repo root assumption: ../../ relative to this file
173
- path = Path(__file__).resolve().parents[2] / "contracts" / "deployments" / "l2.json"
198
+ path = (
199
+ Path(__file__).resolve().parents[2]
200
+ / "contracts" / "deployments" / f"{RequestorConfig._deployment_basename(network)}.json"
201
+ )
174
202
  if not path.exists():
175
203
  # Try package resource fallback
176
204
  try:
177
205
  import importlib.resources as ir
178
- with ir.files("requestor.data.deployments").joinpath("l2.json").open("r") as fh: # type: ignore[attr-defined]
206
+ with ir.files("requestor.data.deployments").joinpath(
207
+ f"{RequestorConfig._deployment_basename(network)}.json"
208
+ ).open("r") as fh: # type: ignore[attr-defined]
179
209
  import json as _json
180
210
  data = _json.load(fh)
181
211
  except Exception:
@@ -192,22 +222,83 @@ class RequestorConfig(BaseSettings):
192
222
  pass
193
223
  return None, None
194
224
 
225
+ @staticmethod
226
+ def _deployment_basename(network: str) -> str:
227
+ # Map well-known network aliases to deployment file base names
228
+ n = (network or "").lower()
229
+ if n in ("l2", "l2.holesky"): # current standard
230
+ return "l2"
231
+ if "." in n:
232
+ return n.split(".")[0]
233
+ return n or "l2"
234
+
235
+ @staticmethod
236
+ def _profile_defaults(network: str) -> Dict[str, str]:
237
+ n = (network or "l2.holesky").lower()
238
+ # Built-in profiles; extend easily in future
239
+ profiles = {
240
+ "l2.holesky": {
241
+ "rpc_url": "https://l2.holesky.golemdb.io/rpc",
242
+ "faucet_url": "https://l2.holesky.golemdb.io/faucet",
243
+ "faucet_enabled": True,
244
+ "token_symbol": "GLM",
245
+ "gas_symbol": "ETH",
246
+ },
247
+ # Example: mainnet has no faucet by default
248
+ "mainnet": {
249
+ "rpc_url": "",
250
+ "faucet_url": "",
251
+ "faucet_enabled": False,
252
+ "token_symbol": "GLM",
253
+ "gas_symbol": "ETH",
254
+ },
255
+ }
256
+ return profiles.get(n, profiles["l2.holesky"]) # default to current standard
257
+
195
258
  @field_validator("stream_payment_address", mode='before')
196
259
  @classmethod
197
- def default_stream_addr(cls, v: str) -> str:
260
+ def default_stream_addr(cls, v: str, info: ValidationInfo) -> str:
198
261
  if v:
199
262
  return v
200
- addr, _ = RequestorConfig._load_l2_deployment()
263
+ network = info.data.get("payments_network") or "l2.holesky"
264
+ addr, _ = RequestorConfig._load_deployment(network)
201
265
  return addr or "0x0000000000000000000000000000000000000000"
202
266
 
203
267
  @field_validator("glm_token_address", mode='before')
204
268
  @classmethod
205
- def default_token_addr(cls, v: str) -> str:
269
+ def default_token_addr(cls, v: str, info: ValidationInfo) -> str:
206
270
  if v:
207
271
  return v
208
- _, token = RequestorConfig._load_l2_deployment()
272
+ network = info.data.get("payments_network") or "l2.holesky"
273
+ _, token = RequestorConfig._load_deployment(network)
209
274
  return token or "0x0000000000000000000000000000000000000000"
210
275
 
276
+ # Optional convenience: expose token and gas symbols based on profile
277
+ token_symbol: str = Field(
278
+ default="",
279
+ description="Human-friendly symbol of payment token (e.g., GLM)"
280
+ )
281
+ gas_token_symbol: str = Field(
282
+ default="",
283
+ description="Symbol of gas token for the chain (e.g., ETH)"
284
+ )
285
+
286
+ @field_validator("token_symbol", mode="before")
287
+ @classmethod
288
+ def default_token_symbol(cls, v: str, info: ValidationInfo) -> str:
289
+ if v:
290
+ return v
291
+ pn = info.data.get("payments_network") or "l2.holesky"
292
+ return RequestorConfig._profile_defaults(pn).get("token_symbol", "")
293
+
294
+ @field_validator("gas_token_symbol", mode="before")
295
+ @classmethod
296
+ def default_gas_symbol(cls, v: str, info: ValidationInfo) -> str:
297
+ if v:
298
+ return v
299
+ pn = info.data.get("payments_network") or "l2.holesky"
300
+ return RequestorConfig._profile_defaults(pn).get("gas_symbol", "")
301
+
211
302
  # Base Directory
212
303
  base_dir: Path = Field(
213
304
  default_factory=lambda: Path.home() / ".golem" / "requestor",
@@ -240,6 +331,11 @@ class RequestorConfig(BaseSettings):
240
331
  kwargs['db_path'] = base_dir / "vms.db"
241
332
  super().__init__(**kwargs)
242
333
 
334
+ @property
335
+ def faucet_enabled(self) -> bool:
336
+ """Whether requesting funds from faucet is allowed for current payments network."""
337
+ return bool(self._profile_defaults(self.payments_network).get("faucet_enabled", False))
338
+
243
339
  def get_provider_url(self, ip_address: str) -> str:
244
340
  """Get provider API URL.
245
341
 
@@ -15,7 +15,7 @@ class L2FaucetService:
15
15
  self.cfg = config
16
16
  self.web3 = Web3(Web3.HTTPProvider(config.polygon_rpc_url))
17
17
  self.client = PowFaucetClient(
18
- faucet_url=getattr(config, 'l2_faucet_url', 'https://l2.holesky.golemdb.io/faucet'),
18
+ faucet_url=getattr(config, 'l2_faucet_url', '') or 'https://l2.holesky.golemdb.io/faucet',
19
19
  captcha_base_url=getattr(config, 'captcha_url', 'https://cap.gobas.me'),
20
20
  captcha_api_key=getattr(config, 'captcha_api_key', '05381a2cef5e'),
21
21
  )
@@ -29,6 +29,10 @@ class L2FaucetService:
29
29
  return 0.0
30
30
 
31
31
  async def request_funds(self, address: str) -> Optional[str]:
32
+ # Disallow faucet if explicitly disabled by profile
33
+ if hasattr(self.cfg, 'faucet_enabled') and not getattr(self.cfg, 'faucet_enabled'):
34
+ logger.info("Faucet disabled for current payments network; skipping.")
35
+ return None
32
36
  bal = self._balance_eth(address)
33
37
  if bal > 0.01:
34
38
  logger.info(f"Sufficient L2 funds ({bal} ETH), skipping faucet.")
@@ -51,4 +55,3 @@ class L2FaucetService:
51
55
  if tx:
52
56
  logger.success(f"L2 faucet sent tx: {tx}")
53
57
  return tx
54
-
@@ -66,7 +66,9 @@ class ProviderService:
66
66
  memory: Optional[int] = None,
67
67
  storage: Optional[int] = None,
68
68
  country: Optional[str] = None,
69
- driver: Optional[str] = None
69
+ driver: Optional[str] = None,
70
+ payments_network: Optional[str] = None,
71
+ include_all_payments: bool = False,
70
72
  ) -> List[Dict]:
71
73
  """Find providers matching requirements."""
72
74
  discovery_driver = driver or config.discovery_driver
@@ -79,7 +81,11 @@ class ProviderService:
79
81
  ws_url=config.golem_base_ws_url,
80
82
  private_key=private_key_bytes,
81
83
  )
82
- return await self._find_providers_golem_base(cpu, memory, storage, country)
84
+ return await self._find_providers_golem_base(
85
+ cpu, memory, storage, country,
86
+ payments_network=payments_network,
87
+ include_all_payments=include_all_payments,
88
+ )
83
89
  else:
84
90
  return await self._find_providers_central(cpu, memory, storage, country)
85
91
 
@@ -88,7 +94,9 @@ class ProviderService:
88
94
  cpu: Optional[int] = None,
89
95
  memory: Optional[int] = None,
90
96
  storage: Optional[int] = None,
91
- country: Optional[str] = None
97
+ country: Optional[str] = None,
98
+ payments_network: Optional[str] = None,
99
+ include_all_payments: bool = False,
92
100
  ) -> List[Dict]:
93
101
  """Find providers using Golem Base."""
94
102
  try:
@@ -104,6 +112,10 @@ class ProviderService:
104
112
  # Filter by advertised network to avoid cross-network results
105
113
  if config.network:
106
114
  query += f' && golem_network="{config.network}"'
115
+ # Filter by payments network unless explicitly disabled
116
+ pn = payments_network if payments_network is not None else getattr(config, 'payments_network', None)
117
+ if pn and not include_all_payments:
118
+ query += f' && golem_payments_network="{pn}"'
107
119
  if cpu:
108
120
  query += f' && golem_cpu>={cpu}'
109
121
  if memory:
@@ -130,6 +142,7 @@ class ProviderService:
130
142
  'provider_name': annotations.get('golem_provider_name'),
131
143
  'ip_address': annotations.get('golem_ip_address'),
132
144
  'country': annotations.get('golem_country'),
145
+ 'payments_network': annotations.get('golem_payments_network'),
133
146
  'resources': {
134
147
  'cpu': int(annotations.get('golem_cpu', 0)),
135
148
  'memory': int(annotations.get('golem_memory', 0)),