website-agent-server 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (24) hide show
  1. {website_agent_server-0.1.2/website_agent_server.egg-info → website_agent_server-0.1.4}/PKG-INFO +2 -1
  2. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/README.md +2 -1
  3. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/pyproject.toml +1 -1
  4. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/__init__.py +1 -1
  5. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/__main__.py +66 -26
  6. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/browser.py +510 -219
  7. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/config.py +2 -1
  8. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/main.py +94 -7
  9. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/app.js +182 -2
  10. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/index.html +29 -0
  11. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/styles.css +67 -0
  12. {website_agent_server-0.1.2 → website_agent_server-0.1.4/website_agent_server.egg-info}/PKG-INFO +2 -1
  13. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/LICENSE +0 -0
  14. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/setup.cfg +0 -0
  15. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/auth.py +0 -0
  16. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/auth.css +0 -0
  17. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/auth.html +0 -0
  18. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/static/auth.js +0 -0
  19. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server/url_policy.py +0 -0
  20. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server.egg-info/SOURCES.txt +0 -0
  21. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server.egg-info/dependency_links.txt +0 -0
  22. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server.egg-info/entry_points.txt +0 -0
  23. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server.egg-info/requires.txt +0 -0
  24. {website_agent_server-0.1.2 → website_agent_server-0.1.4}/website_agent_server.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: website-agent-server
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A server-side browser proxy that lets clients operate websites without direct remote connections.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -80,6 +80,7 @@ venv\Scripts\python.exe -m website_agent_server --pin 123456
80
80
  | `--accept-language` | `zh-CN,zh;q=0.9,en;q=0.8` | `Accept-Language` header sent by browser contexts. |
81
81
  | `--user-agent` | auto | Desktop browser User-Agent. By default the server derives a normal Chrome UA from the bundled Chromium version instead of exposing `HeadlessChrome`. |
82
82
  | `--session-ttl-seconds` | `600` | Disconnected client session and client browser context lifetime. A client can reconnect to its cached browser session during this window. |
83
+ | `--shutdown-timeout-seconds` | `3.0` | Maximum graceful shutdown wait for active HTTP/WebSocket connections after Ctrl+C. |
83
84
  | `--navigation-timeout-ms` | `30000` | Navigation timeout. |
84
85
  | `--frame-interval-seconds` | `0.18` | Screenshot streaming interval. |
85
86
  | `--screenshot-quality` | `95` | Frame quality from 1 to 100. Values below 100 use JPEG; 100 uses PNG. |
@@ -67,7 +67,8 @@ venv\Scripts\python.exe -m website_agent_server --pin 123456
67
67
  | `--accept-language` | `zh-CN,zh;q=0.9,en;q=0.8` | `Accept-Language` header sent by browser contexts. |
68
68
  | `--user-agent` | auto | Desktop browser User-Agent. By default the server derives a normal Chrome UA from the bundled Chromium version instead of exposing `HeadlessChrome`. |
69
69
  | `--session-ttl-seconds` | `600` | Disconnected client session and client browser context lifetime. A client can reconnect to its cached browser session during this window. |
70
- | `--navigation-timeout-ms` | `30000` | Navigation timeout. |
70
+ | `--shutdown-timeout-seconds` | `3.0` | Maximum graceful shutdown wait for active HTTP/WebSocket connections after Ctrl+C. |
71
+ | `--navigation-timeout-ms` | `30000` | Navigation timeout. |
71
72
  | `--frame-interval-seconds` | `0.18` | Screenshot streaming interval. |
72
73
  | `--screenshot-quality` | `95` | Frame quality from 1 to 100. Values below 100 use JPEG; 100 uses PNG. |
73
74
  | `--media-frame-interval-seconds` | `0.35` | Screenshot streaming interval while remote media is playing. |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "website-agent-server"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "A server-side browser proxy that lets clients operate websites without direct remote connections."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  """Website Agent Server package."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.4"
@@ -1,13 +1,41 @@
1
1
  from __future__ import annotations
2
2
 
3
- import argparse
4
- import asyncio
5
- from pathlib import Path
6
-
7
- import uvicorn
8
-
9
- from .config import settings
10
- from .url_policy import HostAccessPolicy
3
+ import argparse
4
+ import asyncio
5
+ import contextlib
6
+ import signal
7
+ import threading
8
+ from pathlib import Path
9
+ from types import FrameType
10
+ from collections.abc import Callable, Generator
11
+
12
+ import uvicorn
13
+ from uvicorn.server import HANDLED_SIGNALS
14
+
15
+ from .config import settings
16
+ from .url_policy import HostAccessPolicy
17
+
18
+
19
+ class WebsiteAgentServer(uvicorn.Server):
20
+ def __init__(self, config: uvicorn.Config, request_stop: Callable[[], None]) -> None:
21
+ super().__init__(config)
22
+ self._request_stop = request_stop
23
+
24
+ def handle_exit(self, sig: int, frame: FrameType | None) -> None:
25
+ self._request_stop()
26
+ super().handle_exit(sig, frame)
27
+
28
+ @contextlib.contextmanager
29
+ def capture_signals(self) -> Generator[None]:
30
+ if threading.current_thread() is not threading.main_thread():
31
+ yield
32
+ return
33
+ original_handlers = {sig: signal.signal(sig, self.handle_exit) for sig in HANDLED_SIGNALS}
34
+ try:
35
+ yield
36
+ finally:
37
+ for sig, handler in original_handlers.items():
38
+ signal.signal(sig, handler)
11
39
 
12
40
 
13
41
  def build_parser() -> argparse.ArgumentParser:
@@ -60,11 +88,17 @@ def build_parser() -> argparse.ArgumentParser:
60
88
  "--session-ttl-seconds",
61
89
  type=int,
62
90
  default=settings.session_ttl_seconds,
63
- help="Idle session lifetime in seconds.",
64
- )
65
- parser.add_argument(
66
- "--navigation-timeout-ms",
67
- type=int,
91
+ help="Idle session lifetime in seconds.",
92
+ )
93
+ parser.add_argument(
94
+ "--shutdown-timeout-seconds",
95
+ type=float,
96
+ default=settings.shutdown_timeout_seconds,
97
+ help="Maximum graceful shutdown wait for active HTTP/WebSocket connections.",
98
+ )
99
+ parser.add_argument(
100
+ "--navigation-timeout-ms",
101
+ type=int,
68
102
  default=settings.navigation_timeout_ms,
69
103
  help="Navigation timeout in milliseconds.",
70
104
  )
@@ -146,7 +180,8 @@ async def apply_args(args: argparse.Namespace) -> None:
146
180
  settings.accept_language = args.accept_language
147
181
  settings.user_agent = args.user_agent
148
182
  settings.session_ttl_seconds = args.session_ttl_seconds
149
- settings.navigation_timeout_ms = args.navigation_timeout_ms
183
+ settings.shutdown_timeout_seconds = max(0.1, args.shutdown_timeout_seconds)
184
+ settings.navigation_timeout_ms = args.navigation_timeout_ms
150
185
  settings.frame_interval_seconds = args.frame_interval_seconds
151
186
  settings.screenshot_quality = max(1, min(100, args.screenshot_quality))
152
187
  settings.media_frame_interval_seconds = max(0.05, args.media_frame_interval_seconds)
@@ -167,20 +202,25 @@ async def apply_args(args: argparse.Namespace) -> None:
167
202
  settings.lock_url = None
168
203
 
169
204
 
170
- def main() -> None:
171
- parser = build_parser()
172
- args = parser.parse_args()
205
+ def main() -> None:
206
+ parser = build_parser()
207
+ args = parser.parse_args()
173
208
  try:
174
209
  asyncio.run(apply_args(args))
175
- except ValueError as exc:
176
- parser.error(str(exc))
177
- uvicorn.run(
178
- "website_agent_server.main:app",
179
- host=settings.host,
180
- port=settings.port,
181
- reload=False,
182
- ws_ping_interval=None,
183
- )
210
+ except ValueError as exc:
211
+ parser.error(str(exc))
212
+ from .main import app, manager
213
+
214
+ config = uvicorn.Config(
215
+ app,
216
+ host=settings.host,
217
+ port=settings.port,
218
+ reload=False,
219
+ ws_ping_interval=None,
220
+ timeout_graceful_shutdown=settings.shutdown_timeout_seconds,
221
+ )
222
+ server = WebsiteAgentServer(config, manager.request_stop)
223
+ server.run()
184
224
 
185
225
 
186
226
  if __name__ == "__main__":