portacode 1.4.14__py3-none-any.whl → 1.4.15.dev0__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 +6 -3
- portacode/connection/handlers/proxmox_infra.py +77 -23
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.dist-info}/METADATA +1 -1
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.dist-info}/RECORD +9 -9
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.dist-info}/WHEEL +0 -0
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.14.dist-info → portacode-1.4.15.dev0.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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4,
|
|
31
|
+
__version__ = version = '1.4.15.dev0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev0')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -326,9 +326,10 @@ Configures a Proxmox node for Portacode infrastructure usage (API token validati
|
|
|
326
326
|
|
|
327
327
|
**Payload Fields:**
|
|
328
328
|
|
|
329
|
-
* `token_identifier` (string,
|
|
330
|
-
* `token_value` (string,
|
|
331
|
-
* `verify_ssl` (boolean, optional): When true, the handler verifies SSL certificates; defaults to `false`.
|
|
329
|
+
* `token_identifier` (string, optional when reconfiguring): API token identifier in the form `user@realm!tokenid`. Required on first configuration or when replacing the stored token.
|
|
330
|
+
* `token_value` (string, optional when reconfiguring): Secret value associated with the token. Required when `token_identifier` is supplied.
|
|
331
|
+
* `verify_ssl` (boolean, optional): When true, the handler verifies SSL certificates; defaults to `false`. When omitted, the last configured value is preserved.
|
|
332
|
+
* `cloudflare_api_token` (string, optional): Cloudflare API token the host can reuse later to provision tunnels.
|
|
332
333
|
|
|
333
334
|
**Responses:**
|
|
334
335
|
|
|
@@ -1126,6 +1127,8 @@ Provides system information in response to a `system_info` action. Handled by [`
|
|
|
1126
1127
|
* `message` (string|null): Informational text about the network setup attempt.
|
|
1127
1128
|
* `bridge` (string): The bridge interface configured (typically `vmbr1`).
|
|
1128
1129
|
* `health` (string|null): `"healthy"` when the connectivity verification succeeded.
|
|
1130
|
+
* `cloudflare` (object): Optional Cloudflare metadata collected for future tunnels:
|
|
1131
|
+
* `configured` (boolean): True when a Cloudflare API token is stored.
|
|
1129
1132
|
* `node_status` (object|null): Status response returned by the Proxmox API when validating the token.
|
|
1130
1133
|
* `managed_containers` (object): Cached summary of the Portacode-managed containers:
|
|
1131
1134
|
* `updated_at` (string): ISO timestamp when this snapshot was last refreshed.
|
|
@@ -1005,6 +1005,12 @@ def _bootstrap_portacode(
|
|
|
1005
1005
|
return public_key, results
|
|
1006
1006
|
|
|
1007
1007
|
|
|
1008
|
+
def _build_cloudflare_snapshot(cloudflare_config: Dict[str, Any] | None) -> Dict[str, Any]:
|
|
1009
|
+
if not cloudflare_config:
|
|
1010
|
+
return {"configured": False}
|
|
1011
|
+
return {"configured": bool(cloudflare_config.get("api_token"))}
|
|
1012
|
+
|
|
1013
|
+
|
|
1008
1014
|
def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
1009
1015
|
network = config.get("network", {})
|
|
1010
1016
|
base_network = {
|
|
@@ -1012,8 +1018,13 @@ def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1012
1018
|
"message": network.get("message"),
|
|
1013
1019
|
"bridge": network.get("bridge", DEFAULT_BRIDGE),
|
|
1014
1020
|
}
|
|
1021
|
+
cloudflare_snapshot = _build_cloudflare_snapshot(config.get("cloudflare"))
|
|
1015
1022
|
if not config:
|
|
1016
|
-
return {
|
|
1023
|
+
return {
|
|
1024
|
+
"configured": False,
|
|
1025
|
+
"network": base_network,
|
|
1026
|
+
"cloudflare": cloudflare_snapshot,
|
|
1027
|
+
}
|
|
1017
1028
|
return {
|
|
1018
1029
|
"configured": True,
|
|
1019
1030
|
"host": config.get("host"),
|
|
@@ -1024,18 +1035,54 @@ def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1024
1035
|
"templates": config.get("templates") or [],
|
|
1025
1036
|
"last_verified": config.get("last_verified"),
|
|
1026
1037
|
"network": base_network,
|
|
1038
|
+
"cloudflare": cloudflare_snapshot,
|
|
1027
1039
|
}
|
|
1028
1040
|
|
|
1029
1041
|
|
|
1030
|
-
def
|
|
1042
|
+
def _resolve_proxmox_credentials(
|
|
1043
|
+
token_identifier: Optional[str],
|
|
1044
|
+
token_value: Optional[str],
|
|
1045
|
+
existing: Dict[str, Any],
|
|
1046
|
+
) -> Tuple[str, str, str]:
|
|
1047
|
+
if token_identifier:
|
|
1048
|
+
if not token_value:
|
|
1049
|
+
raise ValueError("token_value is required when providing a new token_identifier")
|
|
1050
|
+
user, token_name = _parse_token(token_identifier)
|
|
1051
|
+
return user, token_name, token_value
|
|
1052
|
+
if existing and existing.get("user") and existing.get("token_name") and existing.get("token_value"):
|
|
1053
|
+
return existing["user"], existing["token_name"], existing["token_value"]
|
|
1054
|
+
raise ValueError("Proxmox token identifier and value are required when no existing configuration is available")
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
def _build_cloudflare_config(existing: Dict[str, Any], api_token: Optional[str]) -> Dict[str, Any]:
|
|
1058
|
+
cloudflare: Dict[str, Any] = dict(existing.get("cloudflare", {}) or {})
|
|
1059
|
+
if api_token:
|
|
1060
|
+
cloudflare["api_token"] = api_token
|
|
1061
|
+
if cloudflare.get("api_token"):
|
|
1062
|
+
cloudflare["configured"] = True
|
|
1063
|
+
elif "configured" in cloudflare:
|
|
1064
|
+
cloudflare.pop("configured", None)
|
|
1065
|
+
return cloudflare
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def configure_infrastructure(
|
|
1069
|
+
token_identifier: Optional[str] = None,
|
|
1070
|
+
token_value: Optional[str] = None,
|
|
1071
|
+
verify_ssl: Optional[bool] = None,
|
|
1072
|
+
cloudflare_api_token: Optional[str] = None,
|
|
1073
|
+
) -> Dict[str, Any]:
|
|
1031
1074
|
ProxmoxAPI = _ensure_proxmoxer()
|
|
1032
|
-
|
|
1075
|
+
existing = _load_config()
|
|
1076
|
+
user, token_name, resolved_token_value = _resolve_proxmox_credentials(
|
|
1077
|
+
token_identifier, token_value, existing
|
|
1078
|
+
)
|
|
1079
|
+
actual_verify_ssl = verify_ssl if verify_ssl is not None else existing.get("verify_ssl", False)
|
|
1033
1080
|
client = ProxmoxAPI(
|
|
1034
1081
|
DEFAULT_HOST,
|
|
1035
1082
|
user=user,
|
|
1036
1083
|
token_name=token_name,
|
|
1037
|
-
token_value=
|
|
1038
|
-
verify_ssl=
|
|
1084
|
+
token_value=resolved_token_value,
|
|
1085
|
+
verify_ssl=actual_verify_ssl,
|
|
1039
1086
|
timeout=30,
|
|
1040
1087
|
)
|
|
1041
1088
|
node = _pick_node(client)
|
|
@@ -1043,31 +1090,35 @@ def configure_infrastructure(token_identifier: str, token_value: str, verify_ssl
|
|
|
1043
1090
|
storages = client.nodes(node).storage.get()
|
|
1044
1091
|
default_storage = _pick_storage(storages)
|
|
1045
1092
|
templates = _list_templates(client, node, storages)
|
|
1046
|
-
network
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1093
|
+
network = dict(existing.get("network", {}) or {})
|
|
1094
|
+
if not network.get("applied"):
|
|
1095
|
+
try:
|
|
1096
|
+
network = _ensure_bridge()
|
|
1097
|
+
# Wait for network convergence before validating connectivity
|
|
1098
|
+
time.sleep(2)
|
|
1099
|
+
if not _verify_connectivity():
|
|
1100
|
+
raise RuntimeError("Connectivity check failed; bridge reverted")
|
|
1101
|
+
network["health"] = "healthy"
|
|
1102
|
+
except Exception as exc:
|
|
1103
|
+
logger.warning("Bridge setup failed; reverting previous changes: %s", exc)
|
|
1104
|
+
_revert_bridge()
|
|
1105
|
+
raise
|
|
1058
1106
|
config = {
|
|
1059
1107
|
"host": DEFAULT_HOST,
|
|
1060
1108
|
"node": node,
|
|
1061
1109
|
"user": user,
|
|
1062
1110
|
"token_name": token_name,
|
|
1063
|
-
"token_value":
|
|
1064
|
-
"verify_ssl":
|
|
1111
|
+
"token_value": resolved_token_value,
|
|
1112
|
+
"verify_ssl": actual_verify_ssl,
|
|
1065
1113
|
"default_storage": default_storage,
|
|
1066
1114
|
"templates": templates,
|
|
1067
1115
|
"last_verified": datetime.utcnow().isoformat() + "Z",
|
|
1068
1116
|
"network": network,
|
|
1069
1117
|
"node_status": status,
|
|
1070
1118
|
}
|
|
1119
|
+
cloudflare = _build_cloudflare_config(existing, cloudflare_api_token)
|
|
1120
|
+
if cloudflare:
|
|
1121
|
+
config["cloudflare"] = cloudflare
|
|
1071
1122
|
_save_config(config)
|
|
1072
1123
|
snapshot = build_snapshot(config)
|
|
1073
1124
|
snapshot["node_status"] = status
|
|
@@ -1643,10 +1694,13 @@ class ConfigureProxmoxInfraHandler(SyncHandler):
|
|
|
1643
1694
|
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
1644
1695
|
token_identifier = message.get("token_identifier")
|
|
1645
1696
|
token_value = message.get("token_value")
|
|
1646
|
-
verify_ssl =
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1697
|
+
verify_ssl = message.get("verify_ssl")
|
|
1698
|
+
snapshot = configure_infrastructure(
|
|
1699
|
+
token_identifier=token_identifier,
|
|
1700
|
+
token_value=token_value,
|
|
1701
|
+
verify_ssl=verify_ssl,
|
|
1702
|
+
cloudflare_api_token=message.get("cloudflare_api_token"),
|
|
1703
|
+
)
|
|
1650
1704
|
return {
|
|
1651
1705
|
"event": "proxmox_infra_configured",
|
|
1652
1706
|
"success": True,
|
|
@@ -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=ZC_kn1NEQ9Aet-DPiqGe5ZTNbDk0ZOpeb6OhHRUx77M,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=oyLPOVLPlUuN_eRvHPGazB51yi8W8JEF3oOEYxucGTE,45069
|
|
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=bW3coRPyolXX8GIFQXfOyonxg2ad7UOfmMenN23_5FQ,99509
|
|
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=K9s0e9lb97I9_kNmnpoJMpoO1uTrl2ef9KTUwIbiF-g,63287
|
|
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.
|
|
68
|
+
portacode-1.4.15.dev0.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.
|
|
95
|
-
portacode-1.4.
|
|
96
|
-
portacode-1.4.
|
|
97
|
-
portacode-1.4.
|
|
98
|
-
portacode-1.4.
|
|
94
|
+
portacode-1.4.15.dev0.dist-info/METADATA,sha256=R7FEzw07kw5YY1i3Sj0-W88K5pOVHVwMYGPE1kVDduA,13051
|
|
95
|
+
portacode-1.4.15.dev0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
96
|
+
portacode-1.4.15.dev0.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.15.dev0.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.15.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|