portacode 1.4.16.dev2__py3-none-any.whl → 1.4.16.dev4__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.16.dev2'
32
- __version_tuple__ = version_tuple = (1, 4, 16, 'dev2')
31
+ __version__ = version = '1.4.16.dev4'
32
+ __version_tuple__ = version_tuple = (1, 4, 16, 'dev4')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -1136,22 +1136,34 @@ Provides system information in response to a `system_info` action. Handled by [`
1136
1136
  * `bridge` (string): The bridge interface configured (typically `vmbr1`).
1137
1137
  * `health` (string|null): `"healthy"` when the connectivity verification succeeded.
1138
1138
  * `node_status` (object|null): Status response returned by the Proxmox API when validating the token.
1139
- * `managed_containers` (object): Cached summary of the Portacode-managed containers:
1140
- * `updated_at` (string): ISO timestamp when this snapshot was last refreshed.
1141
- * `count` (integer): Number of managed containers.
1142
- * `total_ram_mib` (integer): RAM footprint summed across all containers.
1143
- * `total_disk_gib` (integer): Disk footprint summed across all containers.
1144
- * `total_cpu_share` (number): CPU shares requested across all containers.
1145
- * `containers` (array[object]): Container summaries with the following fields:
1146
- * `vmid` (string|null): Numeric CT ID.
1147
- * `hostname` (string|null): Hostname configured in the CT.
1148
- * `template` (string|null): Template identifier used.
1149
- * `storage` (string|null): Storage pool backing the rootfs.
1150
- * `disk_gib` (integer): Rootfs size in GiB.
1151
- * `ram_mib` (integer): Memory size in MiB.
1152
- * `cpu_share` (number): vCPU-equivalent share requested at creation.
1153
- * `status` (string): Lowercase lifecycle status (e.g., `running`, `stopped`, `deleted`).
1154
- * `created_at` (string|null): ISO timestamp recorded when the CT was provisioned.
1139
+ * `managed_containers` (object): Cached summary of the Portacode-managed containers:
1140
+ * `updated_at` (string): ISO timestamp when this snapshot was last refreshed.
1141
+ * `count` (integer): Number of managed containers.
1142
+ * `total_ram_mib` (integer): RAM footprint summed across all containers.
1143
+ * `total_disk_gib` (integer): Disk footprint summed across all containers.
1144
+ * `total_cpu_share` (number): CPU shares requested across all containers.
1145
+ * `containers` (array[object]): Container summaries with the following fields:
1146
+ * `vmid` (string|null): Numeric CT ID.
1147
+ * `hostname` (string|null): Hostname configured in the CT.
1148
+ * `template` (string|null): Template identifier used.
1149
+ * `storage` (string|null): Storage pool backing the rootfs.
1150
+ * `disk_gib` (integer): Rootfs size in GiB.
1151
+ * `ram_mib` (integer): Memory size in MiB.
1152
+ * `cpu_share` (number): vCPU-equivalent share requested at creation.
1153
+ * `status` (string): Lowercase lifecycle status (e.g., `running`, `stopped`, `deleted`).
1154
+ * `created_at` (string|null): ISO timestamp recorded when the CT was provisioned.
1155
+ * `managed` (boolean): `true` for Portacode-managed entries.
1156
+ * `unmanaged_containers` (array[object]): Facts about containers Portacode did not provision; fields mirror the managed list but are marked `managed=false`.
1157
+ * `unmanaged_count` (integer): Number of unmanaged containers detected on the node.
1158
+ * `allocated_ram_mib` (integer): Total RAM reserved by both managed and unmanaged containers.
1159
+ * `allocated_disk_gib` (integer): Total disk reserved by both managed and unmanaged containers.
1160
+ * `allocated_cpu_share` (number): Total CPU shares requested by both managed and unmanaged containers.
1161
+ * `available_ram_mib` (integer|null): Remaining RAM after subtracting all reservations from the host total (null when unavailable).
1162
+ * `available_disk_gib` (integer|null): Remaining disk GB after subtracting allocations from the host total.
1163
+ * `available_cpu_share` (number|null): Remaining CPU shares after allocations.
1164
+ * `host_total_ram_mib` (integer|null): Host memory capacity observed via Proxmox.
1165
+ * `host_total_disk_gib` (integer|null): Host disk capacity observed via Proxmox.
1166
+ * `host_total_cpu_cores` (integer|null): Number of CPU cores reported by Proxmox.
1155
1167
  * `portacode_version` (string): Installed CLI version returned by `portacode.__version__`.
1156
1168
 
1157
1169
  ### `proxmox_infra_configured`
@@ -7,6 +7,7 @@ import json
7
7
  import logging
8
8
  import math
9
9
  import os
10
+ import re
10
11
  import secrets
11
12
  import shlex
12
13
  import shutil
@@ -18,7 +19,7 @@ import time
18
19
  import threading
19
20
  from datetime import datetime, timezone
20
21
  from pathlib import Path
21
- from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
22
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple
22
23
 
23
24
  import platformdirs
24
25
 
@@ -201,6 +202,70 @@ def _current_time_iso() -> str:
201
202
  return datetime.now(timezone.utc).isoformat()
202
203
 
203
204
 
205
+ def _to_int(value: Any, default: int = 0) -> int:
206
+ try:
207
+ return int(value)
208
+ except (TypeError, ValueError):
209
+ return default
210
+
211
+
212
+ def _to_float(value: Any, default: float = 0.0) -> float:
213
+ try:
214
+ return float(value)
215
+ except (TypeError, ValueError):
216
+ return default
217
+
218
+
219
+ def _calculate_available(total: Optional[float], used: float) -> Optional[float]:
220
+ if total is None:
221
+ return None
222
+ delta = total - used
223
+ return delta if delta >= 0 else 0.0
224
+
225
+
226
+ def _normalize_bytes(value: Any) -> float:
227
+ if isinstance(value, (int, float)):
228
+ return float(value)
229
+ text = str(value or "").strip()
230
+ if not text:
231
+ return 0.0
232
+ match = re.match(r"(?i)^\s*([0-9]*\.?[0-9]+)\s*([kmgtp]?i?b?)?\s*$", text)
233
+ if not match:
234
+ return 0.0
235
+ number = match.group(1)
236
+ unit = (match.group(2) or "").lower()
237
+ try:
238
+ value = float(number)
239
+ except ValueError:
240
+ return 0.0
241
+ if unit.startswith("k"):
242
+ return value * 1024
243
+ if unit.startswith("m"):
244
+ return value * 1024**2
245
+ if unit.startswith("g"):
246
+ return value * 1024**3
247
+ if unit.startswith("t"):
248
+ return value * 1024**4
249
+ if unit.startswith("p"):
250
+ return value * 1024**5
251
+ return value
252
+
253
+
254
+ def _bytes_to_mib(value: Any) -> int:
255
+ return int(round(_normalize_bytes(value) / (1024**2)))
256
+
257
+
258
+ def _bytes_to_gib(value: Any) -> int:
259
+ return int(round(_normalize_bytes(value) / (1024**3)))
260
+
261
+
262
+ def _parse_bool_flag(value: Any) -> bool:
263
+ if isinstance(value, bool):
264
+ return value
265
+ text = str(value or "").strip().lower()
266
+ return text in {"1", "true", "yes", "on"}
267
+
268
+
204
269
  def _parse_iso_timestamp(value: str) -> Optional[datetime]:
205
270
  if not value:
206
271
  return None
@@ -381,35 +446,68 @@ def _load_managed_container_records() -> List[Dict[str, Any]]:
381
446
  return records
382
447
 
383
448
 
384
- def _build_managed_containers_summary(records: List[Dict[str, Any]]) -> Dict[str, Any]:
449
+ def _extract_host_totals(node_status: Dict[str, Any] | None) -> Tuple[Optional[int], Optional[int], Optional[int]]:
450
+ if not node_status:
451
+ return None, None, None
452
+ memory_total = node_status.get("memory", {}).get("total")
453
+ disk_total = node_status.get("disk", {}).get("total")
454
+ cpu_cores = node_status.get("cpuinfo", {}).get("cores")
455
+ host_ram = _bytes_to_mib(memory_total) if memory_total is not None else None
456
+ host_disk = _bytes_to_gib(disk_total) if disk_total is not None else None
457
+ host_cpu = _to_int(cpu_cores) if cpu_cores is not None else None
458
+ return host_ram, host_disk, host_cpu
459
+
460
+
461
+ def _build_unmanaged_container_entry(ct: Dict[str, Any], cfg: Dict[str, Any], vmid: str) -> Dict[str, Any]:
462
+ ram_mib = _to_int(cfg.get("memory")) or _bytes_to_mib(ct.get("maxmem"))
463
+ disk_gib = _bytes_to_gib(ct.get("maxdisk"))
464
+ cpu_share = _to_float(
465
+ cfg.get("cpulimit")
466
+ or cfg.get("cpus")
467
+ or cfg.get("cores")
468
+ or ct.get("cpus")
469
+ or ct.get("cpu")
470
+ )
471
+ hostname = ct.get("name") or cfg.get("hostname") or f"ct{vmid}"
472
+ storage = cfg.get("storage") or ct.get("storage")
473
+ status = (ct.get("status") or "unknown").lower()
474
+ reserved = _parse_bool_flag(cfg.get("onboot"))
475
+ return {
476
+ "vmid": vmid,
477
+ "hostname": hostname,
478
+ "template": cfg.get("ostemplate"),
479
+ "storage": storage,
480
+ "disk_gib": disk_gib,
481
+ "ram_mib": ram_mib,
482
+ "cpu_share": cpu_share,
483
+ "reserve_on_boot": reserved,
484
+ "status": status,
485
+ "managed": False,
486
+ }
487
+
488
+
489
+
490
+ def _build_managed_containers_summary(
491
+ records: List[Dict[str, Any]],
492
+ unmanaged_records: List[Dict[str, Any]],
493
+ node_status: Dict[str, Any] | None,
494
+ ) -> Dict[str, Any]:
495
+ managed_containers: List[Dict[str, Any]] = []
385
496
  total_ram = 0
386
497
  total_disk = 0
387
498
  total_cpu_share = 0.0
388
- containers: List[Dict[str, Any]] = []
389
-
390
- def _as_int(value: Any) -> int:
391
- try:
392
- return int(value)
393
- except (TypeError, ValueError):
394
- return 0
395
499
 
396
- def _as_float(value: Any) -> float:
397
- try:
398
- return float(value)
399
- except (TypeError, ValueError):
400
- return 0.0
401
-
402
- for record in sorted(records, key=lambda entry: _as_int(entry.get("vmid"))):
403
- ram_mib = _as_int(record.get("ram_mib"))
404
- disk_gib = _as_int(record.get("disk_gib"))
405
- cpu_share = _as_float(record.get("cpus"))
500
+ for record in sorted(records, key=lambda entry: _to_int(entry.get("vmid"))):
501
+ ram_mib = _to_int(record.get("ram_mib"))
502
+ disk_gib = _to_int(record.get("disk_gib"))
503
+ cpu_share = _to_float(record.get("cpus"))
406
504
  total_ram += ram_mib
407
505
  total_disk += disk_gib
408
506
  total_cpu_share += cpu_share
409
507
  status = (record.get("status") or "unknown").lower()
410
- containers.append(
508
+ managed_containers.append(
411
509
  {
412
- "vmid": str(_as_int(record.get("vmid"))) if record.get("vmid") is not None else None,
510
+ "vmid": str(_to_int(record.get("vmid"))) if record.get("vmid") is not None else None,
413
511
  "device_id": record.get("device_id"),
414
512
  "hostname": record.get("hostname"),
415
513
  "template": record.get("template"),
@@ -419,44 +517,84 @@ def _build_managed_containers_summary(records: List[Dict[str, Any]]) -> Dict[str
419
517
  "cpu_share": cpu_share,
420
518
  "created_at": record.get("created_at"),
421
519
  "status": status,
520
+ "managed": True,
422
521
  }
423
522
  )
424
523
 
524
+ unmanaged_total_ram = sum(
525
+ _to_int(entry.get("ram_mib"))
526
+ for entry in unmanaged_records
527
+ if entry.get("reserve_on_boot")
528
+ )
529
+ unmanaged_total_disk = sum(_to_int(entry.get("disk_gib")) for entry in unmanaged_records)
530
+ unmanaged_total_cpu = sum(
531
+ _to_float(entry.get("cpu_share"))
532
+ for entry in unmanaged_records
533
+ if entry.get("reserve_on_boot")
534
+ )
535
+
536
+ allocated_ram = total_ram + unmanaged_total_ram
537
+ allocated_disk = total_disk + unmanaged_total_disk
538
+ allocated_cpu = total_cpu_share + unmanaged_total_cpu
539
+
540
+ host_ram, host_disk, host_cpu = _extract_host_totals(node_status)
541
+
425
542
  return {
426
543
  "updated_at": datetime.utcnow().isoformat() + "Z",
427
- "count": len(containers),
544
+ "count": len(managed_containers),
428
545
  "total_ram_mib": total_ram,
429
546
  "total_disk_gib": total_disk,
430
547
  "total_cpu_share": round(total_cpu_share, 2),
431
- "containers": containers,
548
+ "containers": managed_containers,
549
+ "unmanaged_containers": unmanaged_records,
550
+ "unmanaged_count": len(unmanaged_records),
551
+ "allocated_ram_mib": allocated_ram,
552
+ "allocated_disk_gib": allocated_disk,
553
+ "allocated_cpu_share": round(allocated_cpu, 2),
554
+ "available_ram_mib": _calculate_available(host_ram, allocated_ram) if host_ram is not None else None,
555
+ "available_disk_gib": _calculate_available(host_disk, allocated_disk) if host_disk is not None else None,
556
+ "available_cpu_share": _calculate_available(host_cpu, allocated_cpu) if host_cpu is not None else None,
557
+ "host_total_ram_mib": host_ram,
558
+ "host_total_disk_gib": host_disk,
559
+ "host_total_cpu_cores": host_cpu,
432
560
  }
433
561
 
434
562
 
435
563
  def _get_managed_containers_summary(force: bool = False) -> Dict[str, Any]:
436
- def _refresh_container_statuses(records: List[Dict[str, Any]], config: Dict[str, Any] | None) -> None:
437
- if not records or not config:
438
- return
564
+ def _refresh_container_statuses(
565
+ records: List[Dict[str, Any]],
566
+ config: Dict[str, Any] | None,
567
+ managed_vmids: Set[str],
568
+ ) -> Tuple[Dict[str, str], List[Dict[str, Any]], Dict[str, Any] | None]:
569
+ statuses: Dict[str, str] = {}
570
+ unmanaged: List[Dict[str, Any]] = []
571
+ node_status: Dict[str, Any] | None = None
572
+ if not config:
573
+ return statuses, unmanaged, node_status
439
574
  try:
440
575
  proxmox = _connect_proxmox(config)
441
576
  node = _get_node_from_config(config)
442
- statuses = {
443
- str(ct.get("vmid")): (ct.get("status") or "unknown").lower()
444
- for ct in proxmox.nodes(node).lxc.get()
445
- }
577
+ node_status = proxmox.nodes(node).status.get()
578
+ for ct in proxmox.nodes(node).lxc.get():
579
+ vmid_val = ct.get("vmid")
580
+ if vmid_val is None:
581
+ continue
582
+ vmid_key = str(_to_int(vmid_val))
583
+ statuses[vmid_key] = (ct.get("status") or "unknown").lower()
584
+ if vmid_key in managed_vmids:
585
+ continue
586
+ cfg: Dict[str, Any] = {}
587
+ try:
588
+ cfg = proxmox.nodes(node).lxc(vmid_key).config.get() or {}
589
+ except Exception as exc: # pragma: no cover - best effort
590
+ logger.debug("Failed to read config for container %s: %s", vmid_key, exc)
591
+ description = (cfg.get("description") or "")
592
+ if MANAGED_MARKER in description:
593
+ continue
594
+ unmanaged.append(_build_unmanaged_container_entry(ct, cfg, vmid_key))
446
595
  except Exception as exc: # pragma: no cover - best effort
447
596
  logger.debug("Failed to refresh container statuses: %s", exc)
448
- return
449
- for record in records:
450
- vmid = record.get("vmid")
451
- if vmid is None:
452
- continue
453
- try:
454
- vmid_key = str(int(vmid))
455
- except (ValueError, TypeError):
456
- continue
457
- status = statuses.get(vmid_key)
458
- if status:
459
- record["status"] = status
597
+ return statuses, unmanaged, node_status
460
598
 
461
599
  now = time.monotonic()
462
600
  with _MANAGED_CONTAINERS_CACHE_LOCK:
@@ -466,8 +604,19 @@ def _get_managed_containers_summary(force: bool = False) -> Dict[str, Any]:
466
604
  return cached
467
605
  config = _load_config()
468
606
  records = _load_managed_container_records()
469
- _refresh_container_statuses(records, config)
470
- summary = _build_managed_containers_summary(records)
607
+ managed_vmids: Set[str] = {
608
+ str(_to_int(record.get("vmid"))) for record in records if record.get("vmid") is not None
609
+ }
610
+ statuses, unmanaged, node_status = _refresh_container_statuses(records, config, managed_vmids)
611
+ for record in records:
612
+ vmid = record.get("vmid")
613
+ if vmid is None:
614
+ continue
615
+ vmid_key = str(_to_int(vmid))
616
+ status = statuses.get(vmid_key)
617
+ if status:
618
+ record["status"] = status
619
+ summary = _build_managed_containers_summary(records, unmanaged, node_status)
471
620
  with _MANAGED_CONTAINERS_CACHE_LOCK:
472
621
  _MANAGED_CONTAINERS_CACHE["timestamp"] = now
473
622
  _MANAGED_CONTAINERS_CACHE["summary"] = summary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.16.dev2
3
+ Version: 1.4.16.dev4
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=s515CTO7U0276keYdIuPfegW6lNay9X4hqIIs7ajLn4,719
4
+ portacode/_version.py,sha256=hp5vdfqkrwByAm4WdqNsKpWfJgPUKIkE5hM-7_9gKU4,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
@@ -14,7 +14,7 @@ portacode/connection/client.py,sha256=jtLb9_YufqPkzi9t8VQH3iz_JEMisbtY6a8L9U5wei
14
14
  portacode/connection/multiplex.py,sha256=L-TxqJ_ZEbfNEfu1cwxgJ5vUdyRzZjsMy2Kx1diiZys,5237
15
15
  portacode/connection/terminal.py,sha256=n1Uu92JacV5K6d1Qwx94Tw9OB2Tpke5HqsW2NDn76Ls,49032
16
16
  portacode/connection/handlers/README.md,sha256=HsLZG1QK1JNm67HsgL6WoDg9nxzKXxwkc5fJPFJdX5g,12169
17
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=Dfkx6is1Xwt1PgCRqTKrP2zhfy3TmkmhSx9G29VMLB8,101054
17
+ portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=U7zkUpwscWf5aqimNIP3uYXvg-t9S0MhyaZ3ygF-ADk,102593
18
18
  portacode/connection/handlers/__init__.py,sha256=WSeBmi65GWFQPYt9M3E10rn0uZ_EPCJzNJOzSf2HZyw,2921
19
19
  portacode/connection/handlers/base.py,sha256=oENFb-Fcfzwk99Qx8gJQriEMiwSxwygwjOiuCH36hM4,10231
20
20
  portacode/connection/handlers/chunked_content.py,sha256=h6hXRmxSeOgnIxoU8CkmvEf2Odv-ajPrpHIe_W3GKcA,9251
@@ -22,7 +22,7 @@ portacode/connection/handlers/diff_handlers.py,sha256=iYTIRCcpEQ03vIPKZCsMTE5aZb
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=LEUj6jVyP73sD6Wp2-m3SAj0rkXQo_wpiXyyNdpg6Zo,72906
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=DQJX8-lENZxeAaC6pSw6AfXE_4E_q-63nCK7issIldU,78531
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=fr12QpOr_Z8KYGUU-AYrTQwRPAcrLK85hvj3SEq1Kw8,14757
@@ -65,7 +65,7 @@ portacode/utils/__init__.py,sha256=NgBlWTuNJESfIYJzP_3adI1yJQJR0XJLRpSdVNaBAN0,3
65
65
  portacode/utils/diff_apply.py,sha256=4Oi7ft3VUCKmiUE4VM-OeqO7Gk6H7PF3WnN4WHXtjxI,15157
66
66
  portacode/utils/diff_renderer.py,sha256=S76StnQ2DLfsz4Gg0m07UwPfRp8270PuzbNaQq-rmYk,13850
67
67
  portacode/utils/ntp_clock.py,sha256=VqCnWCTehCufE43W23oB-WUdAZGeCcLxkmIOPwInYHc,2499
68
- portacode-1.4.16.dev2.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.16.dev4.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
69
69
  test_modules/README.md,sha256=Do_agkm9WhSzueXjRAkV_xEj6Emy5zB3N3VKY5Roce8,9274
70
70
  test_modules/__init__.py,sha256=1LcbHodIHsB0g-g4NGjSn6AMuCoGbymvXPYLOb6Z7F0,53
71
71
  test_modules/test_device_online.py,sha256=QtYq0Dq9vME8Gp2O4fGSheqVf8LUtpsSKosXXk56gGM,1654
@@ -91,8 +91,8 @@ testing_framework/core/playwright_manager.py,sha256=Tw46qwxIhOFkS48C2IWIQHHNpEe-
91
91
  testing_framework/core/runner.py,sha256=j2QwNJmAxVBmJvcbVS7DgPJUKPNzqfLmt_4NNdaKmZU,19297
92
92
  testing_framework/core/shared_cli_manager.py,sha256=BESSNtyQb7BOlaOvZmm04T8Uezjms4KCBs2MzTxvzYQ,8790
93
93
  testing_framework/core/test_discovery.py,sha256=2FZ9fJ8Dp5dloA-fkgXoJ_gCMC_nYPBnA3Hs2xlagzM,4928
94
- portacode-1.4.16.dev2.dist-info/METADATA,sha256=J7EuYnoswnTNe3Qdhq_0fMvtZdHNqFvlXAtg4dfvDX8,13051
95
- portacode-1.4.16.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
- portacode-1.4.16.dev2.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
- portacode-1.4.16.dev2.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
- portacode-1.4.16.dev2.dist-info/RECORD,,
94
+ portacode-1.4.16.dev4.dist-info/METADATA,sha256=Ld82LaJqGIk-94oOxKNjvrJh_vCwvuadSBHkpr-FucQ,13051
95
+ portacode-1.4.16.dev4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
+ portacode-1.4.16.dev4.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.16.dev4.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.16.dev4.dist-info/RECORD,,