portacode 1.4.11.dev6__py3-none-any.whl → 1.4.11.dev7__py3-none-any.whl

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.
portacode/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.4.11.dev6'
32
- __version_tuple__ = version_tuple = (1, 4, 11, 'dev6')
31
+ __version__ = version = '1.4.11.dev7'
32
+ __version_tuple__ = version_tuple = (1, 4, 11, 'dev7')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -396,6 +396,32 @@ Sent intermittently while `create_proxmox_container` is executing so callers can
396
396
  * `details` (object, optional): Contains `attempt` (if retries were needed) and `error_summary` when a step fails.
397
397
  * `request_id` (string, optional): Mirrors the request ID from the incoming `create_proxmox_container` payload when available.
398
398
 
399
+ ### `start_portacode_service`
400
+
401
+ Runs `sudo portacode service install` inside the container after the dashboard has created the corresponding Device record with the supplied public key.
402
+
403
+ **Payload Fields:**
404
+
405
+ * `ctid` (string, required): Container ID target.
406
+ * `step_index` (integer, required): Next step index to render inside `proxmox_container_progress`.
407
+ * `total_steps` (integer, required): The overall total number of steps (including lifecycle, bootstrap, and service installation).
408
+
409
+ **Responses:**
410
+
411
+ * Emits additional [`proxmox_container_progress`](#proxmox_container_progress-event) events to report the authentication and service-install steps.
412
+ * On success, emits a [`proxmox_service_started`](#proxmox_service_started-event).
413
+ * On failure, emits a generic [`error`](#error) event.
414
+
415
+ ### `proxmox_service_started`
416
+
417
+ Indicates that `portacode service install` finished successfully inside a managed container.
418
+
419
+ **Event Fields:**
420
+
421
+ * `success` (boolean): True when the install succeeded.
422
+ * `message` (string): Success summary (e.g., `Portacode service install completed`).
423
+ * `ctid` (string): Container ID.
424
+
399
425
  ### `clock_sync_request`
400
426
 
401
427
  Internal event that devices send to the gateway to request the authoritative server timestamp (used for adjusting `portacode.utils.ntp_clock`). The gateway responds immediately with [`clock_sync_response`](#clock_sync_response).
@@ -45,6 +45,7 @@ from .proxmox_infra import (
45
45
  ConfigureProxmoxInfraHandler,
46
46
  CreateProxmoxContainerHandler,
47
47
  RevertProxmoxInfraHandler,
48
+ StartPortacodeServiceHandler,
48
49
  )
49
50
 
50
51
  __all__ = [
@@ -84,6 +85,7 @@ __all__ = [
84
85
  "ProjectStateGitUnstageHandler",
85
86
  "ProjectStateGitRevertHandler",
86
87
  "ProjectStateGitCommitHandler",
88
+ "StartPortacodeServiceHandler",
87
89
  "UpdatePortacodeHandler",
88
90
  "RevertProxmoxInfraHandler",
89
91
  ]
@@ -6,6 +6,7 @@ import asyncio
6
6
  import json
7
7
  import logging
8
8
  import os
9
+ import secrets
9
10
  import shutil
10
11
  import stat
11
12
  import subprocess
@@ -43,6 +44,53 @@ UNIT_DIR = Path("/etc/systemd/system")
43
44
  ProgressCallback = Callable[[int, int, Dict[str, Any], str, Optional[Dict[str, Any]]], None]
44
45
 
45
46
 
47
+ def _emit_progress_event(
48
+ handler: SyncHandler,
49
+ *,
50
+ step_index: int,
51
+ total_steps: int,
52
+ step_name: str,
53
+ step_label: str,
54
+ status: str,
55
+ message: str,
56
+ phase: str,
57
+ request_id: Optional[str],
58
+ details: Optional[Dict[str, Any]] = None,
59
+ ) -> None:
60
+ loop = handler.context.get("event_loop")
61
+ if not loop or loop.is_closed():
62
+ logger.debug(
63
+ "progress event skipped (no event loop) step=%s status=%s",
64
+ step_name,
65
+ status,
66
+ )
67
+ return
68
+
69
+ payload: Dict[str, Any] = {
70
+ "event": "proxmox_container_progress",
71
+ "step_name": step_name,
72
+ "step_label": step_label,
73
+ "status": status,
74
+ "phase": phase,
75
+ "step_index": step_index,
76
+ "total_steps": total_steps,
77
+ "message": message,
78
+ }
79
+ if request_id:
80
+ payload["request_id"] = request_id
81
+ if details:
82
+ payload["details"] = details
83
+
84
+ future = asyncio.run_coroutine_threadsafe(handler.send_response(payload), loop)
85
+ future.add_done_callback(
86
+ lambda fut: logger.warning(
87
+ "Failed to emit progress event for %s: %s", step_name, fut.exception()
88
+ )
89
+ if fut.exception()
90
+ else None
91
+ )
92
+
93
+
46
94
  def _call_subprocess(cmd: List[str], **kwargs: Any) -> subprocess.CompletedProcess[str]:
47
95
  env = os.environ.copy()
48
96
  env.setdefault("DEBIAN_FRONTEND", "noninteractive")
@@ -251,7 +299,9 @@ def _format_rootfs(storage: str, disk_gib: int, storage_type: str) -> str:
251
299
  def _get_provisioning_user_info(message: Dict[str, Any]) -> Tuple[str, str, str]:
252
300
  user = (message.get("username") or "svcuser").strip() if message else "svcuser"
253
301
  user = user or "svcuser"
254
- password = message.get("password") or "" if message else ""
302
+ password = message.get("password")
303
+ if not password:
304
+ password = secrets.token_urlsafe(10)
255
305
  ssh_key = (message.get("ssh_key") or "").strip() if message else ""
256
306
  return user, password, ssh_key
257
307
 
@@ -380,6 +430,13 @@ def _write_container_record(vmid: int, payload: Dict[str, Any]) -> None:
380
430
  path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
381
431
 
382
432
 
433
+ def _read_container_record(vmid: int) -> Dict[str, Any]:
434
+ path = CONTAINERS_DIR / f"ct-{vmid}.json"
435
+ if not path.exists():
436
+ raise FileNotFoundError(f"Container record {path} missing")
437
+ return json.loads(path.read_text(encoding="utf-8"))
438
+
439
+
383
440
  def _build_container_payload(message: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]:
384
441
  templates = config.get("templates") or []
385
442
  default_template = templates[0] if templates else ""
@@ -428,10 +485,10 @@ def _connect_proxmox(config: Dict[str, Any]) -> Any:
428
485
  )
429
486
 
430
487
 
431
- def _run_pct(vmid: int, cmd: str) -> Dict[str, Any]:
488
+ def _run_pct(vmid: int, cmd: str, input_text: Optional[str] = None) -> Dict[str, Any]:
432
489
  full = ["pct", "exec", str(vmid), "--", "bash", "-lc", cmd]
433
490
  start = time.time()
434
- proc = subprocess.run(full, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
491
+ proc = subprocess.run(full, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input_text)
435
492
  return {
436
493
  "cmd": cmd,
437
494
  "returncode": proc.returncode,
@@ -560,6 +617,8 @@ def _run_setup_steps(
560
617
  continue
561
618
 
562
619
  attempts = 0
620
+ retry_on = step.get("retry_on", [])
621
+ max_attempts = step.get("retries", 0) + 1
563
622
  while True:
564
623
  attempts += 1
565
624
  res = _run_pct(vmid, step["cmd"])
@@ -572,14 +631,21 @@ def _run_setup_steps(
572
631
  if progress_callback:
573
632
  progress_callback(step_index, computed_total, step, "completed", res)
574
633
  break
634
+
635
+ will_retry = False
636
+ if attempts < max_attempts and retry_on:
637
+ stderr_stdout = (res.get("stderr", "") + res.get("stdout", ""))
638
+ if any(tok in stderr_stdout for tok in retry_on):
639
+ will_retry = True
640
+
575
641
  if progress_callback:
576
- progress_callback(step_index, computed_total, step, "failed", res)
577
- retry_on = step.get("retry_on", [])
578
- if attempts >= step.get("retries", 0) + 1:
579
- return results, False
580
- if any(tok in (res.get("stderr", "") + res.get("stdout", "")) for tok in retry_on):
642
+ status = "retrying" if will_retry else "failed"
643
+ progress_callback(step_index, computed_total, step, status, res)
644
+
645
+ if will_retry:
581
646
  time.sleep(step.get("retry_delay_s", 3))
582
647
  continue
648
+
583
649
  return results, False
584
650
  return results, True
585
651
 
@@ -746,58 +812,12 @@ class CreateProxmoxContainerHandler(SyncHandler):
746
812
  def command_name(self) -> str:
747
813
  return "create_proxmox_container"
748
814
 
749
- def _emit_progress_event(
750
- self,
751
- *,
752
- step_index: int,
753
- total_steps: int,
754
- step_name: str,
755
- step_label: str,
756
- status: str,
757
- message: str,
758
- phase: str,
759
- request_id: Optional[str],
760
- details: Optional[Dict[str, Any]] = None,
761
- ) -> None:
762
- loop = self.context.get("event_loop")
763
- if not loop or loop.is_closed():
764
- logger.debug(
765
- "progress event skipped (no event loop) step=%s status=%s",
766
- step_name,
767
- status,
768
- )
769
- return
770
-
771
- payload: Dict[str, Any] = {
772
- "event": "proxmox_container_progress",
773
- "step_name": step_name,
774
- "step_label": step_label,
775
- "status": status,
776
- "phase": phase,
777
- "step_index": step_index,
778
- "total_steps": total_steps,
779
- "message": message,
780
- }
781
- if request_id:
782
- payload["request_id"] = request_id
783
- if details:
784
- payload["details"] = details
785
-
786
- future = asyncio.run_coroutine_threadsafe(self.send_response(payload), loop)
787
- future.add_done_callback(
788
- lambda fut: logger.warning(
789
- "Failed to emit progress event for %s: %s", step_name, fut.exception()
790
- )
791
- if fut.exception()
792
- else None
793
- )
794
-
795
815
  def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
796
816
  logger.info("create_proxmox_container command received")
797
817
  request_id = message.get("request_id")
798
818
  bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
799
819
  bootstrap_steps = _build_bootstrap_steps(bootstrap_user, bootstrap_password, bootstrap_ssh_key)
800
- total_steps = 3 + len(bootstrap_steps)
820
+ total_steps = 3 + len(bootstrap_steps) + 2
801
821
  current_step_index = 1
802
822
 
803
823
  def _run_lifecycle_step(
@@ -809,7 +829,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
809
829
  ):
810
830
  nonlocal current_step_index
811
831
  step_index = current_step_index
812
- self._emit_progress_event(
832
+ _emit_progress_event(
813
833
  step_index=step_index,
814
834
  total_steps=total_steps,
815
835
  step_name=step_name,
@@ -822,7 +842,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
822
842
  try:
823
843
  result = action()
824
844
  except Exception as exc:
825
- self._emit_progress_event(
845
+ _emit_progress_event(
826
846
  step_index=step_index,
827
847
  total_steps=total_steps,
828
848
  step_name=step_name,
@@ -834,7 +854,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
834
854
  details={"error": str(exc)},
835
855
  )
836
856
  raise
837
- self._emit_progress_event(
857
+ _emit_progress_event(
838
858
  step_index=step_index,
839
859
  total_steps=total_steps,
840
860
  step_name=step_name,
@@ -905,25 +925,33 @@ class CreateProxmoxContainerHandler(SyncHandler):
905
925
  _start_container_step,
906
926
  )
907
927
 
908
- def _bootstrap_progress_callback(step_index: int, total: int, step: Dict[str, Any], status: str, result: Optional[Dict[str, Any]]):
928
+ def _bootstrap_progress_callback(
929
+ step_index: int,
930
+ total: int,
931
+ step: Dict[str, Any],
932
+ status: str,
933
+ result: Optional[Dict[str, Any]],
934
+ ):
909
935
  label = step.get("display_name") or _friendly_step_label(step.get("name", "bootstrap"))
936
+ error_summary = (result or {}).get("error_summary") or (result or {}).get("error")
937
+ attempt = (result or {}).get("attempt")
910
938
  if status == "in_progress":
911
939
  message_text = f"{label} is running…"
912
940
  elif status == "completed":
913
941
  message_text = f"{label} completed."
942
+ elif status == "retrying":
943
+ attempt_desc = f" (attempt {attempt})" if attempt else ""
944
+ message_text = f"{label} failed{attempt_desc}; retrying…"
914
945
  else:
915
- summary = (result or {}).get("error_summary") or (result or {}).get("error")
916
946
  message_text = f"{label} failed"
917
- if summary:
918
- message_text += f": {summary}"
947
+ if error_summary:
948
+ message_text += f": {error_summary}"
919
949
  details: Dict[str, Any] = {}
920
- if status == "failed" and result:
921
- if result.get("attempt"):
922
- details["attempt"] = result["attempt"]
923
- summary_detail = result.get("error_summary") or result.get("error")
924
- if summary_detail:
925
- details["error_summary"] = summary_detail
926
- self._emit_progress_event(
950
+ if attempt:
951
+ details["attempt"] = attempt
952
+ if error_summary:
953
+ details["error_summary"] = error_summary
954
+ _emit_progress_event(
927
955
  step_index=step_index,
928
956
  total_steps=total,
929
957
  step_name=step.get("name", "bootstrap"),
@@ -945,6 +973,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
945
973
  start_index=current_step_index,
946
974
  total_steps=total_steps,
947
975
  )
976
+ current_step_index += len(bootstrap_steps)
948
977
 
949
978
  return {
950
979
  "event": "proxmox_container_created",
@@ -965,6 +994,112 @@ class CreateProxmoxContainerHandler(SyncHandler):
965
994
  }
966
995
 
967
996
 
997
+ class StartPortacodeServiceHandler(SyncHandler):
998
+ """Start the Portacode service inside a newly created container."""
999
+
1000
+ @property
1001
+ def command_name(self) -> str:
1002
+ return "start_portacode_service"
1003
+
1004
+ def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
1005
+ ctid = message.get("ctid")
1006
+ if not ctid:
1007
+ raise ValueError("ctid is required")
1008
+ try:
1009
+ vmid = int(ctid)
1010
+ except ValueError:
1011
+ raise ValueError("ctid must be an integer")
1012
+
1013
+ record = _read_container_record(vmid)
1014
+ user = record.get("username")
1015
+ password = record.get("password")
1016
+ if not user or not password:
1017
+ raise RuntimeError("Container credentials unavailable")
1018
+
1019
+ start_index = int(message.get("step_index", 1))
1020
+ total_steps = int(message.get("total_steps", start_index + 2))
1021
+ request_id = message.get("request_id")
1022
+
1023
+ auth_step_name = "setup_device_authentication"
1024
+ auth_label = "Setting up device authentication"
1025
+ _emit_progress_event(
1026
+ self,
1027
+ step_index=start_index,
1028
+ total_steps=total_steps,
1029
+ step_name=auth_step_name,
1030
+ step_label=auth_label,
1031
+ status="in_progress",
1032
+ message="Notifying the server of the new device…",
1033
+ phase="service",
1034
+ request_id=request_id,
1035
+ )
1036
+ _emit_progress_event(
1037
+ self,
1038
+ step_index=start_index,
1039
+ total_steps=total_steps,
1040
+ step_name=auth_step_name,
1041
+ step_label=auth_label,
1042
+ status="completed",
1043
+ message="Authentication metadata recorded.",
1044
+ phase="service",
1045
+ request_id=request_id,
1046
+ )
1047
+
1048
+ install_step = start_index + 1
1049
+ install_label = "Launching Portacode service"
1050
+ _emit_progress_event(
1051
+ self,
1052
+ step_index=install_step,
1053
+ total_steps=total_steps,
1054
+ step_name="launch_portacode_service",
1055
+ step_label=install_label,
1056
+ status="in_progress",
1057
+ message="Running sudo portacode service install…",
1058
+ phase="service",
1059
+ request_id=request_id,
1060
+ )
1061
+
1062
+ cmd = f"su - {user} -c 'sudo -S portacode service install'"
1063
+ res = _run_pct(vmid, cmd, input_text=password + "\n")
1064
+
1065
+ if res["returncode"] != 0:
1066
+ _emit_progress_event(
1067
+ self,
1068
+ step_index=install_step,
1069
+ total_steps=total_steps,
1070
+ step_name="launch_portacode_service",
1071
+ step_label=install_label,
1072
+ status="failed",
1073
+ message=f"{install_label} failed: {res.get('stderr') or res.get('stdout')}",
1074
+ phase="service",
1075
+ request_id=request_id,
1076
+ details={
1077
+ "stderr": res.get("stderr"),
1078
+ "stdout": res.get("stdout"),
1079
+ },
1080
+ )
1081
+ raise RuntimeError(res.get("stderr") or res.get("stdout") or "Service install failed")
1082
+
1083
+ _emit_progress_event(
1084
+ self,
1085
+ step_index=install_step,
1086
+ total_steps=total_steps,
1087
+ step_name="launch_portacode_service",
1088
+ step_label=install_label,
1089
+ status="completed",
1090
+ message="Portacode service install finished.",
1091
+ phase="service",
1092
+ request_id=request_id,
1093
+ )
1094
+
1095
+ return {
1096
+ "event": "proxmox_service_started",
1097
+ "success": True,
1098
+ "message": "Portacode service install completed",
1099
+ "ctid": str(vmid),
1100
+ }
1101
+
1102
+
968
1103
  class ConfigureProxmoxInfraHandler(SyncHandler):
969
1104
  @property
970
1105
  def command_name(self) -> str:
@@ -55,6 +55,7 @@ from .handlers import (
55
55
  ConfigureProxmoxInfraHandler,
56
56
  CreateProxmoxContainerHandler,
57
57
  RevertProxmoxInfraHandler,
58
+ StartPortacodeServiceHandler,
58
59
  )
59
60
  from .handlers.project_aware_file_handlers import (
60
61
  ProjectAwareFileWriteHandler,
@@ -478,6 +479,7 @@ class TerminalManager:
478
479
  # System management handlers
479
480
  self._command_registry.register(ConfigureProxmoxInfraHandler)
480
481
  self._command_registry.register(CreateProxmoxContainerHandler)
482
+ self._command_registry.register(StartPortacodeServiceHandler)
481
483
  self._command_registry.register(RevertProxmoxInfraHandler)
482
484
  self._command_registry.register(UpdatePortacodeHandler)
483
485
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.11.dev6
3
+ Version: 1.4.11.dev7
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -1,7 +1,7 @@
1
1
  portacode/README.md,sha256=4dKtpvR8LNgZPVz37GmkQCMWIr_u25Ao63iW56s7Ke4,775
2
2
  portacode/__init__.py,sha256=oB3sV1wXr-um-RXio73UG8E5Xx6cF2ZVJveqjNmC-vQ,1086
3
3
  portacode/__main__.py,sha256=jmHTGC1hzmo9iKJLv-SSYe9BSIbPPZ2IOpecI03PlTs,296
4
- portacode/_version.py,sha256=5aWhaBfuSdCUpEwbbl75wIVr6LiHBu_Cbrnz4tRTfac,719
4
+ portacode/_version.py,sha256=rMZzV0VJav1wTlnbu-swqozHj1iDFuOjk8XIEpAzY8o,719
5
5
  portacode/cli.py,sha256=mGLKoZ-T2FBF7IA9wUq0zyG0X9__-A1ao7gajjcVRH8,21828
6
6
  portacode/data.py,sha256=5-s291bv8J354myaHm1Y7CQZTZyRzMU3TGe5U4hb-FA,1591
7
7
  portacode/keypair.py,sha256=0OO4vHDcF1XMxCDqce61xFTlFwlTcmqe5HyGsXFEt7s,5838
@@ -12,17 +12,17 @@ portacode/connection/README.md,sha256=f9rbuIEKa7cTm9C98rCiBbEtbiIXQU11esGSNhSMiJ
12
12
  portacode/connection/__init__.py,sha256=atqcVGkViIEd7pRa6cP2do07RJOM0UWpbnz5zXjGktU,250
13
13
  portacode/connection/client.py,sha256=jtLb9_YufqPkzi9t8VQH3iz_JEMisbtY6a8L9U5weiU,14181
14
14
  portacode/connection/multiplex.py,sha256=L-TxqJ_ZEbfNEfu1cwxgJ5vUdyRzZjsMy2Kx1diiZys,5237
15
- portacode/connection/terminal.py,sha256=oImE33DgtDEEjVM9r1oUejl0miJgwZaJ9K3pAeHhzBo,44653
15
+ portacode/connection/terminal.py,sha256=07wxG_55JMy3yQ9TXCBldW9h43qCW3U8rv2yzGMx4FM,44757
16
16
  portacode/connection/handlers/README.md,sha256=HsLZG1QK1JNm67HsgL6WoDg9nxzKXxwkc5fJPFJdX5g,12169
17
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=gtUP5DEoKSg5q2rO08DxM9r_fNhjXkNzy3sNc6dZArE,92824
18
- portacode/connection/handlers/__init__.py,sha256=iiyF3smwiI0IeDYzWQTl2PPVfW6aSp-g2CSO1ZTo9Ho,2641
17
+ portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=Z5L0gJWoHxV7UonHqxHko_PXZd7Z1mbI6yWtmjxna-s,93951
18
+ portacode/connection/handlers/__init__.py,sha256=y-Aj5SXqc_QJt7i1xkl7kv381Fd2CIcUiG5gR1F8qkI,2711
19
19
  portacode/connection/handlers/base.py,sha256=oENFb-Fcfzwk99Qx8gJQriEMiwSxwygwjOiuCH36hM4,10231
20
20
  portacode/connection/handlers/chunked_content.py,sha256=h6hXRmxSeOgnIxoU8CkmvEf2Odv-ajPrpHIe_W3GKcA,9251
21
21
  portacode/connection/handlers/diff_handlers.py,sha256=iYTIRCcpEQ03vIPKZCsMTE5aZbQw6sF04M3dM6rUV8Q,24477
22
22
  portacode/connection/handlers/file_handlers.py,sha256=nAJH8nXnX07xxD28ngLpgIUzcTuRwZBNpEGEKdRqohw,39507
23
23
  portacode/connection/handlers/project_aware_file_handlers.py,sha256=AqgMnDqX2893T2NsrvUSCwjN5VKj4Pb2TN0S_SuboOE,9803
24
24
  portacode/connection/handlers/project_state_handlers.py,sha256=v6ZefGW9i7n1aZLq2jOGumJIjYb6aHlPI4m1jkYewm8,1686
25
- portacode/connection/handlers/proxmox_infra.py,sha256=F2M7qQxllo32rdERCcYYAC6t8albYd8vAmemAXGwxPs,36836
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=KeYgb7IVdK_nSPJmrPp2W0HR0rlNCQ46DTLRQ63OF9Y,41117
26
26
  portacode/connection/handlers/registry.py,sha256=qXGE60sYEWg6ZtVQzFcZ5YI2XWR6lMgw4hAL9x5qR1I,6181
27
27
  portacode/connection/handlers/session.py,sha256=uNGfiO_1B9-_yjJKkpvmbiJhIl6b-UXlT86UTfd6WYE,42219
28
28
  portacode/connection/handlers/system_handlers.py,sha256=AKh7IbwptlLYrbSw5f-DHigvlaKHsg9lDP-lkAUm8cE,10755
@@ -64,7 +64,7 @@ portacode/utils/__init__.py,sha256=NgBlWTuNJESfIYJzP_3adI1yJQJR0XJLRpSdVNaBAN0,3
64
64
  portacode/utils/diff_apply.py,sha256=4Oi7ft3VUCKmiUE4VM-OeqO7Gk6H7PF3WnN4WHXtjxI,15157
65
65
  portacode/utils/diff_renderer.py,sha256=S76StnQ2DLfsz4Gg0m07UwPfRp8270PuzbNaQq-rmYk,13850
66
66
  portacode/utils/ntp_clock.py,sha256=VqCnWCTehCufE43W23oB-WUdAZGeCcLxkmIOPwInYHc,2499
67
- portacode-1.4.11.dev6.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
67
+ portacode-1.4.11.dev7.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
68
  test_modules/README.md,sha256=Do_agkm9WhSzueXjRAkV_xEj6Emy5zB3N3VKY5Roce8,9274
69
69
  test_modules/__init__.py,sha256=1LcbHodIHsB0g-g4NGjSn6AMuCoGbymvXPYLOb6Z7F0,53
70
70
  test_modules/test_device_online.py,sha256=QtYq0Dq9vME8Gp2O4fGSheqVf8LUtpsSKosXXk56gGM,1654
@@ -90,8 +90,8 @@ testing_framework/core/playwright_manager.py,sha256=Tw46qwxIhOFkS48C2IWIQHHNpEe-
90
90
  testing_framework/core/runner.py,sha256=j2QwNJmAxVBmJvcbVS7DgPJUKPNzqfLmt_4NNdaKmZU,19297
91
91
  testing_framework/core/shared_cli_manager.py,sha256=BESSNtyQb7BOlaOvZmm04T8Uezjms4KCBs2MzTxvzYQ,8790
92
92
  testing_framework/core/test_discovery.py,sha256=2FZ9fJ8Dp5dloA-fkgXoJ_gCMC_nYPBnA3Hs2xlagzM,4928
93
- portacode-1.4.11.dev6.dist-info/METADATA,sha256=qREiPXfLBIuIlBTY9tHtU34IHLIeTlE0yknR5gmUMW8,13051
94
- portacode-1.4.11.dev6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
- portacode-1.4.11.dev6.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
96
- portacode-1.4.11.dev6.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
97
- portacode-1.4.11.dev6.dist-info/RECORD,,
93
+ portacode-1.4.11.dev7.dist-info/METADATA,sha256=6A_o1zGxAkhlk0n-T3_tozO_7X-fdVO3n6RXjOCCBvs,13051
94
+ portacode-1.4.11.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
+ portacode-1.4.11.dev7.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
96
+ portacode-1.4.11.dev7.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
97
+ portacode-1.4.11.dev7.dist-info/RECORD,,