clerk-sdk 0.2.4__tar.gz → 0.2.6__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.
- {clerk_sdk-0.2.4/clerk_sdk.egg-info → clerk_sdk-0.2.6}/PKG-INFO +1 -1
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/client.py +11 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/client_actor/client_actor.py +2 -2
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/decorators/gui_automation.py +14 -6
- clerk_sdk-0.2.6/clerk/gui_automation/ui_actions/support.py +124 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/state_machine.py +16 -0
- clerk_sdk-0.2.6/clerk/models/ui_operator.py +15 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6/clerk_sdk.egg-info}/PKG-INFO +1 -1
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk_sdk.egg-info/SOURCES.txt +1 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/setup.py +1 -1
- clerk_sdk-0.2.4/clerk/gui_automation/ui_actions/support.py +0 -69
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/LICENSE +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/MANIFEST.in +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/README.md +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/base.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/client.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/decorator/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/decorator/models.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/decorator/task_decorator.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/action_model/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/action_model/model.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/action_model/utils.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/client_actor/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/client_actor/exception.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/client_actor/model.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/decorators/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/exceptions/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/exceptions/agent_manager.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/exceptions/modality/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/exceptions/modality/exc.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/exceptions/websocket.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/requirements.txt +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_actions/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_actions/actions.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_actions/base.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_inspector/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_inspector/gui_vision.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_inspector/models.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/ai_recovery.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/decorators.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/exceptions.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/gui_automation/ui_state_machine/models.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/document.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/document_statuses.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/file.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/remote_device.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/models/response_model.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/utils/__init__.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/utils/logger.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk/utils/save_artifact.py +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk_sdk.egg-info/dependency_links.txt +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk_sdk.egg-info/requires.txt +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/clerk_sdk.egg-info/top_level.txt +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/pyproject.toml +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/requirements.txt +0 -0
- {clerk_sdk-0.2.4 → clerk_sdk-0.2.6}/setup.cfg +0 -0
|
@@ -11,6 +11,7 @@ from clerk.gui_automation.ui_state_inspector.models import (
|
|
|
11
11
|
TargetWithAnchor,
|
|
12
12
|
)
|
|
13
13
|
from clerk.models.remote_device import RemoteDevice
|
|
14
|
+
from clerk.models.ui_operator import UiOperatorTask
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class RPAClerk(BaseClerk):
|
|
@@ -46,6 +47,16 @@ class RPAClerk(BaseClerk):
|
|
|
46
47
|
raise RuntimeError("No coordinates found in the response.")
|
|
47
48
|
return Coords(**res.data[0])
|
|
48
49
|
|
|
50
|
+
def create_ui_operator_task(self, payload: Dict) -> UiOperatorTask:
|
|
51
|
+
endpoint = "/ui_operator"
|
|
52
|
+
res = self.post_request(endpoint=endpoint, json=payload)
|
|
53
|
+
return UiOperatorTask(**res.data[0])
|
|
54
|
+
|
|
55
|
+
def get_ui_operator_task(self, id: str) -> UiOperatorTask:
|
|
56
|
+
endpoint = "/ui_operator"
|
|
57
|
+
res = self.get_request(endpoint=endpoint, params={"task_id": id})
|
|
58
|
+
return UiOperatorTask(**res.data[0])
|
|
59
|
+
|
|
49
60
|
|
|
50
61
|
class GUIVisionClerk(BaseClerk):
|
|
51
62
|
root_endpoint: str = "/gui_automation/vision"
|
|
@@ -66,7 +66,7 @@ async def _get_screen_async() -> str:
|
|
|
66
66
|
and returns the base64 encoded image of the screen captured.
|
|
67
67
|
"""
|
|
68
68
|
payload = {
|
|
69
|
-
"proc_inst_id": os.getenv("
|
|
69
|
+
"proc_inst_id": os.getenv("_run_id"),
|
|
70
70
|
"client_name": os.getenv("REMOTE_DEVICE_NAME"),
|
|
71
71
|
"headless": True,
|
|
72
72
|
"action": {"action_type": "screenshot"},
|
|
@@ -132,7 +132,7 @@ async def _perform_action_async(
|
|
|
132
132
|
PerformActionException: If the action fails with an error message.
|
|
133
133
|
"""
|
|
134
134
|
req_payload: Dict = {
|
|
135
|
-
"proc_inst_id": os.getenv("
|
|
135
|
+
"proc_inst_id": os.getenv("_run_id"),
|
|
136
136
|
"client_name": os.getenv("REMOTE_DEVICE_NAME"),
|
|
137
137
|
"headless": (
|
|
138
138
|
True if os.getenv("HEADLESS", "True").lower() == "true" else False
|
|
@@ -24,7 +24,9 @@ global_ws: Union[ClientConnection, None] = None
|
|
|
24
24
|
clerk_client = RPAClerk()
|
|
25
25
|
wss_uri = "wss://agent-manager.f-one.group/action"
|
|
26
26
|
|
|
27
|
-
REMOTE_DEVICE_ALLOCATION_TIMEOUT = int(
|
|
27
|
+
REMOTE_DEVICE_ALLOCATION_TIMEOUT = int(
|
|
28
|
+
os.getenv("REMOTE_DEVICE_ALLOCATION_TIMEOUT", 60)
|
|
29
|
+
)
|
|
28
30
|
REMOTE_DEVICE_ALLOCATION_MAX_TRIES = int(
|
|
29
31
|
os.getenv("REMOTE_DEVICE_ALLOCATION_MAX_TRIES", 60)
|
|
30
32
|
)
|
|
@@ -48,13 +50,13 @@ def _allocate_remote_device(
|
|
|
48
50
|
|
|
49
51
|
except NoClientsAvailable:
|
|
50
52
|
logger.warning(
|
|
51
|
-
f"No clients are available for {group_name} group. Initiating a {REMOTE_DEVICE_ALLOCATION_TIMEOUT}
|
|
53
|
+
f"No clients are available for {group_name} group. Initiating a {REMOTE_DEVICE_ALLOCATION_TIMEOUT} seconds wait. Retry count: {retries}"
|
|
52
54
|
)
|
|
53
55
|
if retries >= REMOTE_DEVICE_ALLOCATION_MAX_TRIES:
|
|
54
56
|
raise ClientAvailabilityTimeout(
|
|
55
|
-
f"No clients available for {group_name} group after {REMOTE_DEVICE_ALLOCATION_TIMEOUT * REMOTE_DEVICE_ALLOCATION_MAX_TRIES}
|
|
57
|
+
f"No clients available for {group_name} group after {REMOTE_DEVICE_ALLOCATION_TIMEOUT * REMOTE_DEVICE_ALLOCATION_MAX_TRIES} seconds"
|
|
56
58
|
)
|
|
57
|
-
time.sleep(REMOTE_DEVICE_ALLOCATION_TIMEOUT
|
|
59
|
+
time.sleep(REMOTE_DEVICE_ALLOCATION_TIMEOUT)
|
|
58
60
|
retries += 1
|
|
59
61
|
|
|
60
62
|
|
|
@@ -93,7 +95,8 @@ def gui_automation(
|
|
|
93
95
|
def wrapper(payload: ClerkCodePayload, *args, **kwargs):
|
|
94
96
|
global global_ws
|
|
95
97
|
force_deallocate = False
|
|
96
|
-
os.environ["
|
|
98
|
+
os.environ["_document_id"] = payload.document.id
|
|
99
|
+
os.environ["_run_id"] = payload.run_id
|
|
97
100
|
|
|
98
101
|
remote_device = _allocate_remote_device(
|
|
99
102
|
clerk_client, group_name, payload.run_id
|
|
@@ -120,12 +123,17 @@ def gui_automation(
|
|
|
120
123
|
raise WebSocketConnectionFailed()
|
|
121
124
|
|
|
122
125
|
except Exception as e:
|
|
123
|
-
os.environ.pop("PROC_ID", None)
|
|
124
126
|
force_deallocate = True
|
|
125
127
|
raise
|
|
126
128
|
finally:
|
|
129
|
+
os.environ.pop("_run_id", None)
|
|
130
|
+
os.environ.pop("_document_id", None)
|
|
127
131
|
if not reserve_client or force_deallocate:
|
|
128
132
|
_deallocate_target(clerk_client, remote_device, payload.run_id)
|
|
133
|
+
else:
|
|
134
|
+
logger.warning(
|
|
135
|
+
f"The client stayed reserved for the this run id: {payload.run_id}"
|
|
136
|
+
)
|
|
129
137
|
|
|
130
138
|
if global_ws and global_ws.state is State.OPEN:
|
|
131
139
|
close_task = event_loop.create_task(close_ws_connection(global_ws))
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from datetime import timedelta, datetime
|
|
2
|
+
import os
|
|
3
|
+
import base64
|
|
4
|
+
import time
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from backoff._typing import Details
|
|
7
|
+
|
|
8
|
+
from clerk.models.ui_operator import TaskStatuses, UiOperatorTask
|
|
9
|
+
from clerk.utils.save_artifact import save_artifact
|
|
10
|
+
from clerk.utils import logger
|
|
11
|
+
from ..client_actor import get_screen
|
|
12
|
+
from ..ui_actions.base import BaseAction
|
|
13
|
+
from ..decorators.gui_automation import clerk_client
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_MAP = {
|
|
17
|
+
"y": True,
|
|
18
|
+
"yes": True,
|
|
19
|
+
"t": True,
|
|
20
|
+
"true": True,
|
|
21
|
+
"on": True,
|
|
22
|
+
"1": True,
|
|
23
|
+
"n": False,
|
|
24
|
+
"no": False,
|
|
25
|
+
"f": False,
|
|
26
|
+
"false": False,
|
|
27
|
+
"off": False,
|
|
28
|
+
"0": False,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def strtobool(value):
|
|
33
|
+
try:
|
|
34
|
+
return _MAP[str(value).lower()]
|
|
35
|
+
except KeyError:
|
|
36
|
+
raise ValueError('"{}" is not a valid bool value'.format(value))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def save_screenshot(filename: str, sub_folder: Optional[str] = None) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Save a screenshot into the process instance folder.
|
|
42
|
+
|
|
43
|
+
This function retrieves the base64 representation of the screen from the target environment using the 'get_screen' function.
|
|
44
|
+
Then, it saves the screenshot into the process instance folder using the 'save_file_into_instance_folder' function.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
filename (str): The name of the file to save the screenshot as.
|
|
48
|
+
sub_folder (str, optional): The name of the subfolder within the instance folder where the screenshot will be saved. Defaults to None.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
str: The file path of the saved screenshot.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
# get the base64 screen from target environment
|
|
55
|
+
screen_b64: str = get_screen()
|
|
56
|
+
return save_artifact(
|
|
57
|
+
filename=filename,
|
|
58
|
+
file_bytes=base64.b64decode(screen_b64),
|
|
59
|
+
subfolder=sub_folder,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _format_action_string(action: BaseAction) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Formats action in the same format as the one used in task modules.
|
|
66
|
+
"""
|
|
67
|
+
action_string = (
|
|
68
|
+
f"{action.__class__.__name__}(target='{action.target_name or action.target}')"
|
|
69
|
+
)
|
|
70
|
+
for anchor in action.anchors:
|
|
71
|
+
action_string += f".{anchor.relation}('{anchor.value}')"
|
|
72
|
+
if action.click_offset != [0, 0]:
|
|
73
|
+
action_string += (
|
|
74
|
+
f".offset(x={action.click_offset[0]}, y={action.click_offset[1]})"
|
|
75
|
+
)
|
|
76
|
+
action_string += ".do()"
|
|
77
|
+
return action_string
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def maybe_engage_operator_ui_action(details: Details) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Makes a call to the operator queue server to create an issue and waits for the allotted time for it to be resolved.
|
|
83
|
+
:param details: A dictionary containing the details of the exception raised (https://pypi.org/project/backoff/)
|
|
84
|
+
:returns: None
|
|
85
|
+
:raises: The exception raised by the action if the issue is not resolved within the allotted time
|
|
86
|
+
"""
|
|
87
|
+
# Determine if the operator should be engaged
|
|
88
|
+
ui_operator_enabled = strtobool(os.getenv("_ui_operator_enabled", default="False"))
|
|
89
|
+
|
|
90
|
+
if not ui_operator_enabled:
|
|
91
|
+
raise details["exception"] # type: ignore
|
|
92
|
+
|
|
93
|
+
ui_operator_pooling_interval = int(os.getenv("_ui_operator_pooling_interval", "1"))
|
|
94
|
+
ui_operator_timeout = int(os.getenv("_ui_operator_timeout", "3600"))
|
|
95
|
+
resolution_deadline = datetime.now() + timedelta(seconds=ui_operator_timeout)
|
|
96
|
+
|
|
97
|
+
# Extract the action object from the details dictionary
|
|
98
|
+
action: BaseAction = details["args"][0]
|
|
99
|
+
issue_description = _format_action_string(action)
|
|
100
|
+
|
|
101
|
+
# create ui operator task
|
|
102
|
+
payload = {
|
|
103
|
+
"document_id": os.getenv("_document_id"),
|
|
104
|
+
"remote_device_id": os.getenv("REMOTE_DEVICE_ID"),
|
|
105
|
+
"issue_description": issue_description,
|
|
106
|
+
}
|
|
107
|
+
task: UiOperatorTask = clerk_client.create_ui_operator_task(payload)
|
|
108
|
+
while datetime.now() < resolution_deadline:
|
|
109
|
+
task: UiOperatorTask = clerk_client.get_ui_operator_task(task.id)
|
|
110
|
+
if task.status == TaskStatuses.COMPLETED:
|
|
111
|
+
logger.debug(
|
|
112
|
+
f"The ui operator task {task.id} has been resolved by {task.assignee_name}"
|
|
113
|
+
)
|
|
114
|
+
return
|
|
115
|
+
elif task.status == TaskStatuses.CANCELLED:
|
|
116
|
+
logger.warning(f"The ui operator task {task.id} has been cancelled")
|
|
117
|
+
raise details["exception"]
|
|
118
|
+
|
|
119
|
+
time.sleep(ui_operator_pooling_interval)
|
|
120
|
+
|
|
121
|
+
logger.warning(
|
|
122
|
+
f"The ui operator task {task.id} was not resolved after {ui_operator_timeout} seconds"
|
|
123
|
+
)
|
|
124
|
+
raise details["exception"]
|
|
@@ -44,6 +44,9 @@ class ScreenPilot:
|
|
|
44
44
|
tolerate_repeat_transitions (int): Number of repeated transitions to tolerate before breaking the execution. Default 5.
|
|
45
45
|
tolerate_repeat_states (int): Number of repeated states to tolerate before breaking the execution. Default 5.
|
|
46
46
|
enable_force_close_app_process (bool): If true, terminates the application process via `taskkill` command. Default False.
|
|
47
|
+
ui_operator_enabled (bool): if true, enables the creation of ui operator task which needs to be resolved in Clerk.
|
|
48
|
+
ui_operator_pooling_interval (int): in seconds, defines the time pooling interval for ui operator task. Default 1.
|
|
49
|
+
ui_operator_timeout (int): in seconds, defines the max waiting time for the ui operator task to be resolved before raising and exception.
|
|
47
50
|
process_name (Optional[str]): Name of the process that needs to be closed (ie. process.exe). Required attribute if `enable_force_close_app_process` is True
|
|
48
51
|
|
|
49
52
|
Methods:
|
|
@@ -63,6 +66,9 @@ class ScreenPilot:
|
|
|
63
66
|
tolerate_repeat_transitions: int = 5
|
|
64
67
|
tolerate_repeat_states: int = 5
|
|
65
68
|
enable_force_close_app_process: bool = False
|
|
69
|
+
ui_operator_enabled: bool = False
|
|
70
|
+
ui_operator_pooling_interval: int = 1 # seconds
|
|
71
|
+
ui_operator_timeout: int = 3600 # seconds (1 hour)
|
|
66
72
|
process_name: Optional[str] = None
|
|
67
73
|
_acted_since_state_eval: bool = False
|
|
68
74
|
_ai_recovery_agent: Optional[CourseCorrector] = None
|
|
@@ -716,6 +722,14 @@ class ScreenPilot:
|
|
|
716
722
|
"""
|
|
717
723
|
return None
|
|
718
724
|
|
|
725
|
+
@classmethod
|
|
726
|
+
def _initialize_env_variables(cls):
|
|
727
|
+
os.environ["_ui_operator_enabled"] = str(cls.ui_operator_enabled)
|
|
728
|
+
os.environ["_ui_operator_pooling_interval"] = str(
|
|
729
|
+
cls.ui_operator_pooling_interval
|
|
730
|
+
)
|
|
731
|
+
os.environ["_ui_operator_timeout"] = str(cls.ui_operator_timeout)
|
|
732
|
+
|
|
719
733
|
@classmethod
|
|
720
734
|
def run(
|
|
721
735
|
cls, goal_function: Optional[Callable] = None, **kwargs
|
|
@@ -732,6 +746,8 @@ class ScreenPilot:
|
|
|
732
746
|
Returns:
|
|
733
747
|
- Exit reason (ScreenPilotOutcome): Exception class that interrupted the state machine execution.
|
|
734
748
|
"""
|
|
749
|
+
cls._initialize_env_variables()
|
|
750
|
+
|
|
735
751
|
if not goal_function:
|
|
736
752
|
goal_function = cls._default_goal_function
|
|
737
753
|
cls._act_on_start()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TaskStatuses(str, Enum):
|
|
7
|
+
OPEN = "open"
|
|
8
|
+
COMPLETED = "completed"
|
|
9
|
+
CANCELLED = "cancelled"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UiOperatorTask(BaseModel):
|
|
13
|
+
id: str
|
|
14
|
+
status: TaskStatuses
|
|
15
|
+
assignee_name: Optional[str]
|
|
@@ -13,7 +13,7 @@ gui_requirements = get_requirements("./clerk/gui_automation")
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="clerk-sdk",
|
|
16
|
-
version="0.2.
|
|
16
|
+
version="0.2.6",
|
|
17
17
|
description="Library for interacting with Clerk",
|
|
18
18
|
long_description=open("README.md").read(),
|
|
19
19
|
long_description_content_type="text/markdown",
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import base64
|
|
3
|
-
from typing import Optional
|
|
4
|
-
from backoff._typing import Details
|
|
5
|
-
|
|
6
|
-
from clerk.utils.save_artifact import save_artifact
|
|
7
|
-
from ..client_actor import get_screen
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
_MAP = {
|
|
11
|
-
"y": True,
|
|
12
|
-
"yes": True,
|
|
13
|
-
"t": True,
|
|
14
|
-
"true": True,
|
|
15
|
-
"on": True,
|
|
16
|
-
"1": True,
|
|
17
|
-
"n": False,
|
|
18
|
-
"no": False,
|
|
19
|
-
"f": False,
|
|
20
|
-
"false": False,
|
|
21
|
-
"off": False,
|
|
22
|
-
"0": False,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def strtobool(value):
|
|
27
|
-
try:
|
|
28
|
-
return _MAP[str(value).lower()]
|
|
29
|
-
except KeyError:
|
|
30
|
-
raise ValueError('"{}" is not a valid bool value'.format(value))
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def save_screenshot(filename: str, sub_folder: Optional[str] = None) -> str:
|
|
34
|
-
"""
|
|
35
|
-
Save a screenshot into the process instance folder.
|
|
36
|
-
|
|
37
|
-
This function retrieves the base64 representation of the screen from the target environment using the 'get_screen' function.
|
|
38
|
-
Then, it saves the screenshot into the process instance folder using the 'save_file_into_instance_folder' function.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
filename (str): The name of the file to save the screenshot as.
|
|
42
|
-
sub_folder (str, optional): The name of the subfolder within the instance folder where the screenshot will be saved. Defaults to None.
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
str: The file path of the saved screenshot.
|
|
46
|
-
|
|
47
|
-
"""
|
|
48
|
-
# get the base64 screen from target environment
|
|
49
|
-
screen_b64: str = get_screen()
|
|
50
|
-
return save_artifact(
|
|
51
|
-
filename=filename,
|
|
52
|
-
file_bytes=base64.b64decode(screen_b64),
|
|
53
|
-
subfolder=sub_folder,
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def maybe_engage_operator_ui_action(details: Details) -> None:
|
|
58
|
-
"""
|
|
59
|
-
Makes a call to the operator queue server to create an issue and waits for the allotted time for it to be resolved.
|
|
60
|
-
:param details: A dictionary containing the details of the exception raised (https://pypi.org/project/backoff/)
|
|
61
|
-
:returns: None
|
|
62
|
-
:raises: The exception raised by the action if the issue is not resolved within the allotted time
|
|
63
|
-
"""
|
|
64
|
-
# Determine if the operator should be engaged
|
|
65
|
-
use_operator = strtobool(os.getenv("USE_OPERATOR", default="False"))
|
|
66
|
-
if not use_operator:
|
|
67
|
-
raise details["exception"] # type: ignore
|
|
68
|
-
|
|
69
|
-
raise NotImplementedError("Feature not yet implemented")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|