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 +2 -2
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +28 -16
- portacode/connection/handlers/proxmox_infra.py +193 -44
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/METADATA +1 -1
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/RECORD +9 -9
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/WHEEL +0 -0
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.16.dev2.dist-info → portacode-1.4.16.dev4.dist-info}/top_level.txt +0 -0
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 16, '
|
|
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
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
508
|
+
managed_containers.append(
|
|
411
509
|
{
|
|
412
|
-
"vmid": str(
|
|
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(
|
|
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":
|
|
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(
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
470
|
-
|
|
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,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=
|
|
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=
|
|
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=
|
|
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.
|
|
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.
|
|
95
|
-
portacode-1.4.16.
|
|
96
|
-
portacode-1.4.16.
|
|
97
|
-
portacode-1.4.16.
|
|
98
|
-
portacode-1.4.16.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|