tzafon 0.1.4__py3-none-any.whl → 1.4.0__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.
Potentially problematic release.
This version of tzafon might be problematic. Click here for more details.
- tzafon/__init__.py +114 -2
- tzafon/_base_client.py +1995 -0
- tzafon/_client.py +403 -0
- tzafon/_compat.py +219 -0
- tzafon/_constants.py +14 -0
- tzafon/_exceptions.py +108 -0
- tzafon/_files.py +123 -0
- tzafon/_models.py +835 -0
- tzafon/_qs.py +150 -0
- tzafon/_resource.py +43 -0
- tzafon/_response.py +830 -0
- tzafon/_streaming.py +333 -0
- tzafon/_types.py +260 -0
- tzafon/_utils/__init__.py +64 -0
- tzafon/_utils/_compat.py +45 -0
- tzafon/_utils/_datetime_parse.py +136 -0
- tzafon/_utils/_logs.py +25 -0
- tzafon/_utils/_proxy.py +65 -0
- tzafon/_utils/_reflection.py +42 -0
- tzafon/_utils/_resources_proxy.py +24 -0
- tzafon/_utils/_streams.py +12 -0
- tzafon/_utils/_sync.py +86 -0
- tzafon/_utils/_transform.py +457 -0
- tzafon/_utils/_typing.py +156 -0
- tzafon/_utils/_utils.py +421 -0
- tzafon/_version.py +4 -0
- tzafon/batch_wrapper.py +102 -0
- tzafon/client_extensions.py +61 -0
- tzafon/py.typed +0 -0
- tzafon/resources/__init__.py +19 -0
- tzafon/resources/computers.py +822 -0
- tzafon/types/__init__.py +13 -0
- tzafon/types/action_result.py +17 -0
- tzafon/types/computer_create_params.py +30 -0
- tzafon/types/computer_execute_action_params.py +11 -0
- tzafon/types/computer_execute_batch_params.py +11 -0
- tzafon/types/computer_execute_batch_response.py +8 -0
- tzafon/types/computer_keep_alive_response.py +8 -0
- tzafon/types/computer_list_response.py +10 -0
- tzafon/types/computer_navigate_params.py +11 -0
- tzafon/types/computer_response.py +19 -0
- tzafon/wrapper.py +102 -0
- tzafon-1.4.0.dist-info/METADATA +429 -0
- tzafon-1.4.0.dist-info/RECORD +46 -0
- {tzafon-0.1.4.dist-info → tzafon-1.4.0.dist-info}/WHEEL +1 -1
- tzafon-1.4.0.dist-info/licenses/LICENSE +7 -0
- tzafon/_connection.py +0 -49
- tzafon/client.py +0 -116
- tzafon/exceptions.py +0 -6
- tzafon/models.py +0 -99
- tzafon-0.1.4.dist-info/METADATA +0 -62
- tzafon-0.1.4.dist-info/RECORD +0 -8
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
tzafon/__init__.py,sha256=V5qEHakTu5iq2ShF_TOZHw4io0UXcTp3_AQBqRpMO48,3251
|
|
2
|
+
tzafon/_base_client.py,sha256=5AX9s8UF-0wXUI15A1uiRcWhTNk_I4UBpt0Q-2r0aLU,67047
|
|
3
|
+
tzafon/_client.py,sha256=EbENZoqN6ERd3PSISUi0jGCdmxC_5-P8sONVbZaqR9w,15066
|
|
4
|
+
tzafon/_compat.py,sha256=DQBVORjFb33zch24jzkhM14msvnzY7mmSmgDLaVFUM8,6562
|
|
5
|
+
tzafon/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
|
|
6
|
+
tzafon/_exceptions.py,sha256=qi7R0qNzTGtKBt4fWynaQkMnUk3YGrIp01jis82aEdo,3224
|
|
7
|
+
tzafon/_files.py,sha256=KnEzGi_O756MvKyJ4fOCW_u3JhOeWPQ4RsmDvqihDQU,3545
|
|
8
|
+
tzafon/_models.py,sha256=lKnskYPONAWDvWo8tmbbVk7HmG7UOsI0Nve0vSMmkRc,30452
|
|
9
|
+
tzafon/_qs.py,sha256=craIKyvPktJ94cvf9zn8j8ekG9dWJzhWv0ob34lIOv4,4828
|
|
10
|
+
tzafon/_resource.py,sha256=EseuwN0R4kKBVcQnyF4NgCeRtc6-OrR8yWVI5fOYGHM,1112
|
|
11
|
+
tzafon/_response.py,sha256=Gaq-HHLZ76rKv515pCAMkfW4rbz_ManOH-7R640hrDE,28792
|
|
12
|
+
tzafon/_streaming.py,sha256=SavIdu5Tnaelw60s19mwFijhkdhdxh1fDqZ1AoIwC0g,10108
|
|
13
|
+
tzafon/_types.py,sha256=1kStHPi-XwmomGBE0xKVtz0mdRF4Svxb8NWZR4FaSiY,7236
|
|
14
|
+
tzafon/_version.py,sha256=iQiubfCSrqbBxAeb4F9zNHDDpkgEuVobKfZWVuACOrs,158
|
|
15
|
+
tzafon/batch_wrapper.py,sha256=ESJGxScFZzdPwDTiCjCz6-QRsoB-_nxoZuHbN9HKbUE,3509
|
|
16
|
+
tzafon/client_extensions.py,sha256=Tn3SkDYNl0B8M3U8eE39KNLsazKpbztAQSmBVVmzgCQ,1964
|
|
17
|
+
tzafon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
tzafon/wrapper.py,sha256=gfihF7G0y-Q77h_N6IKoslB8XXDK1eq3dZAIq1BLedc,3517
|
|
19
|
+
tzafon/_utils/__init__.py,sha256=7fch0GT9zpNnErbciSpUNa-SjTxxjY6kxHxKMOM4AGs,2305
|
|
20
|
+
tzafon/_utils/_compat.py,sha256=D8gtAvjJQrDWt9upS0XaG9Rr5l1QhiAx_I_1utT_tt0,1195
|
|
21
|
+
tzafon/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204
|
|
22
|
+
tzafon/_utils/_logs.py,sha256=PEF_Amx1dH7ka0Jvayh7slfAfeVkgcGsLInHQ0Y1SzI,776
|
|
23
|
+
tzafon/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975
|
|
24
|
+
tzafon/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEMU,1364
|
|
25
|
+
tzafon/_utils/_resources_proxy.py,sha256=lysaDfd1mc_JASfb2pnOzeAGyWwNF8zCLebbfeuKa6I,589
|
|
26
|
+
tzafon/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
|
|
27
|
+
tzafon/_utils/_sync.py,sha256=TpGLrrhRNWTJtODNE6Fup3_k7zrWm1j2RlirzBwre-0,2862
|
|
28
|
+
tzafon/_utils/_transform.py,sha256=NjCzmnfqYrsAikUHQig6N9QfuTVbKipuP3ur9mcNF-E,15951
|
|
29
|
+
tzafon/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,4786
|
|
30
|
+
tzafon/_utils/_utils.py,sha256=0dDqauUbVZEXV0NVl7Bwu904Wwo5eyFCZpQThhFNhyA,12253
|
|
31
|
+
tzafon/resources/__init__.py,sha256=2tbxQpPYxwl6BYAmWlSblYFfmeBEasXs9oxh3vXRG2M,591
|
|
32
|
+
tzafon/resources/computers.py,sha256=6qqhsaqPfnlC0Swy7CwnIdtT60GRWeVnKUm2BBHDHdc,31592
|
|
33
|
+
tzafon/types/__init__.py,sha256=kb0H5ErFRI_4U_KF2ElDt45ZTid6K1dyDjq5_VL_FKs,902
|
|
34
|
+
tzafon/types/action_result.py,sha256=RE4iCBcGaUWXirCBNsjf00jLl_s173NN8-gCMtKVs0g,374
|
|
35
|
+
tzafon/types/computer_create_params.py,sha256=G9rhhm8ckcLbG5tM-vHrgG19-jmfUVN0KMcHodjNs4g,553
|
|
36
|
+
tzafon/types/computer_execute_action_params.py,sha256=-s1B9iUBavL2R2MvQXDxixdYbzr-l24wqjPP9LDo-xQ,304
|
|
37
|
+
tzafon/types/computer_execute_batch_params.py,sha256=hg5xchXjFmXv-I04hfygZYq5_TrkjmfhpmdWWsE3Axw,302
|
|
38
|
+
tzafon/types/computer_execute_batch_response.py,sha256=zIUo0GDOCk6wUeSTa4_RPPHRp1gasRdi_o8EpI6zCek,256
|
|
39
|
+
tzafon/types/computer_keep_alive_response.py,sha256=H75ugeXG2CetcGgwwKE_cYmr_ujDH0qpMWg9TMZ4tkI,250
|
|
40
|
+
tzafon/types/computer_list_response.py,sha256=ABykSdzQgm_bNISk_F7GQaXWRB3WidA9tXuMjmNWZps,294
|
|
41
|
+
tzafon/types/computer_navigate_params.py,sha256=mK4JWt0fqafLuoXHdOh_I6j-hJY0Ub0nAUkadA20v38,294
|
|
42
|
+
tzafon/types/computer_response.py,sha256=agc6iysQzQR22edcoz84CuUpzQ7WLwsxEuo8ccwIbYc,404
|
|
43
|
+
tzafon-1.4.0.dist-info/METADATA,sha256=OWyl374RBvhlhT84mf7NBgAXotNqBd3fYv-wSVL4wlc,13983
|
|
44
|
+
tzafon-1.4.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
45
|
+
tzafon-1.4.0.dist-info/licenses/LICENSE,sha256=ciLXi9zfEehb4Tnhy-M9I2WSRUpZjx-FK1TEChVqlDo,1048
|
|
46
|
+
tzafon-1.4.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 computer
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
tzafon/_connection.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import asyncio
|
|
3
|
-
import traceback
|
|
4
|
-
import websockets
|
|
5
|
-
from websockets.protocol import State
|
|
6
|
-
from .models import Command, Result
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class _WsConnection:
|
|
10
|
-
def __init__(self, url: str, *, ping_interval: int = 20):
|
|
11
|
-
if not url.startswith(("ws://", "wss://")):
|
|
12
|
-
raise ValueError(f"Invalid websocket URL '{url}'")
|
|
13
|
-
self._url = url
|
|
14
|
-
self._conn: websockets.WebSocketClientProtocol | None = None
|
|
15
|
-
self._ping = ping_interval
|
|
16
|
-
|
|
17
|
-
async def connect(self) -> None:
|
|
18
|
-
if self.is_open:
|
|
19
|
-
return
|
|
20
|
-
self._conn = await websockets.connect(
|
|
21
|
-
self._url,
|
|
22
|
-
max_size=2**24, # 16 MiB
|
|
23
|
-
ping_interval=self._ping,
|
|
24
|
-
ping_timeout=self._ping,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
async def close(self) -> None:
|
|
28
|
-
if self._conn and self._conn.state is State.OPEN:
|
|
29
|
-
try:
|
|
30
|
-
await self._conn.close(code=1000, reason="Client shutting down")
|
|
31
|
-
await asyncio.wait_for(self._conn.wait_closed(), timeout=5)
|
|
32
|
-
except Exception:
|
|
33
|
-
traceback.print_exc()
|
|
34
|
-
self._conn = None
|
|
35
|
-
|
|
36
|
-
async def send(self, cmd: Command) -> Result:
|
|
37
|
-
if not self.is_open:
|
|
38
|
-
raise RuntimeError("Websocket not connected")
|
|
39
|
-
await self._conn.send(cmd.dump())
|
|
40
|
-
raw = await self._conn.recv()
|
|
41
|
-
if isinstance(raw, str):
|
|
42
|
-
raw = raw.encode()
|
|
43
|
-
return Result.load(raw)
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def is_open(self) -> bool:
|
|
47
|
-
return (
|
|
48
|
-
self._conn is not None and getattr(self._conn, "state", None) is State.OPEN
|
|
49
|
-
)
|
tzafon/client.py
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import asyncio
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
from .models import Command, Result, ActionType
|
|
8
|
-
from ._connection import _WsConnection
|
|
9
|
-
from .exceptions import ScreenshotFailed
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
log = logging.getLogger("waypoint")
|
|
13
|
-
_DEFAULT_VIEWPORT = {"width": 1280, "height": 720}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Waypoint:
|
|
17
|
-
def __init__(
|
|
18
|
-
self,
|
|
19
|
-
token: str,
|
|
20
|
-
*,
|
|
21
|
-
url_template: str = "wss://api.tzafon.ai/ephemeral-tzafonwright?token={token}",
|
|
22
|
-
connect_timeout: float = 10.0,
|
|
23
|
-
):
|
|
24
|
-
if not token or not token.startswith("wpk_"):
|
|
25
|
-
raise ValueError("token must look like 'wpk_…'")
|
|
26
|
-
self._ws_url = url_template.format(token=token)
|
|
27
|
-
self._timeout = connect_timeout
|
|
28
|
-
self._ws: Optional[_WsConnection] = None
|
|
29
|
-
|
|
30
|
-
async def __aenter__(self) -> "Waypoint":
|
|
31
|
-
self._ws = _WsConnection(self._ws_url)
|
|
32
|
-
await asyncio.wait_for(self._ws.connect(), timeout=self._timeout)
|
|
33
|
-
# one-time only
|
|
34
|
-
await self.set_viewport(**_DEFAULT_VIEWPORT)
|
|
35
|
-
return self
|
|
36
|
-
|
|
37
|
-
async def __aexit__(self, exc_t, exc, tb) -> None:
|
|
38
|
-
if self._ws:
|
|
39
|
-
await self._ws.close()
|
|
40
|
-
self._ws = None
|
|
41
|
-
|
|
42
|
-
async def goto(self, url: str, *, timeout: int = 5_000) -> Result:
|
|
43
|
-
return await self._send(Command(ActionType.GOTO, url=url, timeout=timeout))
|
|
44
|
-
|
|
45
|
-
async def click(self, x: float, y: float) -> Result:
|
|
46
|
-
return await self._send(Command(ActionType.CLICK, x=x, y=y))
|
|
47
|
-
|
|
48
|
-
async def type(self, text: str) -> Result:
|
|
49
|
-
return await self._send(Command(ActionType.TYPE, text=text))
|
|
50
|
-
|
|
51
|
-
async def scroll(self, dx: int = 0, dy: int = 100) -> Result:
|
|
52
|
-
return await self._send(Command(ActionType.SCROLL, delta_x=dx, delta_y=dy))
|
|
53
|
-
|
|
54
|
-
async def screenshot(
|
|
55
|
-
self,
|
|
56
|
-
path: str | os.PathLike | None = None,
|
|
57
|
-
*,
|
|
58
|
-
mkdir: bool = True,
|
|
59
|
-
return_url: bool = False,
|
|
60
|
-
) -> bytes | str:
|
|
61
|
-
"""
|
|
62
|
-
Grab a **JPEG** screenshot.
|
|
63
|
-
|
|
64
|
-
Parameters
|
|
65
|
-
path:
|
|
66
|
-
Optional file destination. If provided the image is written to disk
|
|
67
|
-
*and* the raw bytes are still returned.
|
|
68
|
-
mkdir:
|
|
69
|
-
Automatically create parent folders if they do not exist.
|
|
70
|
-
return_url:
|
|
71
|
-
If True, return the remote URL instead of the image bytes.
|
|
72
|
-
"""
|
|
73
|
-
res = await self._send(Command(ActionType.SCREENSHOT))
|
|
74
|
-
|
|
75
|
-
if return_url:
|
|
76
|
-
if res.image_url:
|
|
77
|
-
return res.image_url
|
|
78
|
-
if path is not None:
|
|
79
|
-
from pathlib import Path
|
|
80
|
-
|
|
81
|
-
p = Path(path)
|
|
82
|
-
if mkdir:
|
|
83
|
-
p.parent.mkdir(parents=True, exist_ok=True)
|
|
84
|
-
p.write_bytes(res.image)
|
|
85
|
-
return str(p.resolve())
|
|
86
|
-
raise ScreenshotFailed(
|
|
87
|
-
"Server did not provide image_url; set return_url=False to get bytes"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if res.success and res.image is None and res.image_url is not None:
|
|
91
|
-
await res.download_image()
|
|
92
|
-
|
|
93
|
-
if not (res.success and res.image):
|
|
94
|
-
raise ScreenshotFailed(res.error_message or "unknown error")
|
|
95
|
-
|
|
96
|
-
if path is not None:
|
|
97
|
-
from pathlib import Path
|
|
98
|
-
|
|
99
|
-
p = Path(path)
|
|
100
|
-
if mkdir:
|
|
101
|
-
p.parent.mkdir(parents=True, exist_ok=True)
|
|
102
|
-
p.write_bytes(res.image)
|
|
103
|
-
return res.image
|
|
104
|
-
|
|
105
|
-
async def set_viewport(self, *, width: int, height: int) -> Result:
|
|
106
|
-
return await self._send(
|
|
107
|
-
Command(ActionType.SET_VIEWPORT_SIZE, width=width, height=height)
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
async def _send(self, cmd: Command) -> Result:
|
|
111
|
-
if self._ws is None:
|
|
112
|
-
raise RuntimeError("Waypoint must be used inside 'async with'")
|
|
113
|
-
res = await self._ws.send(cmd)
|
|
114
|
-
if not res.success:
|
|
115
|
-
log.warning("Command %s failed: %s", cmd.action_type, res.error_message)
|
|
116
|
-
return res
|
tzafon/exceptions.py
DELETED
tzafon/models.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import json
|
|
3
|
-
import base64
|
|
4
|
-
from dataclasses import dataclass, asdict
|
|
5
|
-
from enum import Enum
|
|
6
|
-
from typing import Optional, Dict, Any, Self
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ActionType(str, Enum):
|
|
10
|
-
CLICK = "click"
|
|
11
|
-
TYPE = "type"
|
|
12
|
-
SCROLL = "scroll"
|
|
13
|
-
GOTO = "goto"
|
|
14
|
-
SCREENSHOT = "screenshot"
|
|
15
|
-
SET_VIEWPORT_SIZE = "set_viewport_size"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass(slots=True)
|
|
19
|
-
class Command:
|
|
20
|
-
action_type: ActionType
|
|
21
|
-
# optional payload
|
|
22
|
-
x: Optional[float] = None
|
|
23
|
-
y: Optional[float] = None
|
|
24
|
-
text: Optional[str] = None
|
|
25
|
-
delta_x: Optional[int] = None
|
|
26
|
-
delta_y: Optional[int] = None
|
|
27
|
-
url: Optional[str] = None
|
|
28
|
-
width: Optional[int] = None
|
|
29
|
-
height: Optional[int] = None
|
|
30
|
-
timeout: int = 5000 # ms
|
|
31
|
-
|
|
32
|
-
@classmethod
|
|
33
|
-
def load(cls, body: bytes) -> Self:
|
|
34
|
-
data = json.loads(body.decode("utf-8"))
|
|
35
|
-
action_str = data.pop("action_type", None)
|
|
36
|
-
if action_str is None:
|
|
37
|
-
raise ValueError("Command missing 'action_type'")
|
|
38
|
-
try:
|
|
39
|
-
action_enum = ActionType(action_str)
|
|
40
|
-
except ValueError as e:
|
|
41
|
-
raise ValueError(f"Unknown action_type '{action_str}'") from e
|
|
42
|
-
return cls(action_type=action_enum, **data)
|
|
43
|
-
|
|
44
|
-
def dump(self) -> bytes:
|
|
45
|
-
d = asdict(self)
|
|
46
|
-
d["action_type"] = self.action_type.value
|
|
47
|
-
d = {k: v for k, v in d.items() if v is not None}
|
|
48
|
-
return json.dumps(d).encode("utf-8")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@dataclass(slots=True)
|
|
52
|
-
class Result:
|
|
53
|
-
success: bool
|
|
54
|
-
image: Optional[bytes] = None # jpeg bytes
|
|
55
|
-
image_url: Optional[str] = None # remote location if bytes omitted
|
|
56
|
-
error_message: Optional[str] = None
|
|
57
|
-
|
|
58
|
-
async def download_image(self) -> None:
|
|
59
|
-
"""If `image` is None but `image_url` present, fetch the bytes (async)."""
|
|
60
|
-
if self.image is None and self.image_url is not None:
|
|
61
|
-
try:
|
|
62
|
-
import httpx
|
|
63
|
-
|
|
64
|
-
async with httpx.AsyncClient(timeout=20.0, follow_redirects=True) as c:
|
|
65
|
-
resp = await c.get(self.image_url)
|
|
66
|
-
resp.raise_for_status()
|
|
67
|
-
self.image = resp.content
|
|
68
|
-
except Exception:
|
|
69
|
-
# keep silent – caller will treat as missing image later
|
|
70
|
-
pass
|
|
71
|
-
|
|
72
|
-
@classmethod
|
|
73
|
-
def load(cls, body: bytes) -> Self:
|
|
74
|
-
data = json.loads(body.decode("utf-8"))
|
|
75
|
-
img_b64 = data.get("image")
|
|
76
|
-
img = base64.b64decode(img_b64) if img_b64 else None
|
|
77
|
-
return cls(
|
|
78
|
-
success=data.get("success", False),
|
|
79
|
-
image=img,
|
|
80
|
-
image_url=data.get("image_url"),
|
|
81
|
-
error_message=data.get("error_message"),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
def dump(self) -> bytes:
|
|
85
|
-
d: Dict[str, Any] = {
|
|
86
|
-
"success": self.success,
|
|
87
|
-
"error_message": self.error_message,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if self.image is not None:
|
|
91
|
-
d["image"] = base64.b64encode(self.image).decode()
|
|
92
|
-
if self.image_url is not None:
|
|
93
|
-
d["image_url"] = self.image_url
|
|
94
|
-
return json.dumps({k: v for k, v in d.items() if v is not None}).encode()
|
|
95
|
-
|
|
96
|
-
def __str__(self) -> str:
|
|
97
|
-
ok = "✅" if self.success else "❌"
|
|
98
|
-
img = "🖼️" if self.image else "—"
|
|
99
|
-
return f"<Result {ok} image:{img} msg:{self.error_message!r}>"
|
tzafon-0.1.4.dist-info/METADATA
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: tzafon
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: Tzafon Waypoint – browser automation
|
|
5
|
-
License: MIT
|
|
6
|
-
Requires-Python: >=3.10
|
|
7
|
-
Requires-Dist: httpx>=0.26.0
|
|
8
|
-
Requires-Dist: pytest-asyncio>=0.24.0
|
|
9
|
-
Requires-Dist: pytest>=8.3.4
|
|
10
|
-
Requires-Dist: python-dotenv>=1.1.0
|
|
11
|
-
Requires-Dist: twine>=6.1.0
|
|
12
|
-
Requires-Dist: websockets>=15.0.1
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
# Tzafon Waypoint
|
|
16
|
-
|
|
17
|
-
A Python client for interacting with the tzafon API for web automation (waypoint.tzafon.ai).
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
pip install tzafon
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Usage
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
import asyncio
|
|
29
|
-
from tzafon import Waypoint
|
|
30
|
-
|
|
31
|
-
async def main():
|
|
32
|
-
async with Waypoint(token="wpk_your_token_here") as wp:
|
|
33
|
-
# Navigate to a website
|
|
34
|
-
await wp.goto("https://example.com")
|
|
35
|
-
|
|
36
|
-
# Take a screenshot
|
|
37
|
-
image_bytes = await wp.screenshot("screenshot.jpg")
|
|
38
|
-
|
|
39
|
-
# Click at a specific position
|
|
40
|
-
await wp.click(100, 200)
|
|
41
|
-
|
|
42
|
-
# Type some text
|
|
43
|
-
await wp.type("Hello world")
|
|
44
|
-
|
|
45
|
-
# Scroll down
|
|
46
|
-
await wp.scroll(dy=200)
|
|
47
|
-
|
|
48
|
-
if __name__ == "__main__":
|
|
49
|
-
asyncio.run(main())
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Features
|
|
53
|
-
|
|
54
|
-
- Web navigation
|
|
55
|
-
- Screenshots
|
|
56
|
-
- Clicking and typing
|
|
57
|
-
- Scrolling
|
|
58
|
-
- Viewport control
|
|
59
|
-
|
|
60
|
-
## License
|
|
61
|
-
|
|
62
|
-
MIT
|
tzafon-0.1.4.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
tzafon/__init__.py,sha256=QpZABzfciDC1uUdoRHp5tW0694ZJGtHNF9ieGilnt94,53
|
|
2
|
-
tzafon/_connection.py,sha256=6kqekASLSAGrjx5bWJNVTQ3mifw2MbemB8b8xJm8_Qg,1599
|
|
3
|
-
tzafon/client.py,sha256=uzh8ZLntw9cOIK5jAcfSokrpfBWxIksm-AuH3xfHM3s,3901
|
|
4
|
-
tzafon/exceptions.py,sha256=SeeZRBAsyeogXXcfF3b3IaKUsehWNeNZAgxbmrzjI48,168
|
|
5
|
-
tzafon/models.py,sha256=HTpU4YwjbggV85l5-_VxVvjHGOX_jupSNtMEUZ4f1oM,3239
|
|
6
|
-
tzafon-0.1.4.dist-info/METADATA,sha256=vAVOPCaUmOQfAM3xPmH2b4lME7xPop2xMGToW7cebNs,1202
|
|
7
|
-
tzafon-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
-
tzafon-0.1.4.dist-info/RECORD,,
|