gcli-control 0.12.2__tar.gz → 0.12.4__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 (49) hide show
  1. {gcli_control-0.12.2 → gcli_control-0.12.4}/PKG-INFO +1 -1
  2. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/__init__.py +1 -1
  3. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/client.py +86 -2
  4. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/host.py +3 -2
  5. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/npoint.py +1 -1
  6. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/PKG-INFO +1 -1
  7. {gcli_control-0.12.2 → gcli_control-0.12.4}/pyproject.toml +1 -1
  8. {gcli_control-0.12.2 → gcli_control-0.12.4}/README.md +0 -0
  9. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/__main__.py +0 -0
  10. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/aliases.py +0 -0
  11. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/audio.py +0 -0
  12. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/automation.py +0 -0
  13. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/bluetooth.py +0 -0
  14. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/browser.py +0 -0
  15. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/clipboard.py +0 -0
  16. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/colors.py +0 -0
  17. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/crypto.py +0 -0
  18. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/desktop.py +0 -0
  19. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/elevator.py +0 -0
  20. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/encrypt.py +0 -0
  21. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/fileops.py +0 -0
  22. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/forensics.py +0 -0
  23. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/hardware.py +0 -0
  24. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/hosts_config.py +0 -0
  25. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/keylog.py +0 -0
  26. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/monitoring.py +0 -0
  27. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/netutils.py +0 -0
  28. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/network_config.py +0 -0
  29. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/output.py +0 -0
  30. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/persistence.py +0 -0
  31. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/power.py +0 -0
  32. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/processes.py +0 -0
  33. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/protocol.py +0 -0
  34. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/registry.py +0 -0
  35. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/security.py +0 -0
  36. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/session.py +0 -0
  37. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/system.py +0 -0
  38. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/transfer.py +0 -0
  39. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/users.py +0 -0
  40. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/utils.py +0 -0
  41. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/webcam.py +0 -0
  42. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli/wifi.py +0 -0
  43. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/SOURCES.txt +0 -0
  44. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/dependency_links.txt +0 -0
  45. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/entry_points.txt +0 -0
  46. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/requires.txt +0 -0
  47. {gcli_control-0.12.2 → gcli_control-0.12.4}/gcli_control.egg-info/top_level.txt +0 -0
  48. {gcli_control-0.12.2 → gcli_control-0.12.4}/setup.cfg +0 -0
  49. {gcli_control-0.12.2 → gcli_control-0.12.4}/tests/test_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcli-control
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: Remote access tool via npoint.io - encrypted relay with 200+ commands, auto-reconnection, dashboard, session stats, rate limiting, and full desktop/system control. v0.12.2 fixes SSH connection timeout.
5
5
  Author: gcli contributors
6
6
  License-Expression: MIT
@@ -2,5 +2,5 @@
2
2
  gcli — remote access tool via npoint.io.
3
3
  Encrypted command relay using AES-256-GCM over npoint.io JSON bins.
4
4
  """
5
- __version__ = "0.12.2"
5
+ __version__ = "0.12.4"
6
6
  __author__ = "gcli"
@@ -177,6 +177,8 @@ BUILTIN_COMMANDS = [
177
177
  # Quick commands
178
178
  ":q",
179
179
  ":exit",
180
+ # Update
181
+ ":update",
180
182
  ]
181
183
 
182
184
 
@@ -276,7 +278,8 @@ class SSHSession:
276
278
  # Always try to claim — don't skip on host_alive.
277
279
  result = claim_results(doc, self.password, cmd_id, self.seen_results)
278
280
  if result and result.get("pong"):
279
- print(success("[gcli] Host responded! Connected."))
281
+ host_version = result.get("version", "unknown")
282
+ print(success(f"[gcli] Host responded! Connected. (host v{host_version})"))
280
283
  self.connected = True
281
284
  return True
282
285
  # Re-send the ping if we haven't heard back after a while.
@@ -1017,6 +1020,9 @@ class SSHSession:
1017
1020
  elif line == ":q":
1018
1021
  self._send_disconnect()
1019
1022
  self.connected = False
1023
+ # Update
1024
+ elif line == ":update":
1025
+ self._do_update()
1020
1026
  # Session stats
1021
1027
  elif line == ":stats":
1022
1028
  self._do_stats()
@@ -1259,6 +1265,7 @@ class SSHSession:
1259
1265
  print(f" {Style.GREEN}:dashboard{Style.RESET} Combined system overview")
1260
1266
  print(f" {Style.GREEN}:jobs{Style.RESET} List running background jobs")
1261
1267
  print(f" {Style.GREEN}:killjob <id>{Style.RESET} Kill a background job")
1268
+ print(f" {Style.GREEN}:update{Style.RESET} Update gcli on remote host")
1262
1269
  print(f" {Style.GREEN}:q{Style.RESET} Quick disconnect")
1263
1270
  print(f"\n {Style.GREEN}:exit{Style.RESET} Disconnect")
1264
1271
  print(f"{Style.CYAN}{Style.BOLD}{'=' * 40}{Style.RESET}\n")
@@ -1348,10 +1355,87 @@ class SSHSession:
1348
1355
  result = self._poll_result(cmd_id)
1349
1356
  if result and result.get("pong"):
1350
1357
  latency = (time.time() - t0) * 1000
1351
- print(success(f"[gcli] Pong! Round-trip: {latency:.0f}ms"))
1358
+ host_version = result.get("version", "unknown")
1359
+ print(success(f"[gcli] Pong! Round-trip: {latency:.0f}ms (host v{host_version})"))
1352
1360
  else:
1353
1361
  print(error("[gcli] No pong received"))
1354
1362
 
1363
+ def _do_update(self) -> None:
1364
+ """
1365
+ Update gcli-control on the remote host.
1366
+ 1. Get current host version
1367
+ 2. pip install latest from PyPI
1368
+ 3. Disconnect host (watchdog restarts with new version)
1369
+ 4. Wait and re-ping to verify
1370
+ """
1371
+ from . import __version__ as local_version
1372
+
1373
+ # --- Step 1: Check current host version ---
1374
+ print(info("[gcli] Checking current host version..."))
1375
+ t0 = time.time()
1376
+ cmd_id = self._send("ping", {"type": "ping"})
1377
+ result = self._poll_result(cmd_id, timeout=10.0)
1378
+ if result and result.get("pong"):
1379
+ host_version = result.get("version", "unknown")
1380
+ print(info(f"[gcli] Host version: {host_version} (your client: v{local_version})"))
1381
+ else:
1382
+ print(warn("[gcli] Could not ping host. It may be offline."))
1383
+ return
1384
+
1385
+ if host_version == local_version:
1386
+ print(success(f"[gcli] Host is already up to date (v{local_version})"))
1387
+ return
1388
+
1389
+ # --- Step 2: pip install latest ---
1390
+ print(info("[gcli] Installing latest gcli-control on remote host..."))
1391
+ install_cmd = (
1392
+ "pip install --user --break-system-packages "
1393
+ "--upgrade gcli-control 2>&1 | tail -5"
1394
+ )
1395
+ cmd_id = self._send("exec", {"type": "exec", "cmd": install_cmd, "timeout": 120})
1396
+ result = self._poll_result(cmd_id, timeout=130.0)
1397
+ if result:
1398
+ stdout = result.get("stdout", "").strip()
1399
+ if result.get("rc") == 0 or "Successfully installed" in stdout:
1400
+ print(success(f"[gcli] pip install output:\n{stdout}"))
1401
+ else:
1402
+ print(warn(f"[gcli] pip install may have issues:\n{stdout}"))
1403
+ stderr = result.get("stderr", "").strip()
1404
+ if stderr:
1405
+ print(warn(f"[gcli] stderr: {stderr[:300]}"))
1406
+ # Continue anyway — the watchdog restart might pick it up
1407
+ else:
1408
+ print(warn("[gcli] pip install timed out. Trying to continue..."))
1409
+
1410
+ # --- Step 3: Disconnect host so watchdog restarts with new version ---
1411
+ print(info("[gcli] Disconnecting host (watchdog will restart with new version)..."))
1412
+ cmd_id = self._send("disconnect", {"type": "disconnect"})
1413
+ self._poll_result(cmd_id, timeout=5.0)
1414
+
1415
+ # --- Step 4: Wait for host to come back ---
1416
+ print(info("[gcli] Waiting for host to restart (up to 90s)..."))
1417
+ for attempt in range(18):
1418
+ time.sleep(5)
1419
+ try:
1420
+ cmd_id = self._send("ping", {"type": "ping"})
1421
+ result = self._poll_result(cmd_id, timeout=8.0)
1422
+ if result and result.get("pong"):
1423
+ new_version = result.get("version", "unknown")
1424
+ latency = 0 # already measured
1425
+ print(success(f"[gcli] Host is back! Version: {new_version}"))
1426
+ if new_version == local_version:
1427
+ print(success(f"[gcli] Update successful: {host_version} -> {new_version}"))
1428
+ else:
1429
+ print(warn(f"[gcli] Expected v{local_version}, got v{new_version}"))
1430
+ self.connected = True
1431
+ return
1432
+ except Exception:
1433
+ pass
1434
+ print(info(f"[gcli] Waiting... ({5 * (attempt + 1)}s)"))
1435
+
1436
+ print(error("[gcli] Host did not come back within 90s. Check watchdog manually."))
1437
+ self.connected = False
1438
+
1355
1439
  def _do_host_status(self) -> None:
1356
1440
  """Show current host status including last_seen time."""
1357
1441
  try:
@@ -504,8 +504,9 @@ class Daemon:
504
504
  self._respond(cmd_id, {"ok": True, **info})
505
505
 
506
506
  def _handle_ping(self, cmd_id: str) -> None:
507
- """Respond to a ping."""
508
- self._respond(cmd_id, {"ok": True, "pong": True, "time": time.time()})
507
+ """Respond to a ping, including the host version."""
508
+ from . import __version__
509
+ self._respond(cmd_id, {"ok": True, "pong": True, "time": time.time(), "version": __version__})
509
510
 
510
511
  def _handle_disconnect(self, cmd_id: str) -> None:
511
512
  """Gracefully shut down the daemon."""
@@ -20,7 +20,7 @@ RETRY_DELAY = 2.0 # seconds between retries
20
20
  READ_TIMEOUT = 15
21
21
  WRITE_TIMEOUT = 30
22
22
  HEADERS = {
23
- "User-Agent": "gcli/0.12.2",
23
+ "User-Agent": "gcli/0.12.4",
24
24
  "Accept": "application/json",
25
25
  }
26
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcli-control
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: Remote access tool via npoint.io - encrypted relay with 200+ commands, auto-reconnection, dashboard, session stats, rate limiting, and full desktop/system control. v0.12.2 fixes SSH connection timeout.
5
5
  Author: gcli contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gcli-control"
7
- version = "0.12.2"
7
+ version = "0.12.4"
8
8
  description = "Remote access tool via npoint.io - encrypted relay with 200+ commands, auto-reconnection, dashboard, session stats, rate limiting, and full desktop/system control. v0.12.2 fixes SSH connection timeout."
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes