bump pymd3

This commit is contained in:
2026-04-04 22:30:27 -04:00
parent 94cb2441e2
commit 5dbbeb3394
4 changed files with 137 additions and 112 deletions

Binary file not shown.

View File

@@ -11,7 +11,7 @@ dependencies = [
"numpy==2.4.3",
"pydantic==2.12.5",
"pyicloud>=2.4.1",
"pymobiledevice3==9.0.0",
"pymobiledevice3==9.8.1",
"python-dotenv>=1.2.2",
"python-socketio==5.16.1",
"sqlalchemy>=2.0.48",

View File

@@ -11,16 +11,17 @@ import random
import math
import socketio
import httpx
from contextlib import asynccontextmanager, suppress
from contextlib import suppress
from typing import Optional, Dict
from dotenv import load_dotenv
with warnings.catch_warnings():
# Ignore: "Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater."
warnings.simplefilter("ignore", category=UserWarning)
warnings.simplefilter("ignore", category=UserWarning)
import fastapi
import uvicorn
from fastapi import FastAPI, APIRouter, Request
from fastapi import FastAPI, APIRouter, Request, Response
from fastapi.encoders import jsonable_encoder
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
@@ -140,16 +141,6 @@ class TunneldRunnerSio:
usbmux_monitor: bool = True,
mobdev2_monitor: bool = True,
) -> None:
# instance = cls(
# host,
# port,
# protocol=protocol,
# usb_monitor=usb_monitor,
# wifi_monitor=wifi_monitor,
# usbmux_monitor=usbmux_monitor,
# mobdev2_monitor=mobdev2_monitor,
# context=context,)
# asyncio.run(instance._run_app())
cls(
host,
port,
@@ -158,7 +149,8 @@ class TunneldRunnerSio:
wifi_monitor=wifi_monitor,
usbmux_monitor=usbmux_monitor,
mobdev2_monitor=mobdev2_monitor,
context=context)._run_app()
context=context,
)._run_app()
def __init__(
self,
@@ -171,11 +163,12 @@ class TunneldRunnerSio:
usbmux_monitor: bool = True,
mobdev2_monitor: bool = True,
):
@asynccontextmanager
async def lifespan(app: FastAPI):
async def app_startup() -> None:
logger.info("Application startup: starting tunneld core and watcher")
self._tunneld_core.start()
await start_tunnel_watcher()
yield
async def app_shutdown() -> None:
logger.info("Closing tunneld tasks...")
await end_tunnel_watcher()
await end_icloud_monitor()
@@ -195,10 +188,14 @@ class TunneldRunnerSio:
self._vue_dist = os.getenv("VUE_DIST")
self._vue_app = FastAPI(
title="iOS Device Management API",
lifespan=lifespan,
cors_allowed_origins="*",
)
self._asgi_app = socketio.ASGIApp(self.context.sio, self._vue_app)
self._asgi_app = socketio.ASGIApp(
self.context.sio,
self._vue_app,
on_startup=app_startup,
on_shutdown=app_shutdown,
)
self.context.icloud_monitor.sio = self.context.sio
self.context.icloud_monitor.get_client_sids = lambda: list(
self.context.connected_clients
@@ -347,6 +344,7 @@ class TunneldRunnerSio:
async def tunnel_watcher_loop() -> None:
previous = collect_active_tunnels()
while True:
try:
await asyncio.sleep(1)
current = collect_active_tunnels()
added_keys = set(current.keys()) - set(previous.keys())
@@ -354,6 +352,7 @@ class TunneldRunnerSio:
added = [current[k] for k in sorted(added_keys)]
removed = [previous[k] for k in sorted(removed_keys)]
for item in added:
try:
logger.info(
"Tunnel discovered interface=%s udid=%s address=%s port=%s transport=%s",
item.get("interface"),
@@ -368,25 +367,33 @@ class TunneldRunnerSio:
if self.context.udid is None:
self.context.udid = item.get("udid")
device_name = await get_device_name()
if device_name != self.context.device_name:
selected_device_name = self.context.device_name
if selected_device_name and device_name != selected_device_name:
wrong_udid = self.context.udid
self._tunneld_core.cancel(udid=item.get("udid"))
logger.warning(
"Tunnel established to wrong device. Dropping tunnel. wrong_udid=%s for device: %s",
self.context.udid, self.context.device_name)
wrong_udid, selected_device_name)
self.context.udid = None
await safe_sio_emit(
"appError",
{
"type": "tunnel_wrong_device",
"message": "Tunnel established to wrong device. Dropping tunnel.",
"wrong_udid": self.context.udid,
"wrong_udid": wrong_udid,
"device_name": device_name,
},
)
await get_tun(item.get("udid"), max_retries=10, retry_delay=1.0)
except Exception:
logger.exception(
"Tunnel watcher failed while handling added tunnel for udid=%s",
item.get("udid"),
)
if removed:
for item in removed:
try:
logger.warning(
"Tunnel disconnected interface=%s udid=%s address=%s port=%s",
item.get("interface"),
@@ -395,12 +402,26 @@ class TunneldRunnerSio:
item.get("port"),
)
await safe_sio_emit("tunnel_device_disconnected", item)
except Exception:
logger.exception(
"Tunnel watcher failed while emitting disconnection for udid=%s",
item.get("udid"),
)
await handle_tunnel_drop(removed)
previous = current
except asyncio.CancelledError:
raise
except Exception:
logger.exception("Tunnel watcher loop crashed; continuing")
async def start_tunnel_watcher() -> None:
if self.context.tunnel_watcher_task is None or self.context.tunnel_watcher_task.done():
if self.context.tunnel_watcher_task is not None and self.context.tunnel_watcher_task.done():
with suppress(Exception):
exc = self.context.tunnel_watcher_task.exception()
if exc is not None:
logger.error("Previous tunnel watcher task exited with error: %s", exc)
self.context.tunnel_watcher_task = asyncio.create_task(
tunnel_watcher_loop(),
name="tunnel-task-watcher",
@@ -1303,17 +1324,49 @@ class TunneldRunnerSio:
logger.info("Reverse Geocoded %s to %s", coords, rev_geocode)
return generate_http_response(rev_geocode)
@self._app.post("/proxy/ors/{full_path:path}")
@self._app.get("/ors/status")
async def app_ors_status(request: Request) -> fastapi.Response:
logger.info("request: %s", request)
target_url = "https://ors.intrepidnet.org/ors/v2/status"
async with httpx.AsyncClient() as client:
# Forward the request to the external service
response = await client.get(target_url, params=request.query_params)
# Return the response content and status code back to the original client
return Response(
content=response.content,
status_code=response.status_code,
headers=dict(response.headers)
)
@self._app.get("/ors/health")
async def app_ors_status(request: Request) -> fastapi.Response:
logger.info("request: %s", request)
target_url = "https://ors.intrepidnet.org/ors/v2/health"
async with httpx.AsyncClient() as client:
# Forward the request to the external service
response = await client.get(target_url, params=request.query_params)
# Return the response content and status code back to the original client
return Response(
content=response.content,
status_code=response.status_code,
headers=dict(response.headers)
)
@self._app.post("/ors/proxy/{full_path:path}")
async def app_proxy_ors(full_path: str, request: Request):
logger.info("request: %s", request)
body = await request.body()
body = await request.json()
headers = dict(request.headers)
method = request.method
url = "https://ors.intrepidnet.org/" + full_path
logger.info("body: %s", body)
logger.info("headers: %s", headers)
logger.info("method: %s", method)
logger.info("url: %s", url)
async with httpx.AsyncClient() as client:
response = await client.request(method, url, headers=headers, content=body)
return response.content
response = await client.request(method, url, json=body)
return response.json()
""" Socket.IO Functions"""

52
uv.lock generated
View File

@@ -688,21 +688,20 @@ wheels = [
[[package]]
name = "ipsw-parser"
version = "1.5.0"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cached-property" },
{ name = "click" },
{ name = "coloredlogs" },
{ name = "construct" },
{ name = "plumbum" },
{ name = "pyimg4" },
{ name = "remotezip2" },
{ name = "requests" },
{ name = "typer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2c/96/c820ec1e2f50d398c2442b2f8759448a896a300745c2920c4cf7ba89dd4c/ipsw_parser-1.5.0.tar.gz", hash = "sha256:5becd2000017b7b8549cc6b6e3f5149541792bf6353663d0f77932965bf26aad", size = 51957, upload-time = "2025-11-18T21:57:37.881Z" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/8a/bae825b78838c86b05bd7ca3bf26e98dfcf3d0955619fe717402a628dfa7/ipsw_parser-1.6.0.tar.gz", hash = "sha256:d3121ae9f2a12bae6604972a9e00f6168cdedb3ad3db6b178061b4995e8fd14f", size = 53471, upload-time = "2026-03-17T20:27:27.524Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/99/b5805555d14abf34d9534c9e8ba81d629107fa5d3bd7f110ca752a21d1c2/ipsw_parser-1.5.0-py3-none-any.whl", hash = "sha256:740248a1937d7b7dacc5d98e758b578ed88c357638126a8a71d4b0c100a3c9eb", size = 36611, upload-time = "2025-11-18T21:57:36.975Z" },
{ url = "https://files.pythonhosted.org/packages/a8/a3/9671c80252fc7a8b870b01639c3ae821d39f9489ac346bce7d8bfbf168f4/ipsw_parser-1.6.0-py3-none-any.whl", hash = "sha256:0e1f2b726c053617c65b0cef04afeecdf73ad14cc17e7e58d6382209d6693466", size = 37623, upload-time = "2026-03-17T20:27:26.518Z" },
]
[[package]]
@@ -834,21 +833,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/0d/9c59313ab43d0858a9a665e80763bd830dc78d5f379afc3815e123c486c2/keyrings.alt-5.0.2-py3-none-any.whl", hash = "sha256:6be74693192f3f37bbb752bfac9b86e6177076b17d2ac12a390f1d6abff8ac7c", size = 17930, upload-time = "2024-08-14T01:09:26.785Z" },
]
[[package]]
name = "la-panic"
version = "0.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cached-property" },
{ name = "click" },
{ name = "coloredlogs" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/85/28/757e1ccd939162caa27c8a6173d490deb986c38a7fd73fe2f264f6d7485d/la-panic-0.5.0.tar.gz", hash = "sha256:5239025d1e96aaed1fbd1c4a5d35572fd70cf42ddd68839ff1e4f1d21e3e279b", size = 57320, upload-time = "2023-11-12T17:49:33.159Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/63/8a/59eb5256cb0449c6f052453a1ce1fc32eed77ebd93b6be43f0136a7428a8/la_panic-0.5.0-py3-none-any.whl", hash = "sha256:5e224e5d038a020897606baf36e3aecbd33a5e56d82d0ac9cd72bcc0d5c3007d", size = 51866, upload-time = "2023-11-12T17:49:31.277Z" },
]
[[package]]
name = "loguru"
version = "0.7.3"
@@ -1191,16 +1175,14 @@ wheels = [
[[package]]
name = "pycrashreport"
version = "1.2.7"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cached-property" },
{ name = "click" },
{ name = "la-panic" },
{ name = "typer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d5/0b/534e02c6badef117b5a4054c065b50e6055d833b983b024d57afb425b618/pycrashreport-1.2.7.tar.gz", hash = "sha256:8f4d52d3292c1ec479fac589633d1477d19fbeb6ab01960969389c6a9c63ed7f", size = 100834, upload-time = "2025-08-25T07:15:42.872Z" }
sdist = { url = "https://files.pythonhosted.org/packages/aa/9c/1e1c2d84f746972cbe4d5e65f816008c64e427fc680f55115aa313683c23/pycrashreport-2.0.0.tar.gz", hash = "sha256:31d5e32faa3a047fe01e923bde3eaf1ec86b23e264babf2fc8f7b61fe4812342", size = 102682, upload-time = "2026-03-30T22:35:19.705Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/09/ac/751f4e55f5f3de5f152a5891682da3c25a8c57c7058a2274ca5a430a9fe2/pycrashreport-1.2.7-py3-none-any.whl", hash = "sha256:70a448ec44b86b016ce81346552d32400cc2c51d7e1928420e98a37461319866", size = 33056, upload-time = "2025-08-25T07:15:41.323Z" },
{ url = "https://files.pythonhosted.org/packages/ed/ec/be636536ab13a63e3385a82c841d08ce2a51712ffc2a337cd24afc12e038/pycrashreport-2.0.0-py3-none-any.whl", hash = "sha256:12feb922c3349cdb33b468d105915f5f829c53df25640c5c2241bc4182bdb422", size = 34244, upload-time = "2026-03-30T22:35:18.478Z" },
]
[[package]]
@@ -1387,7 +1369,7 @@ requires-dist = [
{ name = "numpy", specifier = "==2.4.3" },
{ name = "pydantic", specifier = "==2.12.5" },
{ name = "pyicloud", specifier = ">=2.4.1" },
{ name = "pymobiledevice3", specifier = "==9.0.0" },
{ name = "pymobiledevice3", specifier = "==9.8.1" },
{ name = "python-dotenv", specifier = ">=1.2.2" },
{ name = "python-socketio", specifier = "==5.16.1" },
{ name = "sqlalchemy", specifier = ">=2.0.48" },
@@ -1399,12 +1381,11 @@ requires-dist = [
[[package]]
name = "pymobiledevice3"
version = "9.0.0"
version = "9.8.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asn1" },
{ name = "bpylist2" },
{ name = "click" },
{ name = "coloredlogs" },
{ name = "construct" },
{ name = "construct-typing" },
@@ -1445,9 +1426,9 @@ dependencies = [
{ name = "wsproto" },
{ name = "xonsh" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1d/89/aed5abbb6a4ece29bed2b9e85ccfbc28a8197658a4923e8cd2d5193796d8/pymobiledevice3-9.0.0.tar.gz", hash = "sha256:e85c169d67cf17d1dcf4ce26e3a84a801e86d13a0144ff7fb57eb532745ddcfb", size = 735101, upload-time = "2026-03-11T08:37:05.172Z" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/0e/81b8c391929cb2e2c9c831968df5c9cabc5687ab5d45fc9c345fcdb48171/pymobiledevice3-9.8.1.tar.gz", hash = "sha256:621479ad545795e52af1955e0cc6c8e807b51d81442173b3386f9d1fef21308a", size = 758015, upload-time = "2026-03-31T15:00:55.143Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/ae/aaff1375b383c78729b6b25abac1458e9fd5c0c84b8da364bf204559dc16/pymobiledevice3-9.0.0-py3-none-any.whl", hash = "sha256:7366533cc8807299ef0b88c6c56a77120f7d984914ec6436191a7cc2991d3ae7", size = 789609, upload-time = "2026-03-11T08:37:01.731Z" },
{ url = "https://files.pythonhosted.org/packages/1a/58/3275db73730e99a6345231a7deeef5b184bd6a26b5dd40adefcb5ad0f84d/pymobiledevice3-9.8.1-py3-none-any.whl", hash = "sha256:9e1a5c8277a8a7955544579f118da8645f83ac05a1a03e79a96307242089842a", size = 797514, upload-time = "2026-03-31T15:00:53.169Z" },
]
[[package]]
@@ -1661,15 +1642,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
]
[[package]]
name = "setuptools"
version = "82.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893, upload-time = "2026-02-08T15:08:40.206Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468, upload-time = "2026-02-08T15:08:38.723Z" },
]
[[package]]
name = "shellingham"
version = "1.5.4"