bump pymd3
This commit is contained in:
Binary file not shown.
@@ -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",
|
||||
|
||||
@@ -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,60 +344,84 @@ class TunneldRunnerSio:
|
||||
async def tunnel_watcher_loop() -> None:
|
||||
previous = collect_active_tunnels()
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
current = collect_active_tunnels()
|
||||
added_keys = set(current.keys()) - set(previous.keys())
|
||||
removed_keys = set(previous.keys()) - set(current.keys())
|
||||
added = [current[k] for k in sorted(added_keys)]
|
||||
removed = [previous[k] for k in sorted(removed_keys)]
|
||||
for item in added:
|
||||
logger.info(
|
||||
"Tunnel discovered interface=%s udid=%s address=%s port=%s transport=%s",
|
||||
item.get("interface"),
|
||||
item.get("udid"),
|
||||
item.get("address"),
|
||||
item.get("port"),
|
||||
item.get("transport"),
|
||||
)
|
||||
await safe_sio_emit("tunnel_device_connected", item)
|
||||
logger.info(f"Current udid: %s, new udid: %s",
|
||||
self.context.udid, item.get("udid"))
|
||||
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:
|
||||
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)
|
||||
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,
|
||||
"device_name": device_name,
|
||||
},
|
||||
)
|
||||
await get_tun(item.get("udid"), max_retries=10, retry_delay=1.0)
|
||||
try:
|
||||
await asyncio.sleep(1)
|
||||
current = collect_active_tunnels()
|
||||
added_keys = set(current.keys()) - set(previous.keys())
|
||||
removed_keys = set(previous.keys()) - set(current.keys())
|
||||
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"),
|
||||
item.get("udid"),
|
||||
item.get("address"),
|
||||
item.get("port"),
|
||||
item.get("transport"),
|
||||
)
|
||||
await safe_sio_emit("tunnel_device_connected", item)
|
||||
logger.info(f"Current udid: %s, new udid: %s",
|
||||
self.context.udid, item.get("udid"))
|
||||
if self.context.udid is None:
|
||||
self.context.udid = item.get("udid")
|
||||
device_name = await get_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",
|
||||
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": 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:
|
||||
logger.warning(
|
||||
"Tunnel disconnected interface=%s udid=%s address=%s port=%s",
|
||||
item.get("interface"),
|
||||
item.get("udid"),
|
||||
item.get("address"),
|
||||
item.get("port"),
|
||||
)
|
||||
await safe_sio_emit("tunnel_device_disconnected", item)
|
||||
await handle_tunnel_drop(removed)
|
||||
if removed:
|
||||
for item in removed:
|
||||
try:
|
||||
logger.warning(
|
||||
"Tunnel disconnected interface=%s udid=%s address=%s port=%s",
|
||||
item.get("interface"),
|
||||
item.get("udid"),
|
||||
item.get("address"),
|
||||
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
|
||||
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
52
uv.lock
generated
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user