Защита Bridge и камеры K1 при переподключении
This commit is contained in:
@@ -1076,6 +1076,10 @@ class ConnectionVerifyRequest(StrictRequest):
|
||||
)
|
||||
compatibility_attestation: CompatibilityAttestationRequest | None = None
|
||||
operation_id: str | None = Field(default=None, min_length=1, max_length=128)
|
||||
# New clients bind Verify to the exact mode draft rendered at the click.
|
||||
# Optionality keeps the legacy server-resolved `{}` request and older
|
||||
# local clients readable; mode equality remains mandatory either way.
|
||||
expected_mode_revision: int | None = Field(default=None, ge=0)
|
||||
expected_discovery_generation: int | None = Field(default=None, ge=0)
|
||||
expected_reconfiguration_revision: int | None = Field(default=None, ge=0)
|
||||
expected_reconfiguration_intent_id: str | None = Field(
|
||||
@@ -8236,7 +8240,10 @@ class XgridsK1CompatibilityService:
|
||||
active_profile_id,
|
||||
device_session_id,
|
||||
camera_preview,
|
||||
activation_admitted=camera_activation_admitted,
|
||||
# Automatic acquisition camera ownership is deliberately not
|
||||
# manual preview authority. The selected delivery remains
|
||||
# visible, but source controls cannot detach evidence.
|
||||
activation_admitted=(camera_activation_admitted and not acquisition_active),
|
||||
),
|
||||
"device_calibration": device_calibration,
|
||||
"camera_preview": camera_preview,
|
||||
@@ -9400,6 +9407,22 @@ class XgridsK1CompatibilityService:
|
||||
runtime = self.runtime.snapshot()
|
||||
control = self._application_control_session.snapshot()
|
||||
physical = self._physical_command_coordinator.snapshot()
|
||||
physical_recovery_requires_explicit_reset = bool(
|
||||
physical.get("resolved_unclassified_stop_recovery_required")
|
||||
is True
|
||||
)
|
||||
if physical_recovery_requires_explicit_reset:
|
||||
# A recovery card is bound to the mode recorded by the
|
||||
# physical ledger. An old browser must never turn its
|
||||
# action into a hidden mode change; only the visible
|
||||
# scenario-reset control may retire that ownership.
|
||||
raise NetworkProvisioningConflict(
|
||||
"незавершённое физическое состояние K1 требует явного "
|
||||
"сброса сценария перед сменой способа подключения",
|
||||
reason_code=(
|
||||
"connection-mode-selection-physical-recovery-reset-required"
|
||||
),
|
||||
)
|
||||
acquisition_state = acquisition.state if acquisition is not None else None
|
||||
selection_reasons = _connection_mode_selection_reason_codes(
|
||||
acquisition_state=acquisition_state,
|
||||
@@ -9694,6 +9717,39 @@ class XgridsK1CompatibilityService:
|
||||
reason_code="network-provision-discovery-generation-conflict",
|
||||
)
|
||||
|
||||
def _require_connection_mode_draft_for_verify(
|
||||
self,
|
||||
requested_mode: ConnectionMode | None,
|
||||
*,
|
||||
expected_mode_revision: int | None,
|
||||
) -> None:
|
||||
"""Fence Verify before BLE, host Wi-Fi, MQTT or topology mutation."""
|
||||
|
||||
with self._lock:
|
||||
desired = self._desired_connection_mode
|
||||
revision = self._desired_connection_mode_revision
|
||||
scenario_reset_pending = self._connection_scenario_reset_pending is not None
|
||||
if scenario_reset_pending:
|
||||
raise ConnectionVerificationError(
|
||||
"сначала завершается явная смена сценария подключения",
|
||||
reason_code="connection-mode-switch-pending",
|
||||
)
|
||||
if requested_mode is None:
|
||||
raise ConnectionVerificationError(
|
||||
"Read-only проверка K1 не получила точный способ подключения",
|
||||
reason_code="connection-verify-connection-missing",
|
||||
)
|
||||
if expected_mode_revision is not None and expected_mode_revision != revision:
|
||||
raise ConnectionVerificationError(
|
||||
"способ подключения изменился до начала проверки",
|
||||
reason_code="connection-mode-draft-revision-conflict",
|
||||
)
|
||||
if requested_mode != desired:
|
||||
raise ConnectionVerificationError(
|
||||
"проверяемое подключение не совпадает с выбранным способом",
|
||||
reason_code="connection-mode-draft-mismatch",
|
||||
)
|
||||
|
||||
def _require_current_connection_reconfiguration_target(
|
||||
self,
|
||||
*,
|
||||
@@ -15700,6 +15756,10 @@ class XgridsK1CompatibilityService:
|
||||
else None
|
||||
)
|
||||
try:
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
cast(ConnectionMode | None, requested_mode),
|
||||
expected_mode_revision=request.expected_mode_revision,
|
||||
)
|
||||
self._require_current_connection_reconfiguration_target(
|
||||
expected_revision=request.expected_reconfiguration_revision,
|
||||
expected_intent_id=request.expected_reconfiguration_intent_id,
|
||||
@@ -15746,6 +15806,10 @@ class XgridsK1CompatibilityService:
|
||||
"фоновая проверка подключения не завершилась вовремя",
|
||||
reason_code="connection-verify-lifecycle-busy",
|
||||
)
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
cast(ConnectionMode | None, requested_mode),
|
||||
expected_mode_revision=request.expected_mode_revision,
|
||||
)
|
||||
self._require_current_connection_reconfiguration_target(
|
||||
expected_revision=request.expected_reconfiguration_revision,
|
||||
expected_intent_id=request.expected_reconfiguration_intent_id,
|
||||
@@ -17944,6 +18008,7 @@ class XgridsK1CompatibilityService:
|
||||
if isinstance(item.get("device_id"), str) and str(item.get("device_id")).strip()
|
||||
}
|
||||
discovery_generation = self._ble_discovery_generation
|
||||
desired_mode_revision = self._desired_connection_mode_revision
|
||||
selected_device_id = self._selected_device_id
|
||||
selected_connection_mode = self._connection_mode
|
||||
selected_device_session_id = self._device_session_id
|
||||
@@ -18026,6 +18091,11 @@ class XgridsK1CompatibilityService:
|
||||
verification="live-device-info",
|
||||
),
|
||||
operation_id=request.operation_id,
|
||||
expected_mode_revision=(
|
||||
request.expected_mode_revision
|
||||
if request.expected_mode_revision is not None
|
||||
else desired_mode_revision
|
||||
),
|
||||
expected_discovery_generation=(
|
||||
discovery_generation if source == "fresh-scan" else None
|
||||
),
|
||||
@@ -18054,7 +18124,11 @@ class XgridsK1CompatibilityService:
|
||||
"direct-lan": "bridge",
|
||||
"device-ap": "quick-connect",
|
||||
"controller-hotspot": "direct-connect",
|
||||
}[request.compatibility_attestation.topology],
|
||||
}[request.compatibility_attestation.topology],
|
||||
)
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
requested_mode,
|
||||
expected_mode_revision=request.expected_mode_revision,
|
||||
)
|
||||
self._require_current_connection_reconfiguration_target(
|
||||
expected_revision=request.expected_reconfiguration_revision,
|
||||
@@ -18085,6 +18159,7 @@ class XgridsK1CompatibilityService:
|
||||
{
|
||||
"device_id": request.device_id,
|
||||
"source": request.source,
|
||||
"expected_mode_revision": request.expected_mode_revision,
|
||||
"expected_discovery_generation": (request.expected_discovery_generation),
|
||||
"expected_reconfiguration_revision": (request.expected_reconfiguration_revision),
|
||||
"expected_reconfiguration_intent_id": (request.expected_reconfiguration_intent_id),
|
||||
@@ -18222,6 +18297,7 @@ class XgridsK1CompatibilityService:
|
||||
request.compatibility_attestation,
|
||||
verification_source=request.source,
|
||||
expected_discovery_generation=(request.expected_discovery_generation),
|
||||
expected_mode_revision=request.expected_mode_revision,
|
||||
require_live_gatt_validation=(physical_recovery_target is not None),
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
@@ -18339,9 +18415,14 @@ class XgridsK1CompatibilityService:
|
||||
*,
|
||||
transport_ref: str,
|
||||
target_ipv4: str,
|
||||
expected_mode_revision: int | None,
|
||||
) -> _SavedQuickConnectHostAssociation:
|
||||
"""Restore the controller-side AP route without touching K1 over BLE."""
|
||||
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
"quick-connect",
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
binding = _recover_durable_quick_connect_host_binding(
|
||||
self.evidence_root,
|
||||
transport_ref=transport_ref,
|
||||
@@ -18401,6 +18482,13 @@ class XgridsK1CompatibilityService:
|
||||
)
|
||||
try:
|
||||
if association_performed:
|
||||
# Re-sample immediately before the only host-network mutation.
|
||||
# A queued scenario reset or stale UI revision wins without
|
||||
# invoking CoreWLAN.
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
"quick-connect",
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
association = await _run_blocking_operation_without_abandonment(
|
||||
associate_with_wifi_profile_once,
|
||||
self.repository_root
|
||||
@@ -18504,6 +18592,109 @@ class XgridsK1CompatibilityService:
|
||||
evidence_session_dir=session_dir,
|
||||
)
|
||||
|
||||
async def _restore_saved_quick_connect_host_path(
|
||||
self,
|
||||
*,
|
||||
transport_ref: str,
|
||||
target_ipv4: str,
|
||||
ble_operation_performed: bool,
|
||||
expected_mode_revision: int | None,
|
||||
) -> tuple[
|
||||
_SavedQuickConnectHostAssociation,
|
||||
_ConfiguredEndpointHostObservation,
|
||||
int,
|
||||
]:
|
||||
"""Restore one exact saved AP route and prove its sole MQTT endpoint."""
|
||||
|
||||
association = await self._associate_saved_quick_connect_host(
|
||||
transport_ref=transport_ref,
|
||||
target_ipv4=target_ipv4,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
supervisor_epoch_before = self._connection_supervisor.snapshot().host_path.epoch
|
||||
try:
|
||||
observation = await _run_blocking_operation_without_abandonment(
|
||||
_probe_quick_connect_endpoint_after_route_settle,
|
||||
target_ipv4,
|
||||
path_probe=lambda target: self._sample_host_path(
|
||||
target,
|
||||
association_timeout_seconds=(
|
||||
CONNECTION_MONITOR_ASSOCIATION_TIMEOUT_SECONDS
|
||||
),
|
||||
),
|
||||
settle_timeout_seconds=(
|
||||
DURABLE_QUICK_CONNECT_ROUTE_SETTLE_TIMEOUT_SECONDS
|
||||
),
|
||||
settle_interval_seconds=(
|
||||
DURABLE_QUICK_CONNECT_ROUTE_SETTLE_INTERVAL_SECONDS
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
_write_quick_connect_host_network_proof(
|
||||
association,
|
||||
target_ipv4=target_ipv4,
|
||||
observation=None,
|
||||
outcome="probe-failed",
|
||||
reason_code=(
|
||||
getattr(exc, "reason_code", None) or "host-network-proof-failed"
|
||||
),
|
||||
supervisor_host_path_epoch_before_admission=supervisor_epoch_before,
|
||||
ble_operation_performed=ble_operation_performed,
|
||||
)
|
||||
raise
|
||||
if observation.reason_code == "host-route-changed-during-tcp-probe":
|
||||
_write_quick_connect_host_network_proof(
|
||||
association,
|
||||
target_ipv4=target_ipv4,
|
||||
observation=observation,
|
||||
outcome="route-changed",
|
||||
reason_code="connection-verify-lease-changed",
|
||||
supervisor_host_path_epoch_before_admission=supervisor_epoch_before,
|
||||
ble_operation_performed=ble_operation_performed,
|
||||
)
|
||||
raise ConnectionVerificationError(
|
||||
"Маршрут к K1 изменился во время Quick Connect проверки",
|
||||
reason_code="connection-verify-lease-changed",
|
||||
)
|
||||
if not observation.path.available or observation.path.route_class != "direct":
|
||||
_write_quick_connect_host_network_proof(
|
||||
association,
|
||||
target_ipv4=target_ipv4,
|
||||
observation=observation,
|
||||
outcome="route-rejected",
|
||||
reason_code="connection-verify-route-mismatch",
|
||||
supervisor_host_path_epoch_before_admission=supervisor_epoch_before,
|
||||
ble_operation_performed=ble_operation_performed,
|
||||
)
|
||||
raise ConnectionVerificationError(
|
||||
"После подключения Quick Connect прямой маршрут к K1 не подтверждён",
|
||||
reason_code="connection-verify-route-mismatch",
|
||||
)
|
||||
if not observation.reachable:
|
||||
_write_quick_connect_host_network_proof(
|
||||
association,
|
||||
target_ipv4=target_ipv4,
|
||||
observation=observation,
|
||||
outcome="endpoint-unreachable",
|
||||
reason_code="connection-verify-mqtt-unreachable",
|
||||
supervisor_host_path_epoch_before_admission=supervisor_epoch_before,
|
||||
ble_operation_performed=ble_operation_performed,
|
||||
)
|
||||
raise ConnectionVerificationError(
|
||||
"Прямой маршрут Quick Connect подтверждён, но MQTT endpoint K1 недоступен",
|
||||
reason_code="connection-verify-mqtt-unreachable",
|
||||
)
|
||||
_write_quick_connect_host_network_proof(
|
||||
association,
|
||||
target_ipv4=target_ipv4,
|
||||
observation=observation,
|
||||
outcome="direct-endpoint-reachable",
|
||||
reason_code=None,
|
||||
supervisor_host_path_epoch_before_admission=supervisor_epoch_before,
|
||||
ble_operation_performed=ble_operation_performed,
|
||||
)
|
||||
return association, observation, supervisor_epoch_before
|
||||
|
||||
async def _adopt_existing_lan_connection(
|
||||
self,
|
||||
device_id: str,
|
||||
@@ -18513,6 +18704,7 @@ class XgridsK1CompatibilityService:
|
||||
"fresh-scan", "retained-current-process", "durable-configured-state"
|
||||
] = "fresh-scan",
|
||||
expected_discovery_generation: int | None = None,
|
||||
expected_mode_revision: int | None = None,
|
||||
require_live_gatt_validation: bool = False,
|
||||
) -> tuple[str, _ProvisionalFreshBridgeTopology | None]:
|
||||
"""Create a process lease from live or persisted device topology evidence."""
|
||||
@@ -18526,6 +18718,10 @@ class XgridsK1CompatibilityService:
|
||||
requested_mode = "direct-connect"
|
||||
else:
|
||||
raise ValueError("read-only K1 adoption received an unsupported topology")
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
requested_mode,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
with self._lock:
|
||||
scanned_device = next(
|
||||
(
|
||||
@@ -18877,6 +19073,10 @@ class XgridsK1CompatibilityService:
|
||||
# Publish the BLE verification fence before awaiting native I/O.
|
||||
# A scan cannot invalidate a capture or its validation handoff in
|
||||
# the post-GATT/pre-pin window.
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
requested_mode,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
with self._lock:
|
||||
if self._provisioning_active:
|
||||
raise RuntimeError("настройка Wi-Fi началась во время проверки сети")
|
||||
@@ -18897,9 +19097,14 @@ class XgridsK1CompatibilityService:
|
||||
quick_host_association: _SavedQuickConnectHostAssociation | None = None
|
||||
quick_supervisor_epoch_before: int | None = None
|
||||
if requested_mode == "quick-connect":
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
requested_mode,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
quick_host_association = await self._associate_saved_quick_connect_host(
|
||||
transport_ref=actual_transport_ref,
|
||||
target_ipv4=semantic_record.ipv4,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
quick_supervisor_epoch_before = (
|
||||
self._connection_supervisor.snapshot().host_path.epoch
|
||||
@@ -19132,6 +19337,10 @@ class XgridsK1CompatibilityService:
|
||||
transport_source == "durable-state"
|
||||
and require_live_gatt_validation
|
||||
)
|
||||
self._require_connection_mode_draft_for_verify(
|
||||
requested_mode,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
status_read = await read_wifi_status_once(
|
||||
actual_transport_ref,
|
||||
timeout_seconds=20.0,
|
||||
@@ -19372,6 +19581,38 @@ class XgridsK1CompatibilityService:
|
||||
)
|
||||
if retained_after_read["status"] != "retained":
|
||||
raise RuntimeError("process recovery token изменился после GATT validation")
|
||||
live_quick_host_association: _SavedQuickConnectHostAssociation | None = None
|
||||
live_quick_endpoint_observation: _ConfiguredEndpointHostObservation | None = None
|
||||
live_quick_supervisor_epoch_before: int | None = None
|
||||
if requested_mode == "quick-connect" and require_live_gatt_validation:
|
||||
exact_saved_quick_topology = bool(
|
||||
expected_reconciliation is None
|
||||
and semantic_record is not None
|
||||
and physical_transport_ref_comparison_key(semantic_record.transport_ref)
|
||||
== physical_transport_ref_comparison_key(actual_transport_ref)
|
||||
and semantic_record.connection_mode == "quick-connect"
|
||||
and semantic_record.ipv4 == target
|
||||
and semantic_record.compatibility_profile_id
|
||||
== XGRIDS_K1_COMPATIBILITY_PROFILE_ID
|
||||
and semantic_record.firmware_version
|
||||
== compatibility_attestation.firmware_version
|
||||
)
|
||||
if not exact_saved_quick_topology:
|
||||
raise ConnectionVerificationError(
|
||||
"Live Quick Connect recovery не имеет точной сохранённой topology "
|
||||
"этого K1; подключение Wi-Fi Mac не выполнялось",
|
||||
reason_code="connection-verify-quick-host-binding-unavailable",
|
||||
)
|
||||
(
|
||||
live_quick_host_association,
|
||||
live_quick_endpoint_observation,
|
||||
live_quick_supervisor_epoch_before,
|
||||
) = await self._restore_saved_quick_connect_host_path(
|
||||
transport_ref=actual_transport_ref,
|
||||
target_ipv4=target,
|
||||
ble_operation_performed=True,
|
||||
expected_mode_revision=expected_mode_revision,
|
||||
)
|
||||
defer_fresh_bridge_semantic_commit = bool(
|
||||
verification_source == "fresh-scan"
|
||||
and requested_mode == "bridge"
|
||||
@@ -19538,11 +19779,17 @@ class XgridsK1CompatibilityService:
|
||||
raise RuntimeError(
|
||||
"K1 сообщил адрес другой сети; прямой локальный маршрут отсутствует"
|
||||
)
|
||||
control_endpoint_observation = await _run_blocking_operation_without_abandonment(
|
||||
self._probe_control_endpoint,
|
||||
target,
|
||||
association_timeout_seconds=COMMAND_BOUND_ASSOCIATION_TIMEOUT_SECONDS,
|
||||
)
|
||||
control_endpoint_observation = live_quick_endpoint_observation
|
||||
if control_endpoint_observation is None:
|
||||
control_endpoint_observation = (
|
||||
await _run_blocking_operation_without_abandonment(
|
||||
self._probe_control_endpoint,
|
||||
target,
|
||||
association_timeout_seconds=(
|
||||
COMMAND_BOUND_ASSOCIATION_TIMEOUT_SECONDS
|
||||
),
|
||||
)
|
||||
)
|
||||
if not control_endpoint_observation.reachable:
|
||||
self._update_applied_topology_reachability(
|
||||
connection_mode=requested_mode,
|
||||
@@ -19634,6 +19881,21 @@ class XgridsK1CompatibilityService:
|
||||
path=control_endpoint_observation.path,
|
||||
reachable=True,
|
||||
)
|
||||
if live_quick_host_association is not None:
|
||||
_write_quick_connect_host_network_proof(
|
||||
live_quick_host_association,
|
||||
target_ipv4=target,
|
||||
observation=control_endpoint_observation,
|
||||
outcome="direct-endpoint-reachable",
|
||||
reason_code=None,
|
||||
supervisor_host_path_epoch_before_admission=(
|
||||
live_quick_supervisor_epoch_before
|
||||
),
|
||||
supervisor_host_path_epoch_after_admission=(
|
||||
self._connection_supervisor.snapshot().host_path.epoch
|
||||
),
|
||||
ble_operation_performed=True,
|
||||
)
|
||||
logger.info(
|
||||
"K1 existing direct-LAN connection adopted without provisioning",
|
||||
extra={
|
||||
@@ -22906,6 +23168,7 @@ class XgridsK1CompatibilityService:
|
||||
def select_camera_preview(self, request: CameraPreviewSelectRequest) -> dict[str, Any]:
|
||||
camera_holder_acquired = False
|
||||
try:
|
||||
self._require_camera_device_session(request.device_session_id)
|
||||
with self._lock:
|
||||
current_acquisition = self._acquisition
|
||||
current_out_dir = self._acquisition_out_dir
|
||||
@@ -22941,6 +23204,17 @@ class XgridsK1CompatibilityService:
|
||||
raise RuntimeError(
|
||||
"camera preview доступен после автоматического запуска правой камеры"
|
||||
)
|
||||
if acquisition_active_before_select:
|
||||
if request.source_id != DEFAULT_ACQUISITION_CAMERA_SOURCE:
|
||||
raise LocalAcquisitionLifecycleError(
|
||||
"Во время активного приёма правая камера принадлежит evidence-сессии; "
|
||||
"переключение видеоканала недоступно до завершения приёма.",
|
||||
reason_code="acquisition-camera-evidence-owned",
|
||||
)
|
||||
# The mandatory right camera is already selected and bound to
|
||||
# this acquisition. Re-selecting it is a read-only no-op: a UI
|
||||
# retry must never detach or replace the evidence producer.
|
||||
return self.state()
|
||||
camera_holder_acquired = self._ensure_camera_preview_process_lease()
|
||||
target = self._camera_target_for_session(request.device_session_id)
|
||||
with self._lock:
|
||||
@@ -22975,6 +23249,18 @@ class XgridsK1CompatibilityService:
|
||||
@_serialized_k1_transition_access
|
||||
def stop_camera_preview(self, request: CameraPreviewStopRequest) -> dict[str, Any]:
|
||||
self._require_camera_device_session(request.device_session_id)
|
||||
with self._lock:
|
||||
acquisition = self._acquisition
|
||||
acquisition_active = bool(
|
||||
acquisition is not None
|
||||
and acquisition.state not in TERMINAL_ACQUISITION_STATES
|
||||
)
|
||||
if acquisition_active:
|
||||
raise LocalAcquisitionLifecycleError(
|
||||
"Во время активного приёма правая камера принадлежит evidence-сессии; "
|
||||
"остановите весь приём штатной командой STOP.",
|
||||
reason_code="acquisition-camera-evidence-owned",
|
||||
)
|
||||
self.camera_preview.stop(request.generation)
|
||||
self._release_camera_preview_process_lease()
|
||||
return self.state()
|
||||
@@ -31644,6 +31930,7 @@ def _plugin_execution_http_status(reason_code: object) -> int:
|
||||
"connection-mode-switch-lifecycle-busy",
|
||||
"connection-mode-switch-acquisition-changed",
|
||||
"connection-mode-selection-lifecycle-busy",
|
||||
"connection-mode-selection-physical-recovery-reset-required",
|
||||
"connection-mode-selection-physical-state-unsafe",
|
||||
"connection-mode-selection-control-state-unsafe",
|
||||
"acquisition-start-lifecycle-busy",
|
||||
@@ -35098,6 +35385,7 @@ def _write_quick_connect_host_network_proof(
|
||||
reason_code: str | None,
|
||||
supervisor_host_path_epoch_before_admission: int | None,
|
||||
supervisor_host_path_epoch_after_admission: int | None = None,
|
||||
ble_operation_performed: bool = False,
|
||||
) -> None:
|
||||
"""Persist one redacted CoreWLAN -> route -> TCP proof without K1 mutation."""
|
||||
|
||||
@@ -35161,7 +35449,7 @@ def _write_quick_connect_host_network_proof(
|
||||
"supervisor_host_path_epoch_after_admission": (
|
||||
supervisor_host_path_epoch_after_admission
|
||||
),
|
||||
"ble_operation_performed": False,
|
||||
"ble_operation_performed": ble_operation_performed,
|
||||
"device_write_performed": False,
|
||||
"automatic_retry": False,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user