remoteRF-server 0.1.63__tar.gz → 0.1.64__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 (42) hide show
  1. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/PKG-INFO +1 -1
  2. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/pyproject.toml +1 -1
  3. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_tunnel_server.py +64 -40
  4. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/PKG-INFO +1 -1
  5. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/README.md +0 -0
  6. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/setup.cfg +0 -0
  7. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/__init__.py +0 -0
  8. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/__init__.py +0 -0
  9. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  10. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  11. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  12. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  13. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  14. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/__init__.py +0 -0
  15. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  16. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/api_token.py +0 -0
  17. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  18. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_location.py +0 -0
  19. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/list_string.py +0 -0
  20. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  21. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/__init__.py +0 -0
  22. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  23. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  24. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/__init__.py +0 -0
  25. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_directory_store.py +0 -0
  26. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/__init__.py +0 -0
  27. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/acc_perms.py +0 -0
  28. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/cert_provider.py +0 -0
  29. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/device_manager.py +0 -0
  30. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/grpc_server.py +0 -0
  31. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/reservation.py +0 -0
  32. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/rpc_manager.py +0 -0
  33. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_cli.py +0 -0
  34. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_handler.py +0 -0
  35. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/serverrf_cli.py +0 -0
  36. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/tools/__init__.py +0 -0
  37. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/tools/gen_certs.py +0 -0
  38. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
  39. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  40. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  41. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/requires.txt +0 -0
  42. {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.63
3
+ Version: 0.1.64
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "0.1.63"
7
+ version = "0.1.64"
8
8
  dependencies = [
9
9
  "grpcio",
10
10
  "protobuf",
@@ -34,10 +34,12 @@ from .host_directory_store import (
34
34
 
35
35
  import hashlib
36
36
 
37
+
37
38
  def host_key(host_id: str) -> str:
38
39
  h = hashlib.sha256((host_id or "").encode("utf-8")).hexdigest()
39
40
  return h[:16] # short stable key
40
41
 
42
+
41
43
  def _copy_argument_map(dst_map: Any, src_map: Any) -> None:
42
44
  if src_map is None:
43
45
  return
@@ -108,6 +110,7 @@ class HostSession:
108
110
  fut.set_exception(RuntimeError(f"Host '{self.host_id}' disconnected"))
109
111
  self.inflight.clear()
110
112
 
113
+
111
114
  class HostTunnelRegistry:
112
115
  """
113
116
  Persistent host/device directory + live sessions.
@@ -193,16 +196,6 @@ class HostTunnelRegistry:
193
196
  known_hosts = csv_split(kv_lists.get("KNOWN_HOSTS", ""))
194
197
  known_devices = csv_split(kv_lists.get("KNOWN_DEVICES", ""))
195
198
 
196
- # also accept hosts referenced by HOST_*_DEVICES keys even if KNOWN_HOSTS drifted
197
- hosts_from_keys: Set[str] = set()
198
- for k, v in kv_lists.items():
199
- if k.startswith("HOST_") and k.endswith("_DEVICES"):
200
- # We don't know the original host_id from the sanitized key reliably.
201
- # That's fine; we still load routes primarily from DEVICE_*_HOST in meta.
202
- # But we DO want to retain the device list; we’ll stitch it via meta host ids.
203
- # So: just collect devices from these keys.
204
- pass
205
-
206
199
  # Reset directory + status (do not touch live sessions)
207
200
  with self._lock:
208
201
  self._routes.clear()
@@ -223,9 +216,6 @@ class HostTunnelRegistry:
223
216
  if k.startswith("HOST_") and k.endswith("_DEVICES"):
224
217
  devices_from_host_lists.update(csv_split(v))
225
218
 
226
- devices_from_meta: Set[str] = set()
227
- # any DEVICE_<X>_HOST implies a known device id (but we can’t reverse sanitize_env_key)
228
- # so we rely on KNOWN_DEVICES + HOST_*_DEVICES as primary device-id sources.
229
219
  # meta augments those devices; it can’t introduce new raw device ids safely.
230
220
  all_devices: List[str] = sorted(set(known_devices) | devices_from_host_lists)
231
221
 
@@ -247,15 +237,11 @@ class HostTunnelRegistry:
247
237
  kind = (kv_meta.get(f"DEVICE_{dk}_KIND", "") or "").strip()
248
238
 
249
239
  if host_id:
250
- # ensure host exists
251
240
  if host_id not in self._host_status:
252
241
  self._host_status[host_id] = HostStatus(host_id=host_id, online=False, last_seen_ms=0)
253
242
  self._host_devices.setdefault(host_id, set()).add(device_id)
254
-
255
- # route exists even while offline
256
243
  self._routes[device_id] = DeviceRoute(host_id=host_id, host_local_id=int(local_id))
257
244
 
258
- # keep info visible even when offline
259
245
  self._device_infos[device_id] = host_tunnel_pb2.DeviceInfo(
260
246
  device_id=str(device_id),
261
247
  local_id=int(local_id),
@@ -277,6 +263,7 @@ class HostTunnelRegistry:
277
263
  # ---------------------------
278
264
  # persistence write helpers
279
265
  # ---------------------------
266
+
280
267
  def _persist_host_meta(self, host_id: str, *, host_name: str = "", platform: str = "", version: str = "") -> None:
281
268
  hk = host_key(host_id)
282
269
  self._env_meta.set_kv_if_absent(f"HOST_{hk}_ID", host_id)
@@ -287,6 +274,18 @@ class HostTunnelRegistry:
287
274
  if version:
288
275
  self._env_meta.upsert_kv(f"HOST_{hk}_VERSION", version)
289
276
 
277
+ def persist_host_meta_from_hello(self, hello: Any) -> None:
278
+ """
279
+ Safe helper so HostTunnelServicer can persist from HostHello without needing to know field names.
280
+ """
281
+ try:
282
+ hid = str(getattr(hello, "host_id", "") or "").strip()
283
+ ver = str(getattr(hello, "version", "") or "").strip()
284
+ if hid:
285
+ self._persist_host_meta(hid, version=ver)
286
+ except Exception:
287
+ pass
288
+
290
289
  def _persist_host(self, host_id: str) -> None:
291
290
  self._env_lists.append_to_csv_list("KNOWN_HOSTS", host_id)
292
291
 
@@ -296,7 +295,6 @@ class HostTunnelRegistry:
296
295
  self._env_meta.set_kv_if_absent(f"HOST_{hk}_ID", host_id) # reverse map
297
296
  self._env_lists.append_to_csv_list(f"HOST_{hk}_DEVICES", device_id)
298
297
 
299
-
300
298
  dk = sanitize_env_key(device_id)
301
299
  self._env_meta.upsert_kv(f"DEVICE_{dk}_HOST", host_id)
302
300
  self._env_meta.upsert_kv(f"DEVICE_{dk}_LOCAL_ID", str(int(local_id)))
@@ -367,8 +365,6 @@ class HostTunnelRegistry:
367
365
  ds.online = True
368
366
  ds.last_seen_ms = now
369
367
 
370
- # no need to invalidate full cache; active-cache TTL handles this
371
- # but host status flips can matter; be conservative:
372
368
  self._invalidate_caches_locked()
373
369
 
374
370
  def announce_devices(self, host_id: str, devices: list) -> None:
@@ -399,17 +395,14 @@ class HostTunnelRegistry:
399
395
  if not device_id:
400
396
  continue
401
397
 
402
- # local_id is basically not used; keep for completeness
403
398
  try:
404
399
  local_id = int(getattr(d, "local_id", 0))
405
400
  except Exception:
406
401
  local_id = 0
407
402
 
408
- # route
409
403
  self._routes[device_id] = DeviceRoute(host_id=host_id, host_local_id=local_id)
410
404
  self._host_devices[host_id].add(device_id)
411
405
 
412
- # status
413
406
  ds = self._device_status.get(device_id) or DeviceStatus(
414
407
  device_id=device_id,
415
408
  host_id=host_id,
@@ -421,13 +414,11 @@ class HostTunnelRegistry:
421
414
  ds.last_seen_ms = now
422
415
  self._device_status[device_id] = ds
423
416
 
424
- # info (copy protobuf if possible)
425
417
  try:
426
418
  tmp = host_tunnel_pb2.DeviceInfo()
427
419
  tmp.CopyFrom(d)
428
420
  self._device_infos[device_id] = tmp
429
421
  except Exception:
430
- # fallback minimal
431
422
  self._device_infos[device_id] = host_tunnel_pb2.DeviceInfo(
432
423
  device_id=device_id,
433
424
  local_id=int(local_id),
@@ -445,10 +436,6 @@ class HostTunnelRegistry:
445
436
  # ---------------------------
446
437
 
447
438
  def is_host_device(self, device_id: str) -> bool:
448
- """
449
- O(1): does the directory route this device_id to a host?
450
- (Host may be offline.)
451
- """
452
439
  did = (device_id or "").strip()
453
440
  if not did:
454
441
  return False
@@ -456,10 +443,6 @@ class HostTunnelRegistry:
456
443
  return did in self._routes
457
444
 
458
445
  def is_host_device_active(self, device_id: str) -> bool:
459
- """
460
- O(1) route lookup + cached host-alive check (TTL).
461
- True iff device routes to a host AND that host's tunnel session is alive.
462
- """
463
446
  did = (device_id or "").strip()
464
447
  if not did:
465
448
  return False
@@ -485,12 +468,6 @@ class HostTunnelRegistry:
485
468
  # ---------------------------
486
469
 
487
470
  def device_directory_cached(self, *, ttl_ms: Optional[int] = None) -> Dict[str, Tuple[str, Any, bool]]:
488
- """
489
- Returns:
490
- { device_id: (host_id, device_info, is_active) }
491
-
492
- Cached rebuild at most once per ttl_ms.
493
- """
494
471
  now = now_ms()
495
472
  ttl = int(self._dir_cache_ttl_ms if ttl_ms is None else ttl_ms)
496
473
 
@@ -647,6 +624,7 @@ class HostTunnelRegistry:
647
624
  cancel_on_timeout=cancel_on_timeout,
648
625
  )
649
626
 
627
+
650
628
  def handle_host_device(
651
629
  registry: HostTunnelRegistry,
652
630
  *,
@@ -711,6 +689,23 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
711
689
  self.registry.register_host(host_id, session)
712
690
  self.registry.persist_host_meta_from_hello(hello)
713
691
 
692
+ # Ask host for metadata immediately on join (best-effort)
693
+ try:
694
+ mrid = secrets.token_hex(12)
695
+ session.send(
696
+ host_tunnel_pb2.HostFrame(
697
+ meta_request=host_tunnel_pb2.MetaRequest(
698
+ req_id=mrid,
699
+ include_platform=True,
700
+ include_env=True,
701
+ include_devices=True,
702
+ )
703
+ )
704
+ )
705
+ except Exception:
706
+ pass
707
+
708
+ # Also ping/ack
714
709
  try:
715
710
  session.send(
716
711
  host_tunnel_pb2.HostFrame(
@@ -753,6 +748,35 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
753
748
  if fut is not None and not fut.done():
754
749
  fut.set_result(resp)
755
750
 
751
+ elif which == "meta_response":
752
+ # Print host metadata (best-effort diagnostics)
753
+ try:
754
+ mr = frame.meta_response
755
+ if not mr.ok:
756
+ print(f"[HOST META] host_id={host_id or '?'} ERROR: {mr.error}")
757
+ continue
758
+
759
+ meta = mr.meta
760
+ print(f"\n[HOST META] host_id={meta.host_id} version={meta.version} unix_ms={meta.unix_ms}")
761
+
762
+ if meta.kv:
763
+ for k in sorted(meta.kv.keys()):
764
+ print(f" {k}={meta.kv[k]}")
765
+
766
+ if meta.devices:
767
+ print(" devices:")
768
+ for d in meta.devices:
769
+ print(
770
+ f" - local_id={d.local_id} "
771
+ f"label={d.label!r} "
772
+ f"device_id={d.device_id!r} "
773
+ f"serial={d.serial!r} "
774
+ f"kind={d.kind!r}"
775
+ )
776
+ print()
777
+ except Exception:
778
+ pass
779
+
756
780
  elif which == "heartbeat":
757
781
  if session is not None and host_id is not None:
758
782
  session.last_heartbeat_ms = int(frame.heartbeat.unix_ms) or now_ms()
@@ -844,4 +868,4 @@ def start_host_tunnel_server(
844
868
 
845
869
  th = threading.Thread(target=run, daemon=True)
846
870
  th.start()
847
- return server, th, reg
871
+ return server, th, reg
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.63
3
+ Version: 0.1.64
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf